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