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