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