script/dom/webcrypto/subtlecrypto/
x25519_operation.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use js::context::JSContext;
6use pkcs8::PrivateKeyInfo;
7use pkcs8::der::asn1::{BitStringRef, OctetString, OctetStringRef};
8use pkcs8::der::{AnyRef, Decode, Encode};
9use pkcs8::rand_core::OsRng;
10use pkcs8::spki::{AlgorithmIdentifier, ObjectIdentifier, SubjectPublicKeyInfo};
11use x25519_dalek::{PublicKey, StaticSecret};
12
13use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
14    CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
15};
16use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{JsonWebKey, KeyFormat};
17use crate::dom::bindings::error::Error;
18use crate::dom::bindings::root::DomRoot;
19use crate::dom::bindings::str::DOMString;
20use crate::dom::cryptokey::{CryptoKey, Handle};
21use crate::dom::globalscope::GlobalScope;
22use crate::dom::subtlecrypto::{
23    CryptoAlgorithm, ExportedKey, JsonWebKeyExt, JwkStringField, KeyAlgorithmAndDerivatives,
24    SubtleEcdhKeyDeriveParams, SubtleKeyAlgorithm,
25};
26
27/// `id-X25519` object identifier defined in [RFC8410]
28const X25519_OID_STRING: &str = "1.3.101.110";
29
30const PRIVATE_KEY_LENGTH: usize = 32;
31const PUBLIC_KEY_LENGTH: usize = 32;
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_from_rng(OsRng);
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 = SubjectPublicKeyInfo::<AnyRef, BitStringRef>::from_der(key_data)
207                .map_err(|_| Error::Data(None))?;
208
209            // Step 2.4. If the algorithm object identifier field of the algorithm
210            // AlgorithmIdentifier field of spki is not equal to the id-X25519 object identifier
211            // defined in [RFC8410], then throw a DataError.
212            if spki.algorithm.oid != ObjectIdentifier::new_unwrap(X25519_OID_STRING) {
213                return Err(Error::Data(None));
214            }
215
216            // Step 2.5. If the parameters field of the algorithm AlgorithmIdentifier field of spki
217            // is present, then throw a DataError.
218            if spki.algorithm.parameters.is_some() {
219                return Err(Error::Data(None));
220            }
221
222            // Step 2.6. Let publicKey be the X25519 public key identified by the subjectPublicKey
223            // field of spki.
224            let key_bytes: [u8; PUBLIC_KEY_LENGTH] = spki
225                .subject_public_key
226                .as_bytes()
227                .ok_or(Error::Data(None))?
228                .try_into()
229                .map_err(|_| Error::Data(None))?;
230            let public_key = PublicKey::from(key_bytes);
231
232            // Step 2.7. Let key be a new CryptoKey that represents publicKey.
233            // Step 2.8. Set the [[type]] internal slot of key to "public"
234            // Step 2.9. Let algorithm be a new KeyAlgorithm.
235            // Step 2.10. Set the name attribute of algorithm to "X25519".
236            // Step 2.11. Set the [[algorithm]] internal slot of key to algorithm.
237            let algorithm = SubtleKeyAlgorithm {
238                name: CryptoAlgorithm::X25519,
239            };
240            CryptoKey::new(
241                cx,
242                global,
243                KeyType::Public,
244                extractable,
245                KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
246                usages,
247                Handle::X25519PublicKey(public_key),
248            )
249        },
250        // If format is "pkcs8":
251        KeyFormat::Pkcs8 => {
252            // Step 2.1. If usages contains an entry which is not "deriveKey" or "deriveBits" then
253            // throw a SyntaxError.
254            if usages
255                .iter()
256                .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
257            {
258                return Err(Error::Syntax(None));
259            }
260
261            // Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
262            // algorithm over keyData.
263            // Step 2.3. If an error occurs while parsing, then throw a DataError.
264            let private_key_info =
265                PrivateKeyInfo::from_der(key_data).map_err(|_| Error::Data(None))?;
266
267            // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
268            // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-X25519 object
269            // identifier defined in [RFC8410], then throw a DataError.
270            if private_key_info.algorithm.oid != ObjectIdentifier::new_unwrap(X25519_OID_STRING) {
271                return Err(Error::Data(None));
272            }
273
274            // Step 2.5. If the parameters field of the privateKeyAlgorithm
275            // PrivateKeyAlgorithmIdentifier field of privateKeyInfo is present, then throw a
276            // DataError.
277            if private_key_info.algorithm.parameters.is_some() {
278                return Err(Error::Data(None));
279            }
280
281            // Step 2.6. Let curvePrivateKey be the result of performing the parse an ASN.1
282            // structure algorithm, with data as the privateKey field of privateKeyInfo, structure
283            // as the ASN.1 CurvePrivateKey structure specified in Section 7 of [RFC8410], and
284            // exactData set to true.
285            // Step 2.7. If an error occurred while parsing, then throw a DataError.
286            let curve_private_key = OctetStringRef::from_der(private_key_info.private_key)
287                .map_err(|_| Error::Data(None))?;
288            let key_bytes: [u8; PRIVATE_KEY_LENGTH] = curve_private_key
289                .as_bytes()
290                .try_into()
291                .map_err(|_| Error::Data(None))?;
292            let private_key = StaticSecret::from(key_bytes);
293
294            // Step 2.8. Let key be a new CryptoKey that represents the X25519 private key
295            // identified by curvePrivateKey.
296            // Step 2.9. Set the [[type]] internal slot of key to "private"
297            // Step 2.10. Let algorithm be a new KeyAlgorithm.
298            // Step 2.11. Set the name attribute of algorithm to "X25519".
299            // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
300            let algorithm = SubtleKeyAlgorithm {
301                name: CryptoAlgorithm::X25519,
302            };
303            CryptoKey::new(
304                cx,
305                global,
306                KeyType::Private,
307                extractable,
308                KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
309                usages,
310                Handle::X25519PrivateKey(private_key),
311            )
312        },
313        // If format is "jwk":
314        KeyFormat::Jwk => {
315            // Step 2.1
316            // If keyData is a JsonWebKey dictionary:
317            //     Let jwk equal keyData.
318            // Otherwise:
319            //     Throw a DataError.
320            let jwk = JsonWebKey::parse(cx, key_data)?;
321
322            // Step 2.2. If the d field is present and if usages contains an entry which is not
323            // "deriveKey" or "deriveBits" then throw a SyntaxError.
324            if jwk.d.is_some() &&
325                usages
326                    .iter()
327                    .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
328            {
329                return Err(Error::Syntax(None));
330            }
331
332            // Step 2.2. If the d field is not present and if usages is not empty then throw a
333            // SyntaxError.
334            if jwk.d.is_none() && !usages.is_empty() {
335                return Err(Error::Syntax(None));
336            }
337
338            // Step 2.2. If the kty field of jwk is not "OKP", then throw a DataError.
339            if jwk.kty.as_ref().is_none_or(|kty| kty != "OKP") {
340                return Err(Error::Data(None));
341            }
342
343            // Step 2.2. If the crv field of jwk is not "X25519", then throw a DataError.
344            if jwk.crv.as_ref().is_none_or(|crv| crv != "X25519") {
345                return Err(Error::Data(None));
346            }
347
348            // Step 2.2. If usages is non-empty and the use field of jwk is present and is not
349            // equal to "enc" then throw a DataError.
350            if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "enc") {
351                return Err(Error::Data(None));
352            }
353
354            // Step 2.2. If the key_ops field of jwk is present, and is invalid according to the
355            // requirements of JSON Web Key [JWK], or it does not contain all of the specified
356            // usages values, then throw a DataError.
357            jwk.check_key_ops(&usages)?;
358
359            // Step 2.2. If the ext field of jwk is present and has the value false and extractable
360            // is true, then throw a DataError.
361            if jwk.ext.is_some_and(|ext| !ext) && extractable {
362                return Err(Error::Data(None));
363            }
364
365            // Step 2.9.
366            // If the d field is present:
367            let (handle, key_type) = if jwk.d.is_some() {
368                // Step 2.9.1. If jwk does not meet the requirements of the JWK private key format
369                // described in Section 2 of [RFC8037], then throw a DataError.
370                let x = jwk.decode_required_string_field(JwkStringField::X)?;
371                let d = jwk.decode_required_string_field(JwkStringField::D)?;
372                let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] =
373                    x.try_into().map_err(|_| Error::Data(None))?;
374                let private_key_bytes: [u8; PRIVATE_KEY_LENGTH] =
375                    d.try_into().map_err(|_| Error::Data(None))?;
376                let public_key = PublicKey::from(public_key_bytes);
377                let private_key = StaticSecret::from(private_key_bytes);
378                if PublicKey::from(&private_key) != public_key {
379                    return Err(Error::Data(None));
380                }
381
382                // Step 2.9.1. Let key be a new CryptoKey object that represents the X25519 private
383                // key identified by interpreting jwk according to Section 2 of [RFC8037].
384                // NOTE: CryptoKey is created in Step 2.10 - 2.12.
385                let handle = Handle::X25519PrivateKey(private_key);
386
387                // Step 2.9.1. Set the [[type]] internal slot of Key to "private".
388                let key_type = KeyType::Private;
389
390                (handle, key_type)
391            }
392            // Otherwise:
393            else {
394                // Step 2.9.1. If jwk does not meet the requirements of the JWK public key format
395                // described in Section 2 of [RFC8037], then throw a DataError.
396                let x = jwk.decode_required_string_field(JwkStringField::X)?;
397                let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] =
398                    x.try_into().map_err(|_| Error::Data(None))?;
399                let public_key = PublicKey::from(public_key_bytes);
400
401                // Step 2.9.1. Let key be a new CryptoKey object that represents the X25519 public
402                // key identified by interpreting jwk according to Section 2 of [RFC8037].
403                // NOTE: CryptoKey is created in Step 2.10 - 2.12.
404                let handle = Handle::X25519PublicKey(public_key);
405
406                // Step 2.9.1. Set the [[type]] internal slot of Key to "public".
407                let key_type = KeyType::Public;
408
409                (handle, key_type)
410            };
411
412            // Step 2.10. Let algorithm be a new instance of a KeyAlgorithm object.
413            // Step 2.11. Set the name attribute of algorithm to "X25519".
414            // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
415            let algorithm = SubtleKeyAlgorithm {
416                name: CryptoAlgorithm::X25519,
417            };
418            CryptoKey::new(
419                cx,
420                global,
421                key_type,
422                extractable,
423                KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
424                usages,
425                handle,
426            )
427        },
428        // If format is "raw":
429        KeyFormat::Raw | KeyFormat::Raw_public => {
430            // Step 2.1. If usages is not empty then throw a SyntaxError.
431            if !usages.is_empty() {
432                return Err(Error::Syntax(None));
433            }
434
435            // Step 2.2. If the length in bits of keyData is not 256 then throw a DataError.
436            if key_data.len() != 32 {
437                return Err(Error::Data(None));
438            }
439
440            // Step 2.3. Let algorithm be a new KeyAlgorithm object.
441            // Step 2.4. Set the name attribute of algorithm to "X25519".
442            // Step 2.5. Let key be a new CryptoKey representing the key data provided in keyData.
443            // Step 2.6. Set the [[type]] internal slot of key to "public"
444            // Step 2.7. Set the [[algorithm]] internal slot of key to algorithm.
445            let key_bytes: [u8; PUBLIC_KEY_LENGTH] =
446                key_data.try_into().map_err(|_| Error::Data(None))?;
447            let public_key = PublicKey::from(key_bytes);
448            let algorithm = SubtleKeyAlgorithm {
449                name: CryptoAlgorithm::X25519,
450            };
451            CryptoKey::new(
452                cx,
453                global,
454                KeyType::Public,
455                extractable,
456                KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
457                usages,
458                Handle::X25519PublicKey(public_key),
459            )
460        },
461        // Otherwise:
462        _ => {
463            // throw a NotSupportedError.
464            return Err(Error::NotSupported(None));
465        },
466    };
467
468    // Step 3. Return key
469    Ok(key)
470}
471
472/// <https://w3c.github.io/webcrypto/#x25519-operations-export-key>
473pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
474    // Step 1. Let key be the CryptoKey to be exported.
475
476    // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
477    // slot of key cannot be accessed, then throw an OperationError.
478    // NOTE: Done in Step 3.
479
480    // Step 3.
481    let result = match format {
482        // If format is "spki":
483        KeyFormat::Spki => {
484            // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
485            // InvalidAccessError.
486            if key.Type() != KeyType::Public {
487                return Err(Error::InvalidAccess(None));
488            }
489
490            // Step 3.2.
491            // Let data be an instance of the SubjectPublicKeyInfo ASN.1 structure defined in
492            // [RFC5280] with the following properties:
493            //     * Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the
494            //       following properties:
495            //         * Set the algorithm object identifier to the id-X25519 OID defined in
496            //           [RFC8410].
497            //     * Set the subjectPublicKey field to keyData.
498            let Handle::X25519PublicKey(public_key) = key.handle() else {
499                return Err(Error::Operation(None));
500            };
501            let data = SubjectPublicKeyInfo::<BitStringRef, _> {
502                algorithm: AlgorithmIdentifier {
503                    oid: ObjectIdentifier::new_unwrap(X25519_OID_STRING),
504                    parameters: None,
505                },
506                subject_public_key: BitStringRef::from_bytes(public_key.as_bytes())
507                    .map_err(|_| Error::Data(None))?,
508            };
509
510            // Step 3.3. Let result be the result of DER-encoding data.
511            ExportedKey::Bytes(data.to_der().map_err(|_| Error::Operation(None))?)
512        },
513        // If format is "pkcs8":
514        KeyFormat::Pkcs8 => {
515            // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
516            // InvalidAccessError.
517            if key.Type() != KeyType::Private {
518                return Err(Error::InvalidAccess(None));
519            }
520
521            // Step 3.2.
522            // Let data be an instance of the PrivateKeyInfo ASN.1 structure defined in [RFC5208]
523            // with the following properties:
524            //     * Set the version field to 0.
525            //     * Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1
526            //       type with the following properties:
527            //         * Set the algorithm object identifier to the id-X25519 OID defined in
528            //         [RFC8410].
529            //     * Set the privateKey field to the result of DER-encoding a CurvePrivateKey ASN.1
530            //       type, as defined in Section 7 of [RFC8410], that represents the X25519 private
531            //       key represented by the [[handle]] internal slot of key
532            let Handle::X25519PrivateKey(private_key) = key.handle() else {
533                return Err(Error::Operation(None));
534            };
535            let curve_private_key =
536                OctetString::new(private_key.as_bytes()).map_err(|_| Error::Data(None))?;
537            let data = PrivateKeyInfo {
538                algorithm: AlgorithmIdentifier {
539                    oid: ObjectIdentifier::new_unwrap(X25519_OID_STRING),
540                    parameters: None,
541                },
542                private_key: &curve_private_key.to_der().map_err(|_| Error::Data(None))?,
543                public_key: None,
544            };
545
546            // Step 3.3. Let result be the result of DER-encoding data.
547            ExportedKey::Bytes(data.to_der().map_err(|_| Error::Operation(None))?)
548        },
549        // If format is "jwk":
550        KeyFormat::Jwk => {
551            // Step 3.1. Let jwk be a new JsonWebKey dictionary.
552            // Step 3.2. Set the kty attribute of jwk to "OKP".
553            // Step 3.3. Set the crv attribute of jwk to "X25519".
554            let mut jwk = JsonWebKey {
555                kty: Some(DOMString::from("OKP")),
556                crv: Some(DOMString::from("X25519")),
557                ..Default::default()
558            };
559
560            // Step 3.4. Set the x attribute of jwk according to the definition in Section 2 of
561            // [RFC8037].
562            match key.handle() {
563                Handle::X25519PrivateKey(private_key) => {
564                    let public_key = PublicKey::from(private_key);
565                    jwk.encode_string_field(JwkStringField::X, public_key.as_bytes());
566                },
567                Handle::X25519PublicKey(public_key) => {
568                    jwk.encode_string_field(JwkStringField::X, public_key.as_bytes());
569                },
570                _ => return Err(Error::Operation(None)),
571            }
572
573            // Step 3.5.
574            // If the [[type]] internal slot of key is "private"
575            //     Set the d attribute of jwk according to the definition in Section 2 of
576            //     [RFC8037].
577            if key.Type() == KeyType::Private {
578                if let Handle::X25519PrivateKey(private_key) = key.handle() {
579                    jwk.encode_string_field(JwkStringField::D, private_key.as_bytes());
580                } else {
581                    return Err(Error::Operation(None));
582                }
583            }
584
585            // Step 3.6. Set the key_ops attribute of jwk to the usages attribute of key.
586            jwk.set_key_ops(key.usages());
587
588            // Step 3.7. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
589            jwk.ext = Some(key.Extractable());
590
591            // Step 3.8. Let result be jwk.
592            ExportedKey::Jwk(Box::new(jwk))
593        },
594        // If format is "raw":
595        KeyFormat::Raw | KeyFormat::Raw_public => {
596            // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
597            // InvalidAccessError.
598            if key.Type() != KeyType::Public {
599                return Err(Error::InvalidAccess(None));
600            }
601
602            // Step 3.2. Let data be a byte sequence representing the X25519 public key represented
603            // by the [[handle]] internal slot of key.
604            let Handle::X25519PublicKey(public_key) = key.handle() else {
605                return Err(Error::Operation(None));
606            };
607            let data = public_key.as_bytes();
608
609            // Step 3.3. Let result be data.
610            ExportedKey::Bytes(data.to_vec())
611        },
612        // Otherwise:
613        _ => {
614            // throw a NotSupportedError.
615            return Err(Error::NotSupported(None));
616        },
617    };
618
619    // Step 4. Return result.
620    Ok(result)
621}
622
623/// <https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-getPublicKey>
624/// Step 9 - 15, for X25519
625pub(crate) fn get_public_key(
626    cx: &mut JSContext,
627    global: &GlobalScope,
628    key: &CryptoKey,
629    algorithm: &KeyAlgorithmAndDerivatives,
630    usages: Vec<KeyUsage>,
631) -> Result<DomRoot<CryptoKey>, Error> {
632    // Step 9. If usages contains an entry which is not supported for a public key by the algorithm
633    // identified by algorithm, then throw a SyntaxError.
634    //
635    // NOTE: See "impportKey" operation for supported usages
636    if !usages.is_empty() {
637        return Err(Error::Syntax(Some("Usages is not empty".to_string())));
638    }
639
640    // Step 10. Let publicKey be a new CryptoKey representing the public key corresponding to the
641    // private key represented by the [[handle]] internal slot of key.
642    // Step 11. If an error occurred, then throw a OperationError.
643    // Step 12. Set the [[type]] internal slot of publicKey to "public".
644    // Step 13. Set the [[algorithm]] internal slot of publicKey to algorithm.
645    // Step 14. Set the [[extractable]] internal slot of publicKey to true.
646    // Step 15. Set the [[usages]] internal slot of publicKey to usages.
647    let Handle::X25519PrivateKey(private_key) = key.handle() else {
648        return Err(Error::Operation(None));
649    };
650    let public_key = CryptoKey::new(
651        cx,
652        global,
653        KeyType::Public,
654        true,
655        algorithm.clone(),
656        usages,
657        Handle::X25519PublicKey(private_key.into()),
658    );
659
660    Ok(public_key)
661}