script/dom/webcrypto/subtlecrypto/ed448_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 ed448_goldilocks::elliptic_curve::Generate;
6use ed448_goldilocks::elliptic_curve::group::cofactor::CofactorGroup;
7use ed448_goldilocks::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePublicKey};
8use ed448_goldilocks::signature::SignatureEncoding;
9use ed448_goldilocks::{CompressedEdwardsY, PublicKeyBytes, Signature, SigningKey, VerifyingKey};
10use js::context::JSContext;
11use pkcs8::der::Encode;
12use pkcs8::der::asn1::OctetStringRef;
13use pkcs8::{AlgorithmIdentifierRef, ObjectIdentifier, PrivateKeyInfoRef};
14use zeroize::Zeroizing;
15
16use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
17 CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
18};
19use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{JsonWebKey, KeyFormat};
20use crate::dom::bindings::error::Error;
21use crate::dom::bindings::root::DomRoot;
22use crate::dom::bindings::str::DOMString;
23use crate::dom::cryptokey::{CryptoKey, Handle};
24use crate::dom::globalscope::GlobalScope;
25use crate::dom::subtlecrypto::{
26 CryptoAlgorithm, ExportedKey, JsonWebKeyExt, JwkStringField, KeyAlgorithmAndDerivatives,
27 SubtleEd448Params, SubtleKeyAlgorithm,
28};
29
30/// `id-Ed448` object identifier defined in [RFC8410]
31const ED448_OID_STRING: &str = "1.3.101.113";
32
33/// <https://wicg.github.io/webcrypto-secure-curves/#ed448-operations>
34pub(crate) fn sign(
35 normalized_algorithm: &SubtleEd448Params,
36 key: &CryptoKey,
37 message: &[u8],
38) -> Result<Vec<u8>, Error> {
39 // Step 1. If the [[type]] internal slot of key is not "private", then throw an
40 // InvalidAccessError.
41 if key.Type() != KeyType::Private {
42 return Err(Error::InvalidAccess(Some(
43 "[[type]] internal slot of key is not \"private\"".into(),
44 )));
45 }
46
47 // Step 2. Let context be the contents of the context member of normalizedAlgorithm or the empty
48 // octet string if the context member of normalizedAlgorithm is not present.
49 let context = normalized_algorithm.context.as_deref().unwrap_or_default();
50
51 // Step 3. If context has a length greater than 255 bytes, then throw an OperationError.
52 if context.len() > 255 {
53 return Err(Error::Operation(Some(
54 "Context has a length greater than 255 bytes".into(),
55 )));
56 }
57
58 // Step 4. Perform the Ed448 signing process, as specified in [RFC8032], Section 5.2.6, with
59 // message as M and context as C, using the Ed448 private key associated with key.
60 let Handle::Ed448PrivateKey(private_key) = key.handle() else {
61 return Err(Error::Operation(Some(
62 "[[handle]] internal slot of key is not an Ed448 private key".into(),
63 )));
64 };
65 let result = private_key.sign_ctx(context, message).map_err(|_| {
66 Error::Operation(Some(
67 "Failed to sign the message with Ed448 algorithm".into(),
68 ))
69 })?;
70
71 // Step 5. Return a new ArrayBuffer associated with the relevant global object of this [HTML],
72 // and containing the bytes of the signature resulting from performing the Ed448 signing
73 // process.
74 // NOTE: The conversion to ArrayBuffer is done in SubtleCrypto::Sign.
75 Ok(result.to_vec())
76}
77
78/// <https://wicg.github.io/webcrypto-secure-curves/#ed448-operations>
79pub(crate) fn verify(
80 normalized_algorithm: &SubtleEd448Params,
81 key: &CryptoKey,
82 message: &[u8],
83 signature: &[u8],
84) -> Result<bool, Error> {
85 // Step 1. If the [[type]] internal slot of key is not "public", then throw an
86 // InvalidAccessError.
87 if key.Type() != KeyType::Public {
88 return Err(Error::InvalidAccess(Some(
89 "[[type]] internal slot of key is not \"public\"".into(),
90 )));
91 }
92
93 // Step 2. Let context be the contents of the context member of normalizedAlgorithm or the empty
94 // octet string if the context member of normalizedAlgorithm is not present.
95 let context = normalized_algorithm.context.as_deref().unwrap_or_default();
96
97 // Step 3. If context has a length greater than 255 bytes, then throw an OperationError.
98 if context.len() > 255 {
99 return Err(Error::Operation(Some(
100 "Context has a length greater than 255 bytes".into(),
101 )));
102 }
103
104 // Step 4. If the key data of key represents an invalid point or a small-order element on the
105 // Elliptic Curve of Ed448, return false.
106 let Handle::Ed448PublicKey(public_key) = key.handle() else {
107 return Err(Error::Operation(Some(
108 "[[handle]] internal slot of key is not an Ed448 public key".into(),
109 )));
110 };
111 if (*public_key).to_edwards().is_small_order().into() {
112 return Ok(false);
113 }
114
115 // Step 5. If the point R, encoded in the first half of signature, represents an invalid point
116 // or a small-order element on the Elliptic Curve of Ed448, return false.
117 if CompressedEdwardsY::try_from(&signature[..signature.len() / 2])
118 .ok()
119 .and_then(|compressed_point| compressed_point.decompress().into_option())
120 .map(|point| point.to_edwards().is_small_order().into())
121 .unwrap_or(true)
122 {
123 return Ok(false);
124 }
125
126 // Step 6. Perform the Ed448 verification steps, as specified in [RFC8032], Section 5.2.7, using
127 // the cofactorless (unbatched) equation, [S]B = R + [k]A', on the signature, with message as M
128 // and context as C, using the Ed448 public key associated with key.
129 // Step 7. Let result be a boolean with the value true if the signature is valid and the value
130 // false otherwise.
131 let result = Signature::from_slice(signature)
132 .and_then(|signature| public_key.verify_ctx(&signature, context, message))
133 .is_ok();
134
135 // Step 8. Return result.
136 Ok(result)
137}
138
139/// <https://wicg.github.io/webcrypto-secure-curves/#ed448-operations>
140pub(crate) fn generate_key(
141 cx: &mut JSContext,
142 global: &GlobalScope,
143 extractable: bool,
144 usages: Vec<KeyUsage>,
145) -> Result<CryptoKeyPair, Error> {
146 // Step 1. If usages contains a value which is not one of "sign" or "verify", then throw a
147 // SyntaxError.
148 if usages
149 .iter()
150 .any(|usage| !matches!(usage, KeyUsage::Sign | KeyUsage::Verify))
151 {
152 return Err(Error::Syntax(Some(
153 "Usages contains an entry which is not \"sign\" or \"verify\"".into(),
154 )));
155 }
156
157 // Step 2. Generate an Ed448 key pair, as defined in [RFC8032], section 5.1.5.
158 let private_key = SigningKey::try_generate()
159 .map_err(|_| Error::Operation(Some("Failed to generate Ed448 private key".into())))?;
160 let public_key = private_key.verifying_key();
161
162 // Step 3. Let algorithm be a new KeyAlgorithm object.
163 // Step 4. Set the name attribute of algorithm to "Ed448".
164 let algorithm = SubtleKeyAlgorithm {
165 name: CryptoAlgorithm::Ed448,
166 };
167
168 // Step 5. Let publicKey be a new CryptoKey associated with the relevant global object of this
169 // [HTML], and representing the public key of the generated key pair.
170 // Step 6. Set the [[type]] internal slot of publicKey to "public"
171 // Step 7. Set the [[algorithm]] internal slot of publicKey to algorithm.
172 // Step 8. Set the [[extractable]] internal slot of publicKey to true.
173 // Step 9. Set the [[usages]] internal slot of publicKey to be the usage intersection of usages
174 // and [ "verify" ].
175 let public_key = CryptoKey::new(
176 cx,
177 global,
178 KeyType::Public,
179 true,
180 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm.clone()),
181 usages
182 .iter()
183 .filter(|&usage| *usage == KeyUsage::Verify)
184 .cloned()
185 .collect(),
186 Handle::Ed448PublicKey(public_key),
187 );
188
189 // Step 10. Let privateKey be a new CryptoKey associated with the relevant global object of this
190 // [HTML], and representing the private key of the generated key pair.
191 // Step 11. Set the [[type]] internal slot of privateKey to "private"
192 // Step 12. Set the [[algorithm]] internal slot of privateKey to algorithm.
193 // Step 13. Set the [[extractable]] internal slot of privateKey to extractable.
194 // Step 14. Set the [[usages]] internal slot of privateKey to be the usage intersection of
195 // usages and [ "sign" ].
196 let private_key = CryptoKey::new(
197 cx,
198 global,
199 KeyType::Private,
200 extractable,
201 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
202 usages
203 .iter()
204 .filter(|&usage| *usage == KeyUsage::Sign)
205 .cloned()
206 .collect(),
207 Handle::Ed448PrivateKey(private_key),
208 );
209
210 // Step 15. Let result be a new CryptoKeyPair dictionary.
211 // Step 16. Set the publicKey attribute of result to be publicKey.
212 // Step 17. Set the privateKey attribute of result to be privateKey.
213 let result = CryptoKeyPair {
214 publicKey: Some(public_key),
215 privateKey: Some(private_key),
216 };
217
218 // Step 18. Return the result of converting result to an ECMAScript Object, as defined by
219 // [WebIDL].
220 // NOTE: The conversion of result to an ECMAScript Object is done in SubtleCrypto::Generate.
221 Ok(result)
222}
223
224/// <https://wicg.github.io/webcrypto-secure-curves/#ed448-operations>
225pub(crate) fn import_key(
226 cx: &mut JSContext,
227 global: &GlobalScope,
228 format: KeyFormat,
229 key_data: &[u8],
230 extractable: bool,
231 usages: Vec<KeyUsage>,
232) -> Result<DomRoot<CryptoKey>, Error> {
233 // Step 1. Let keyData be the key data to be imported.
234
235 // Step 2.
236 let key = match format {
237 // If format is "spki":
238 KeyFormat::Spki => {
239 // Step 2.1. If usages contains a value which is not "verify" then throw a SyntaxError.
240 if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
241 return Err(Error::Syntax(Some(
242 "Usages contains an entry which is not \"verify\"".into(),
243 )));
244 }
245
246 // Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
247 // algorithm over keyData.
248 // Step 2.3. If an error occurred while parsing, then throw a DataError.
249 // Step 2.4. If the algorithm object identifier field of the algorithm
250 // AlgorithmIdentifier field of spki is not equal to the id-Ed448 object identifier
251 // defined in [RFC8410], then throw a DataError.
252 // Step 2.5. If the parameters field of the algorithm AlgorithmIdentifier field of spki
253 // is present, then throw a DataError.
254 // Step 2.6. Let publicKey be the Ed448 public key identified by the subjectPublicKey
255 // field of spki.
256 let public_key = VerifyingKey::from_public_key_der(key_data).map_err(|_| {
257 Error::Data(Some(
258 "Failed to parse the Ed448 public key in SPKI format".into(),
259 ))
260 })?;
261
262 // Step 2.7. Let key be a new CryptoKey associated with the relevant global object of
263 // this [HTML], and that represents publicKey.
264 // Step 2.8. Set the [[type]] internal slot of key to "public"
265 // Step 2.9. Let algorithm be a new KeyAlgorithm.
266 // Step 2.10. Set the name attribute of algorithm to "Ed448".
267 // Step 2.11. Set the [[algorithm]] internal slot of key to algorithm.
268 let algorithm = SubtleKeyAlgorithm {
269 name: CryptoAlgorithm::Ed448,
270 };
271 CryptoKey::new(
272 cx,
273 global,
274 KeyType::Public,
275 extractable,
276 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
277 usages,
278 Handle::Ed448PublicKey(public_key),
279 )
280 },
281 // If format is "pkcs8":
282 KeyFormat::Pkcs8 => {
283 // Step 2.1. If usages contains a value which is not "sign" then throw a SyntaxError.
284 if usages.iter().any(|usage| *usage != KeyUsage::Sign) {
285 return Err(Error::Syntax(Some(
286 "Usages contains an entry which is not \"sign\"".into(),
287 )));
288 }
289
290 // Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
291 // algorithm over keyData.
292 // Step 2.3. If an error occurs while parsing, then throw a DataError.
293 // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
294 // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-Ed448 object
295 // identifier defined in [RFC8410], then throw a DataError.
296 // Step 2.5. If the parameters field of the privateKeyAlgorithm
297 // PrivateKeyAlgorithmIdentifier field of privateKeyInfo is present, then throw a
298 // DataError.
299 // Step 2.6. Let curvePrivateKey be the result of performing the parse an ASN.1
300 // structure algorithm, with data as the privateKey field of privateKeyInfo, structure
301 // as the ASN.1 CurvePrivateKey structure specified in Section 7 of [RFC8410], and
302 // exactData set to true.
303 // Step 2.7. If an error occurred while parsing, then throw a DataError.
304 let curve_private_key = SigningKey::from_pkcs8_der(key_data).map_err(|_| {
305 Error::Data(Some(
306 "Failed to parse the Ed448 private key in PKCS#8 format".into(),
307 ))
308 })?;
309
310 // Step 2.8. Let key be a new CryptoKey associated with the relevant global object of
311 // this [HTML], and that represents the Ed448 private key identified by curvePrivateKey.
312 // Step 2.9. Set the [[type]] internal slot of key to "private"
313 // Step 2.10. Let algorithm be a new KeyAlgorithm.
314 // Step 2.11. Set the name attribute of algorithm to "Ed448".
315 // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
316 let algorithm = SubtleKeyAlgorithm {
317 name: CryptoAlgorithm::Ed448,
318 };
319 CryptoKey::new(
320 cx,
321 global,
322 KeyType::Private,
323 extractable,
324 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
325 usages,
326 Handle::Ed448PrivateKey(curve_private_key),
327 )
328 },
329 // If format is "jwk":
330 KeyFormat::Jwk => {
331 // Step 2.1.
332 // If keyData is a JsonWebKey dictionary:
333 // Let jwk equal keyData.
334 // Otherwise:
335 // Throw a DataError.
336 let jwk = JsonWebKey::parse(cx, key_data)?;
337
338 // Step 2.2. If the d field is present and usages contains a value which is not "sign",
339 // or, if the d field is not present and usages contains a value which is not "verify"
340 // then throw a SyntaxError.
341 if jwk.d.is_some() && usages.iter().any(|usage| *usage != KeyUsage::Sign) {
342 return Err(Error::Syntax(Some(
343 "The d field is present and if usages contains an entry which is not \
344 \"sign\""
345 .into(),
346 )));
347 }
348 if jwk.d.is_none() && usages.iter().any(|usage| *usage != KeyUsage::Verify) {
349 return Err(Error::Syntax(Some(
350 "The d field is not present and if usages contains a value which is not \
351 \"verify\""
352 .into(),
353 )));
354 }
355
356 // Step 2.3. If the kty field of jwk is not "OKP", then throw a DataError.
357 if jwk.kty.as_ref().is_none_or(|kty| kty != "OKP") {
358 return Err(Error::Data(Some(
359 "The kty field of jwk is not \"OKP\"".into(),
360 )));
361 }
362
363 // Step 2.4. If the crv field of jwk is not "Ed448", then throw a DataError.
364 if jwk.crv.as_ref().is_none_or(|crv| crv != "Ed448") {
365 return Err(Error::Data(Some(
366 "The crv field of jwk is not \"Ed448\"".into(),
367 )));
368 }
369
370 // Step 2.5. If the alg field of jwk is present and is not "Ed448" or "EdDSA", then
371 // throw a DataError.
372 if jwk
373 .alg
374 .as_ref()
375 .is_some_and(|alg| !matches!(alg.str().as_ref(), "Ed448" | "EdDSA"))
376 {
377 return Err(Error::Data(Some(
378 "The 'alg' field is different from 'Ed448' and 'EdDSA'".into(),
379 )));
380 }
381
382 // Step 2.6. If usages is non-empty and the use field of jwk is present and is not
383 // "sig", then throw a DataError.
384 if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "sig") {
385 return Err(Error::Data(Some(
386 "Usages is non-empty and the use field of jwk is present and is not equal to \
387 \"sig\""
388 .into(),
389 )));
390 }
391
392 // Step 2.7. If the key_ops field of jwk is present, and is invalid according to the
393 // requirements of JSON Web Key [JWK], or it does not contain all of the specified
394 // usages values, then throw a DataError.
395 jwk.check_key_ops(&usages)?;
396
397 // Step 2.8. If the ext field of jwk is present and has the value false and extractable
398 // is true, then throw a DataError.
399 if jwk.ext.as_ref().is_some_and(|ext| !ext) && extractable {
400 return Err(Error::Data(Some(
401 "The ext field of jwk is present and has the value false \
402 and extractable is true"
403 .into(),
404 )));
405 }
406
407 // Step 2.9.
408 // If the d field is present:
409 let (handle, key_type) = if jwk.d.is_some() {
410 // Step 2.9.1. If jwk does not meet the requirements of the JWK private key format
411 // described in Section 2 of [RFC8037], then throw a DataError.
412 let d = jwk.decode_required_string_field(JwkStringField::D)?;
413 let x = jwk.decode_required_string_field(JwkStringField::X)?;
414 let private_key_bytes = d.as_slice();
415 let public_key_bytes = PublicKeyBytes(x.as_slice().try_into().map_err(|_| {
416 Error::Data(Some("Invalid length of public key in 'x' field".into()))
417 })?);
418 let private_key = SigningKey::try_from(private_key_bytes).map_err(|_| {
419 Error::Data(Some("Failed to import private key from 'd' field".into()))
420 })?;
421 let public_key = VerifyingKey::try_from(public_key_bytes).map_err(|_| {
422 Error::Data(Some("Failed to import public key from 'x' field".into()))
423 })?;
424 if private_key.verifying_key() != public_key {
425 return Err(Error::Data(Some(
426 "Public key in 'x' field does not match private key in 'd' field".into(),
427 )));
428 };
429
430 // Step 2.9.2. Let key be a new CryptoKey object that represents the Ed448 private
431 // key identified by interpreting jwk according to Section 2 of [RFC8037].
432 // NOTE: The CryptoKey is created in Step 2.10 - Step 2.12.
433 let handle = Handle::Ed448PrivateKey(private_key);
434
435 // Step 2.9.3. Set the [[type]] internal slot of Key to "private".
436 let key_type = KeyType::Private;
437
438 (handle, key_type)
439 }
440 // Otherwise:
441 else {
442 // Step 2.9.1. If jwk does not meet the requirements of the JWK public key format
443 // described in Section 2 of [RFC8037], then throw a DataError.
444 let x = jwk.decode_required_string_field(JwkStringField::X)?;
445 let public_key_bytes = PublicKeyBytes(x.as_slice().try_into().map_err(|_| {
446 Error::Data(Some("Invalid length of public key in 'x' field".into()))
447 })?);
448 let public_key = VerifyingKey::try_from(public_key_bytes).map_err(|_| {
449 Error::Data(Some("Failed to import public key from 'x' field".into()))
450 })?;
451
452 // Step 2.9.2. Let key be a new CryptoKey object that represents the Ed448 public
453 // key identified by interpreting jwk according to Section 2 of [RFC8037].
454 // NOTE: The CryptoKey is created in Step 2.10 - Step 2.12.
455 let handle = Handle::Ed448PublicKey(public_key);
456
457 // Step 2.9.3. Set the [[type]] internal slot of Key to "public".
458 let key_type = KeyType::Public;
459
460 (handle, key_type)
461 };
462
463 // Step 2.10. Let algorithm be a new instance of a KeyAlgorithm object.
464 // Step 2.11. Set the name attribute of algorithm to "Ed448".
465 // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
466 let algorithm = SubtleKeyAlgorithm {
467 name: CryptoAlgorithm::Ed448,
468 };
469 CryptoKey::new(
470 cx,
471 global,
472 key_type,
473 extractable,
474 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
475 usages,
476 handle,
477 )
478 },
479 // If format is "raw":
480 KeyFormat::Raw | KeyFormat::Raw_public => {
481 // Step 2.1. If usages contains a value which is not "verify" then throw a SyntaxError.
482 if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
483 return Err(Error::Syntax(Some(
484 "Usages contains an entry which is not one of \"verify\"".into(),
485 )));
486 }
487
488 // Step 2.2. Let data be keyData.
489 let data = key_data;
490
491 // Step 2.3. If the length in bits of data is not 448 then throw a DataError.
492 // NOTE: It should be "not 456", instead of "not 448", according to
493 // <https://www.rfc-editor.org/info/rfc8032/#section-5.2.5>
494 if data.len() != 57 {
495 return Err(Error::Data(Some("The key length is not 456 bits".into())));
496 }
497
498 // Step 2.4. Let algorithm be a new KeyAlgorithm object.
499 // Step 2.5. Set the name attribute of algorithm to "Ed448".
500 let algorithm = SubtleKeyAlgorithm {
501 name: CryptoAlgorithm::Ed448,
502 };
503
504 // Step 2.6. Let key be a new CryptoKey associated with the relevant global object of
505 // this [HTML], and that represents data.
506 // Step 2.7. Set the [[type]] internal slot of key to "public"
507 // Step 2.8. Set the [[algorithm]] internal slot of key to algorithm.
508 let public_key_bytes =
509 PublicKeyBytes(data.try_into().map_err(|_| {
510 Error::Data(Some("Invalid length of public key raw bytes".into()))
511 })?);
512 let public_key = VerifyingKey::try_from(public_key_bytes).map_err(|_| {
513 Error::Data(Some("Failed to import public key from raw bytes".into()))
514 })?;
515 CryptoKey::new(
516 cx,
517 global,
518 KeyType::Public,
519 extractable,
520 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
521 usages,
522 Handle::Ed448PublicKey(public_key),
523 )
524 },
525 // Otherwise:
526 _ => {
527 // throw a NotSupportedError.
528 return Err(Error::NotSupported(Some(
529 "Unsupported import key format for Ed448".into(),
530 )));
531 },
532 };
533
534 // Step 3. Return key
535 Ok(key)
536}
537
538/// <https://wicg.github.io/webcrypto-secure-curves/#ed448-operations>
539pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
540 // Step 1. Let key be the CryptoKey to be exported.
541
542 // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
543 // slot of key cannot be accessed, then throw an OperationError.
544 // NOTE: Done in Step 3.
545
546 // Step 3.
547 let result = match format {
548 // If format is "spki":
549 KeyFormat::Spki => {
550 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
551 // InvalidAccessError.
552 if key.Type() != KeyType::Public {
553 return Err(Error::InvalidAccess(Some(
554 "[[type]] internal slot of key is not \"public\"".into(),
555 )));
556 }
557
558 // Step 3.2. Let data be an instance of the subjectPublicKeyInfo ASN.1 structure defined
559 // in [RFC5280] with the following properties:
560 // * Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the following
561 // properties:
562 // * Set the algorithm object identifier to the id-Ed448 OID defined in
563 // [RFC8410].
564 // * Set the subjectPublicKey field to keyData.
565 let Handle::Ed448PublicKey(public_key) = key.handle() else {
566 return Err(Error::Operation(Some(
567 "[[handle]] internal slot of key is not an Ed448 public key".into(),
568 )));
569 };
570 let data = public_key.to_public_key_der().map_err(|_| {
571 Error::Operation(Some(
572 "Failed to convert Ed448 public key to subjectPublicKeyInfo ASN.1 structure"
573 .into(),
574 ))
575 })?;
576
577 // Step 3.3. Let result be a new ArrayBuffer associated with the relevant global object
578 // of this [HTML], and containing data.
579 // NOTE: The conversion to a new ArrayBuffer is done in SubtleCrypto::ExportKey.
580 ExportedKey::new_bytes(data.into_vec())
581 },
582 // If format is "pkcs8":
583 KeyFormat::Pkcs8 => {
584 // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
585 // InvalidAccessError.
586 if key.Type() != KeyType::Private {
587 return Err(Error::InvalidAccess(Some(
588 "[[type]] internal slot of key is not \"private\"".into(),
589 )));
590 }
591
592 // Step 3.2. Let data be an instance of the privateKeyInfo ASN.1 structure defined in
593 // [RFC5208] with the following properties:
594 // * Set the version field to 0.
595 // * Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1 type
596 // with the following properties:
597 // * Set the algorithm object identifier to the id-Ed448 OID defined in
598 // [RFC8410].
599 // * Set the privateKey field to the result of DER-encoding a CurvePrivateKey ASN.1
600 // type, as defined in Section 7 of [RFC8410], that represents the Ed448 private
601 // key represented by the [[handle]] internal slot of key
602 //
603 // NOTE: If we directly call `EncodePrivateKey::to_pkcs8_der` on `private_key`, the
604 // resultant PKCS#8 document will include the public key, which does not match the
605 // specification. Therefore, we manually construct the PrivateKeyInfoRef.
606 let Handle::Ed448PrivateKey(private_key) = key.handle() else {
607 return Err(Error::Operation(Some(
608 "[[handle]] internal slot of key is not an Ed448 private key".into(),
609 )));
610 };
611 let curve_private_key = OctetStringRef::new(private_key.as_bytes()).map_err(|_| {
612 Error::Operation(Some(
613 "Failed to construct CurvePrivateKey ASN.1 structure".into(),
614 ))
615 })?;
616 let encoded_curve_private_key: Zeroizing<Vec<u8>> = curve_private_key
617 .to_der()
618 .map_err(|_| {
619 Error::Operation(Some(
620 "Failed to encode CurvePrivateKey ASN.1 structure in DER-encoding".into(),
621 ))
622 })?
623 .into();
624 let private_key_field =
625 OctetStringRef::new(&encoded_curve_private_key).map_err(|_| {
626 Error::Operation(Some(
627 "Failed to construct privateKey field of privateKeyInfo ASN.1 structure"
628 .into(),
629 ))
630 })?;
631 let data = PrivateKeyInfoRef {
632 algorithm: AlgorithmIdentifierRef {
633 oid: ObjectIdentifier::new_unwrap(ED448_OID_STRING),
634 parameters: None,
635 },
636 private_key: private_key_field,
637 public_key: None,
638 };
639
640 // Step 3.3. Let result be a new ArrayBuffer associated with the relevant global object
641 // of this [HTML], and containing data.
642 // NOTE: The conversion to a new ArrayBuffer is done in SubtleCrypto::ExportKey.
643 ExportedKey::new_bytes(data.to_der().map_err(|_| {
644 Error::Operation(Some(
645 "Failed to encode privateKeyInfo ASN.1 structure in DER-encoding".into(),
646 ))
647 })?)
648 },
649 // If format is "jwk":
650 KeyFormat::Jwk => {
651 // Step 3.1. Let jwk be a new JsonWebKey dictionary.
652 let mut jwk = JsonWebKey::default();
653
654 // Step 3.2. Set the kty attribute of jwk to "OKP".
655 jwk.kty = Some(DOMString::from("OKP"));
656
657 // Step 3.3. Set the alg attribute of jwk to "Ed448".
658 jwk.alg = Some(DOMString::from("Ed448"));
659
660 // Step 3.4. Set the crv attribute of jwk to "Ed448".
661 jwk.crv = Some(DOMString::from("Ed448"));
662
663 // Step 3.5. Set the x attribute of jwk according to the definition in Section 2 of
664 // [RFC8037].
665 match key.handle() {
666 Handle::Ed448PrivateKey(private_key) => {
667 jwk.encode_string_field(
668 JwkStringField::X,
669 private_key.verifying_key().as_bytes().as_slice(),
670 );
671 },
672 Handle::Ed448PublicKey(public_key) => {
673 jwk.encode_string_field(JwkStringField::X, public_key.as_bytes().as_slice());
674 },
675 _ => {
676 return Err(Error::Operation(Some(
677 "[[handle]] internal slot of key is not an Ed448 key".into(),
678 )));
679 },
680 }
681
682 // Step 3.6. If the [[type]] internal slot of key is "private"
683 // Set the d attribute of jwk according to the definition in Section 2 of [RFC8037].
684 if key.Type() == KeyType::Private {
685 let Handle::Ed448PrivateKey(private_key) = key.handle() else {
686 return Err(Error::Operation(Some(
687 "[[handle]] internal slot of key is not an Ed448 private key".into(),
688 )));
689 };
690 jwk.encode_string_field(JwkStringField::D, private_key.as_bytes().as_slice());
691 }
692
693 // Step 3.7. Set the key_ops attribute of jwk to the usages attribute of key.
694 jwk.set_key_ops(&key.usages());
695
696 // Step 3.8. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
697 jwk.ext = Some(key.Extractable());
698
699 // Step 3.9. Let result be the result of converting jwk to an ECMAScript Object, as
700 // defined by [WebIDL].
701 // NOTE: The conversion to an ECMAScript Object is done by SubtleCrypto::ExportKey.
702 ExportedKey::new_jwk(jwk)
703 },
704 // If format is "raw":
705 KeyFormat::Raw | KeyFormat::Raw_public => {
706 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
707 // InvalidAccessError.
708 if key.Type() != KeyType::Public {
709 return Err(Error::InvalidAccess(Some(
710 "[[type]] internal slot of key is not \"public\"".into(),
711 )));
712 }
713
714 // Step 3.2. Let data be an octet string representing the Ed448 public key represented
715 // by the [[handle]] internal slot of key.
716 let Handle::Ed448PublicKey(public_key) = key.handle() else {
717 return Err(Error::Operation(Some(
718 "[[handle]] internal slot of key is not an Ed448 public key".into(),
719 )));
720 };
721 let data = public_key.as_bytes();
722
723 // Step 3.3. Let result be a new ArrayBuffer associated with the relevant global object
724 // of this [HTML], and containing data.
725 // NOTE: The conversion to a new ArrayBuffer is done in SubtleCrypto::ExportKey.
726 ExportedKey::new_bytes(data.to_vec())
727 },
728 // Otherwise:
729 _ => {
730 // throw a NotSupportedError.
731 return Err(Error::NotSupported(Some(
732 "Unsupported export key format for Ed448".into(),
733 )));
734 },
735 };
736
737 // Step 4. Return result.
738 Ok(result)
739}