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