Skip to main content

script/dom/webcrypto/subtlecrypto/
x25519_operation.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use js::context::JSContext;
6use pkcs8::PrivateKeyInfo;
7use pkcs8::der::asn1::{BitStringRef, OctetString, OctetStringRef};
8use pkcs8::der::{AnyRef, Decode, Encode};
9use pkcs8::rand_core::OsRng;
10use pkcs8::spki::{AlgorithmIdentifier, ObjectIdentifier, SubjectPublicKeyInfo};
11use x25519_dalek::{PublicKey, StaticSecret};
12
13use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
14    CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
15};
16use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{JsonWebKey, KeyFormat};
17use crate::dom::bindings::error::Error;
18use crate::dom::bindings::root::DomRoot;
19use crate::dom::bindings::str::DOMString;
20use crate::dom::cryptokey::{CryptoKey, Handle};
21use crate::dom::globalscope::GlobalScope;
22use crate::dom::subtlecrypto::{
23    CryptoAlgorithm, ExportedKey, JsonWebKeyExt, JwkStringField, KeyAlgorithmAndDerivatives,
24    SubtleEcdhKeyDeriveParams, SubtleKeyAlgorithm,
25};
26
27/// `id-X25519` object identifier defined in [RFC8410]
28const X25519_OID_STRING: &str = "1.3.101.110";
29
30const PRIVATE_KEY_LENGTH: usize = 32;
31const PUBLIC_KEY_LENGTH: usize = 32;
32pub(crate) const SECRET_LENGTH: usize = 32;
33
34/// <https://w3c.github.io/webcrypto/#x25519-operations-derive-bits>
35pub(crate) fn derive_bits(
36    normalized_algorithm: &SubtleEcdhKeyDeriveParams,
37    key: &CryptoKey,
38    length: Option<u32>,
39) -> Result<Vec<u8>, Error> {
40    // Step 1. If the [[type]] internal slot of key is not "private", then throw an
41    // InvalidAccessError.
42    if key.Type() != KeyType::Private {
43        return Err(Error::InvalidAccess(None));
44    }
45
46    // Step 2. Let publicKey be the public member of normalizedAlgorithm.
47    let public_key = normalized_algorithm.public.root();
48
49    // Step 3. If the [[type]] internal slot of publicKey is not "public", then throw an
50    // InvalidAccessError.
51    if public_key.Type() != KeyType::Public {
52        return Err(Error::InvalidAccess(None));
53    }
54
55    // Step 4. If the name attribute of the [[algorithm]] internal slot of publicKey is not equal
56    // to the name property of the [[algorithm]] internal slot of key, then throw an
57    // InvalidAccessError.
58    if public_key.algorithm().name() != key.algorithm().name() {
59        return Err(Error::InvalidAccess(None));
60    }
61
62    // Step 5. Let secret be the result of performing the X25519 function specified in [RFC7748]
63    // Section 5 with key as the X25519 private key k and the X25519 public key represented by the
64    // [[handle]] internal slot of publicKey as the X25519 public key u.
65    let Handle::X25519PrivateKey(private_key) = key.handle() else {
66        return Err(Error::Operation(None));
67    };
68    let Handle::X25519PublicKey(public_key) = public_key.handle() else {
69        return Err(Error::Operation(None));
70    };
71    let secret = private_key.diffie_hellman(public_key);
72
73    // Step 6. If secret is the all-zero value, then throw a OperationError. This check must be
74    // performed in constant-time, as per [RFC7748] Section 6.1.
75    if !secret.was_contributory() {
76        return Err(Error::Operation(Some(
77            "Secret is the all-zero value".into(),
78        )));
79    }
80
81    // Step 7.
82    // If length is null:
83    //     Return secret
84    // Otherwise:
85    //     If the length of secret in bits is less than length:
86    //         throw an OperationError.
87    //     Otherwise:
88    //         Return a byte sequence containing the first length bits of secret.
89    let secret_slice = secret.as_bytes();
90    match length {
91        None => Ok(secret_slice.to_vec()),
92        Some(length) => {
93            if secret_slice.len() * 8 < length as usize {
94                Err(Error::Operation(None))
95            } else {
96                let mut secret = secret_slice[..length.div_ceil(8) as usize].to_vec();
97                if length % 8 != 0 {
98                    // Clean excess bits in last byte of secret.
99                    let mask = u8::MAX << (8 - length % 8);
100                    if let Some(last_byte) = secret.last_mut() {
101                        *last_byte &= mask;
102                    }
103                }
104                Ok(secret)
105            }
106        },
107    }
108}
109
110/// <https://w3c.github.io/webcrypto/#x25519-operations-generate-key>
111pub(crate) fn generate_key(
112    cx: &mut JSContext,
113    global: &GlobalScope,
114    extractable: bool,
115    usages: Vec<KeyUsage>,
116) -> Result<CryptoKeyPair, Error> {
117    // Step 1. If usages contains an entry which is not "deriveKey" or "deriveBits" then throw a
118    // SyntaxError.
119    if usages
120        .iter()
121        .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
122    {
123        return Err(Error::Syntax(None));
124    }
125
126    // Step 2. Generate an X25519 key pair, with the private key being 32 random bytes, and the
127    // public key being X25519(a, 9), as defined in [RFC7748], section 6.1.
128    let private_key = StaticSecret::random_from_rng(OsRng);
129    let public_key = PublicKey::from(&private_key);
130
131    // Step 3. Let algorithm be a new KeyAlgorithm object.
132    // Step 4. Set the name attribute of algorithm to "X25519".
133    let algorithm = SubtleKeyAlgorithm {
134        name: CryptoAlgorithm::X25519,
135    };
136
137    // Step 5. Let publicKey be a new CryptoKey representing the public key of the generated key pair.
138    // Step 6. Set the [[type]] internal slot of publicKey to "public"
139    // Step 7. Set the [[algorithm]] internal slot of publicKey to algorithm.
140    // Step 8. Set the [[extractable]] internal slot of publicKey to true.
141    // Step 9. Set the [[usages]] internal slot of publicKey to be the empty list.
142    let public_key = CryptoKey::new(
143        cx,
144        global,
145        KeyType::Public,
146        true,
147        KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm.clone()),
148        Vec::new(),
149        Handle::X25519PublicKey(public_key),
150    );
151
152    // Step 10. Let privateKey be a new CryptoKey representing the private key of the generated key pair.
153    // Step 11. Set the [[type]] internal slot of privateKey to "private"
154    // Step 12. Set the [[algorithm]] internal slot of privateKey to algorithm.
155    // Step 13. Set the [[extractable]] internal slot of privateKey to extractable.
156    // Step 14. Set the [[usages]] internal slot of privateKey to be the usage intersection of
157    // usages and [ "deriveKey", "deriveBits" ].
158    let private_key = CryptoKey::new(
159        cx,
160        global,
161        KeyType::Private,
162        extractable,
163        KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
164        usages
165            .iter()
166            .filter(|usage| matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
167            .cloned()
168            .collect(),
169        Handle::X25519PrivateKey(private_key),
170    );
171
172    // Step 15. Let result be a new CryptoKeyPair dictionary.
173    // Step 16. Set the publicKey attribute of result to be publicKey.
174    // Step 17. Set the privateKey attribute of result to be privateKey.
175    let result = CryptoKeyPair {
176        publicKey: Some(public_key),
177        privateKey: Some(private_key),
178    };
179
180    // Step 18. Return result.
181    Ok(result)
182}
183
184/// <https://w3c.github.io/webcrypto/#x25519-operations-import-key>
185pub(crate) fn import_key(
186    cx: &mut JSContext,
187    global: &GlobalScope,
188    format: KeyFormat,
189    key_data: &[u8],
190    extractable: bool,
191    usages: Vec<KeyUsage>,
192) -> Result<DomRoot<CryptoKey>, Error> {
193    // Step 1. Let keyData be the key data to be imported.
194
195    // Step 2.
196    let key = match format {
197        // If format is "spki":
198        KeyFormat::Spki => {
199            // Step 2.1. If usages is not empty then throw a SyntaxError.
200            if !usages.is_empty() {
201                return Err(Error::Syntax(None));
202            }
203
204            // Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
205            // algorithm over keyData.
206            // Step 2.3. If an error occurred while parsing, then throw a DataError.
207            let spki = SubjectPublicKeyInfo::<AnyRef, BitStringRef>::from_der(key_data)
208                .map_err(|_| Error::Data(None))?;
209
210            // Step 2.4. If the algorithm object identifier field of the algorithm
211            // AlgorithmIdentifier field of spki is not equal to the id-X25519 object identifier
212            // defined in [RFC8410], then throw a DataError.
213            if spki.algorithm.oid != ObjectIdentifier::new_unwrap(X25519_OID_STRING) {
214                return Err(Error::Data(None));
215            }
216
217            // Step 2.5. If the parameters field of the algorithm AlgorithmIdentifier field of spki
218            // is present, then throw a DataError.
219            if spki.algorithm.parameters.is_some() {
220                return Err(Error::Data(None));
221            }
222
223            // Step 2.6. Let publicKey be the X25519 public key identified by the subjectPublicKey
224            // field of spki.
225            let key_bytes: [u8; PUBLIC_KEY_LENGTH] = spki
226                .subject_public_key
227                .as_bytes()
228                .ok_or(Error::Data(None))?
229                .try_into()
230                .map_err(|_| Error::Data(None))?;
231            let public_key = PublicKey::from(key_bytes);
232
233            // Step 2.7. Let key be a new CryptoKey that represents publicKey.
234            // Step 2.8. Set the [[type]] internal slot of key to "public"
235            // Step 2.9. Let algorithm be a new KeyAlgorithm.
236            // Step 2.10. Set the name attribute of algorithm to "X25519".
237            // Step 2.11. Set the [[algorithm]] internal slot of key to algorithm.
238            let algorithm = SubtleKeyAlgorithm {
239                name: CryptoAlgorithm::X25519,
240            };
241            CryptoKey::new(
242                cx,
243                global,
244                KeyType::Public,
245                extractable,
246                KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
247                usages,
248                Handle::X25519PublicKey(public_key),
249            )
250        },
251        // If format is "pkcs8":
252        KeyFormat::Pkcs8 => {
253            // Step 2.1. If usages contains an entry which is not "deriveKey" or "deriveBits" then
254            // throw a SyntaxError.
255            if usages
256                .iter()
257                .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
258            {
259                return Err(Error::Syntax(None));
260            }
261
262            // Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
263            // algorithm over keyData.
264            // Step 2.3. If an error occurs while parsing, then throw a DataError.
265            let private_key_info =
266                PrivateKeyInfo::from_der(key_data).map_err(|_| Error::Data(None))?;
267
268            // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
269            // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-X25519 object
270            // identifier defined in [RFC8410], then throw a DataError.
271            if private_key_info.algorithm.oid != ObjectIdentifier::new_unwrap(X25519_OID_STRING) {
272                return Err(Error::Data(None));
273            }
274
275            // Step 2.5. If the parameters field of the privateKeyAlgorithm
276            // PrivateKeyAlgorithmIdentifier field of privateKeyInfo is present, then throw a
277            // DataError.
278            if private_key_info.algorithm.parameters.is_some() {
279                return Err(Error::Data(None));
280            }
281
282            // Step 2.6. Let curvePrivateKey be the result of performing the parse an ASN.1
283            // structure algorithm, with data as the privateKey field of privateKeyInfo, structure
284            // as the ASN.1 CurvePrivateKey structure specified in Section 7 of [RFC8410], and
285            // exactData set to true.
286            // Step 2.7. If an error occurred while parsing, then throw a DataError.
287            let curve_private_key = OctetStringRef::from_der(private_key_info.private_key)
288                .map_err(|_| Error::Data(None))?;
289            let key_bytes: [u8; PRIVATE_KEY_LENGTH] = curve_private_key
290                .as_bytes()
291                .try_into()
292                .map_err(|_| Error::Data(None))?;
293            let private_key = StaticSecret::from(key_bytes);
294
295            // Step 2.8. Let key be a new CryptoKey that represents the X25519 private key
296            // identified by curvePrivateKey.
297            // Step 2.9. Set the [[type]] internal slot of key to "private"
298            // Step 2.10. Let algorithm be a new KeyAlgorithm.
299            // Step 2.11. Set the name attribute of algorithm to "X25519".
300            // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
301            let algorithm = SubtleKeyAlgorithm {
302                name: CryptoAlgorithm::X25519,
303            };
304            CryptoKey::new(
305                cx,
306                global,
307                KeyType::Private,
308                extractable,
309                KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
310                usages,
311                Handle::X25519PrivateKey(private_key),
312            )
313        },
314        // If format is "jwk":
315        KeyFormat::Jwk => {
316            // Step 2.1
317            // If keyData is a JsonWebKey dictionary:
318            //     Let jwk equal keyData.
319            // Otherwise:
320            //     Throw a DataError.
321            let jwk = JsonWebKey::parse(cx, key_data)?;
322
323            // Step 2.2. If the d field is present and if usages contains an entry which is not
324            // "deriveKey" or "deriveBits" then throw a SyntaxError.
325            if jwk.d.is_some() &&
326                usages
327                    .iter()
328                    .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
329            {
330                return Err(Error::Syntax(None));
331            }
332
333            // Step 2.2. If the d field is not present and if usages is not empty then throw a
334            // SyntaxError.
335            if jwk.d.is_none() && !usages.is_empty() {
336                return Err(Error::Syntax(None));
337            }
338
339            // Step 2.2. If the kty field of jwk is not "OKP", then throw a DataError.
340            if jwk.kty.as_ref().is_none_or(|kty| kty != "OKP") {
341                return Err(Error::Data(None));
342            }
343
344            // Step 2.2. If the crv field of jwk is not "X25519", then throw a DataError.
345            if jwk.crv.as_ref().is_none_or(|crv| crv != "X25519") {
346                return Err(Error::Data(None));
347            }
348
349            // Step 2.2. If usages is non-empty and the use field of jwk is present and is not
350            // equal to "enc" then throw a DataError.
351            if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "enc") {
352                return Err(Error::Data(None));
353            }
354
355            // Step 2.2. If the key_ops field of jwk is present, and is invalid according to the
356            // requirements of JSON Web Key [JWK], or it does not contain all of the specified
357            // usages values, then throw a DataError.
358            jwk.check_key_ops(&usages)?;
359
360            // Step 2.2. If the ext field of jwk is present and has the value false and extractable
361            // is true, then throw a DataError.
362            if jwk.ext.is_some_and(|ext| !ext) && extractable {
363                return Err(Error::Data(None));
364            }
365
366            // Step 2.9.
367            // If the d field is present:
368            let (handle, key_type) = if jwk.d.is_some() {
369                // Step 2.9.1. If jwk does not meet the requirements of the JWK private key format
370                // described in Section 2 of [RFC8037], then throw a DataError.
371                let x = jwk.decode_required_string_field(JwkStringField::X)?;
372                let d = jwk.decode_required_string_field(JwkStringField::D)?;
373                let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] =
374                    x.as_slice().try_into().map_err(|_| Error::Data(None))?;
375                let private_key_bytes: [u8; PRIVATE_KEY_LENGTH] =
376                    d.as_slice().try_into().map_err(|_| Error::Data(None))?;
377                let public_key = PublicKey::from(public_key_bytes);
378                let private_key = StaticSecret::from(private_key_bytes);
379                if PublicKey::from(&private_key) != public_key {
380                    return Err(Error::Data(None));
381                }
382
383                // Step 2.9.1. Let key be a new CryptoKey object that represents the X25519 private
384                // key identified by interpreting jwk according to Section 2 of [RFC8037].
385                // NOTE: CryptoKey is created in Step 2.10 - 2.12.
386                let handle = Handle::X25519PrivateKey(private_key);
387
388                // Step 2.9.1. Set the [[type]] internal slot of Key to "private".
389                let key_type = KeyType::Private;
390
391                (handle, key_type)
392            }
393            // Otherwise:
394            else {
395                // Step 2.9.1. If jwk does not meet the requirements of the JWK public key format
396                // described in Section 2 of [RFC8037], then throw a DataError.
397                let x = jwk.decode_required_string_field(JwkStringField::X)?;
398                let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] =
399                    x.as_slice().try_into().map_err(|_| Error::Data(None))?;
400                let public_key = PublicKey::from(public_key_bytes);
401
402                // Step 2.9.1. Let key be a new CryptoKey object that represents the X25519 public
403                // key identified by interpreting jwk according to Section 2 of [RFC8037].
404                // NOTE: CryptoKey is created in Step 2.10 - 2.12.
405                let handle = Handle::X25519PublicKey(public_key);
406
407                // Step 2.9.1. Set the [[type]] internal slot of Key to "public".
408                let key_type = KeyType::Public;
409
410                (handle, key_type)
411            };
412
413            // Step 2.10. Let algorithm be a new instance of a KeyAlgorithm object.
414            // Step 2.11. Set the name attribute of algorithm to "X25519".
415            // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
416            let algorithm = SubtleKeyAlgorithm {
417                name: CryptoAlgorithm::X25519,
418            };
419            CryptoKey::new(
420                cx,
421                global,
422                key_type,
423                extractable,
424                KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
425                usages,
426                handle,
427            )
428        },
429        // If format is "raw":
430        KeyFormat::Raw | KeyFormat::Raw_public => {
431            // Step 2.1. If usages is not empty then throw a SyntaxError.
432            if !usages.is_empty() {
433                return Err(Error::Syntax(None));
434            }
435
436            // Step 2.2. If the length in bits of keyData is not 256 then throw a DataError.
437            if key_data.len() != 32 {
438                return Err(Error::Data(None));
439            }
440
441            // Step 2.3. Let algorithm be a new KeyAlgorithm object.
442            // Step 2.4. Set the name attribute of algorithm to "X25519".
443            // Step 2.5. Let key be a new CryptoKey representing the key data provided in keyData.
444            // Step 2.6. Set the [[type]] internal slot of key to "public"
445            // Step 2.7. Set the [[algorithm]] internal slot of key to algorithm.
446            let key_bytes: [u8; PUBLIC_KEY_LENGTH] =
447                key_data.try_into().map_err(|_| Error::Data(None))?;
448            let public_key = PublicKey::from(key_bytes);
449            let algorithm = SubtleKeyAlgorithm {
450                name: CryptoAlgorithm::X25519,
451            };
452            CryptoKey::new(
453                cx,
454                global,
455                KeyType::Public,
456                extractable,
457                KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
458                usages,
459                Handle::X25519PublicKey(public_key),
460            )
461        },
462        // Otherwise:
463        _ => {
464            // throw a NotSupportedError.
465            return Err(Error::NotSupported(None));
466        },
467    };
468
469    // Step 3. Return key
470    Ok(key)
471}
472
473/// <https://w3c.github.io/webcrypto/#x25519-operations-export-key>
474pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
475    // Step 1. Let key be the CryptoKey to be exported.
476
477    // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
478    // slot of key cannot be accessed, then throw an OperationError.
479    // NOTE: Done in Step 3.
480
481    // Step 3.
482    let result = match format {
483        // If format is "spki":
484        KeyFormat::Spki => {
485            // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
486            // InvalidAccessError.
487            if key.Type() != KeyType::Public {
488                return Err(Error::InvalidAccess(None));
489            }
490
491            // Step 3.2.
492            // Let data be an instance of the SubjectPublicKeyInfo ASN.1 structure defined in
493            // [RFC5280] with the following properties:
494            //     * Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the
495            //       following properties:
496            //         * Set the algorithm object identifier to the id-X25519 OID defined in
497            //           [RFC8410].
498            //     * Set the subjectPublicKey field to keyData.
499            let Handle::X25519PublicKey(public_key) = key.handle() else {
500                return Err(Error::Operation(None));
501            };
502            let data = SubjectPublicKeyInfo::<BitStringRef, _> {
503                algorithm: AlgorithmIdentifier {
504                    oid: ObjectIdentifier::new_unwrap(X25519_OID_STRING),
505                    parameters: None,
506                },
507                subject_public_key: BitStringRef::from_bytes(public_key.as_bytes())
508                    .map_err(|_| Error::Data(None))?,
509            };
510
511            // Step 3.3. Let result be the result of DER-encoding data.
512            ExportedKey::new_bytes(data.to_der().map_err(|_| Error::Operation(None))?)
513        },
514        // If format is "pkcs8":
515        KeyFormat::Pkcs8 => {
516            // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
517            // InvalidAccessError.
518            if key.Type() != KeyType::Private {
519                return Err(Error::InvalidAccess(None));
520            }
521
522            // Step 3.2.
523            // Let data be an instance of the PrivateKeyInfo ASN.1 structure defined in [RFC5208]
524            // with the following properties:
525            //     * Set the version field to 0.
526            //     * Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1
527            //       type with the following properties:
528            //         * Set the algorithm object identifier to the id-X25519 OID defined in
529            //         [RFC8410].
530            //     * Set the privateKey field to the result of DER-encoding a CurvePrivateKey ASN.1
531            //       type, as defined in Section 7 of [RFC8410], that represents the X25519 private
532            //       key represented by the [[handle]] internal slot of key
533            let Handle::X25519PrivateKey(private_key) = key.handle() else {
534                return Err(Error::Operation(None));
535            };
536            let curve_private_key =
537                OctetString::new(private_key.as_bytes()).map_err(|_| Error::Data(None))?;
538            let data = PrivateKeyInfo {
539                algorithm: AlgorithmIdentifier {
540                    oid: ObjectIdentifier::new_unwrap(X25519_OID_STRING),
541                    parameters: None,
542                },
543                private_key: &curve_private_key.to_der().map_err(|_| Error::Data(None))?,
544                public_key: None,
545            };
546
547            // Step 3.3. Let result be the result of DER-encoding data.
548            ExportedKey::new_bytes(data.to_der().map_err(|_| Error::Operation(None))?)
549        },
550        // If format is "jwk":
551        KeyFormat::Jwk => {
552            // Step 3.1. Let jwk be a new JsonWebKey dictionary.
553            let mut jwk = JsonWebKey::default();
554
555            // Step 3.2. Set the kty attribute of jwk to "OKP".
556            jwk.kty = Some(DOMString::from("OKP"));
557
558            // Step 3.3. Set the crv attribute of jwk to "X25519".
559            jwk.crv = Some(DOMString::from("X25519"));
560
561            // Step 3.4. Set the x attribute of jwk according to the definition in Section 2 of
562            // [RFC8037].
563            match key.handle() {
564                Handle::X25519PrivateKey(private_key) => {
565                    let public_key = PublicKey::from(private_key);
566                    jwk.encode_string_field(JwkStringField::X, public_key.as_bytes());
567                },
568                Handle::X25519PublicKey(public_key) => {
569                    jwk.encode_string_field(JwkStringField::X, public_key.as_bytes());
570                },
571                _ => return Err(Error::Operation(None)),
572            }
573
574            // Step 3.5.
575            // If the [[type]] internal slot of key is "private"
576            //     Set the d attribute of jwk according to the definition in Section 2 of
577            //     [RFC8037].
578            if key.Type() == KeyType::Private {
579                if let Handle::X25519PrivateKey(private_key) = key.handle() {
580                    jwk.encode_string_field(JwkStringField::D, private_key.as_bytes());
581                } else {
582                    return Err(Error::Operation(None));
583                }
584            }
585
586            // Step 3.6. Set the key_ops attribute of jwk to the usages attribute of key.
587            jwk.set_key_ops(key.usages());
588
589            // Step 3.7. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
590            jwk.ext = Some(key.Extractable());
591
592            // Step 3.8. Let result be jwk.
593            ExportedKey::new_jwk(jwk)
594        },
595        // If format is "raw":
596        KeyFormat::Raw | KeyFormat::Raw_public => {
597            // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
598            // InvalidAccessError.
599            if key.Type() != KeyType::Public {
600                return Err(Error::InvalidAccess(None));
601            }
602
603            // Step 3.2. Let data be a byte sequence representing the X25519 public key represented
604            // by the [[handle]] internal slot of key.
605            let Handle::X25519PublicKey(public_key) = key.handle() else {
606                return Err(Error::Operation(None));
607            };
608            let data = public_key.as_bytes();
609
610            // Step 3.3. Let result be data.
611            ExportedKey::new_bytes(data.to_vec())
612        },
613        // Otherwise:
614        _ => {
615            // throw a NotSupportedError.
616            return Err(Error::NotSupported(None));
617        },
618    };
619
620    // Step 4. Return result.
621    Ok(result)
622}
623
624/// <https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-getPublicKey>
625/// Step 9 - 15, for X25519
626pub(crate) fn get_public_key(
627    cx: &mut JSContext,
628    global: &GlobalScope,
629    key: &CryptoKey,
630    algorithm: &KeyAlgorithmAndDerivatives,
631    usages: Vec<KeyUsage>,
632) -> Result<DomRoot<CryptoKey>, Error> {
633    // Step 9. If usages contains an entry which is not supported for a public key by the algorithm
634    // identified by algorithm, then throw a SyntaxError.
635    //
636    // NOTE: See "impportKey" operation for supported usages
637    if !usages.is_empty() {
638        return Err(Error::Syntax(Some("Usages is not empty".to_string())));
639    }
640
641    // Step 10. Let publicKey be a new CryptoKey representing the public key corresponding to the
642    // private key represented by the [[handle]] internal slot of key.
643    // Step 11. If an error occurred, then throw a OperationError.
644    // Step 12. Set the [[type]] internal slot of publicKey to "public".
645    // Step 13. Set the [[algorithm]] internal slot of publicKey to algorithm.
646    // Step 14. Set the [[extractable]] internal slot of publicKey to true.
647    // Step 15. Set the [[usages]] internal slot of publicKey to usages.
648    let Handle::X25519PrivateKey(private_key) = key.handle() else {
649        return Err(Error::Operation(None));
650    };
651    let public_key = CryptoKey::new(
652        cx,
653        global,
654        KeyType::Public,
655        true,
656        algorithm.clone(),
657        usages,
658        Handle::X25519PublicKey(private_key.into()),
659    );
660
661    Ok(public_key)
662}