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