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, KeyUsageVecHelper};
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.usage_intersection(&[KeyUsage::Verify]),
182 Handle::Ed448PublicKey(public_key),
183 );
184
185 // Step 10. Let privateKey be a new CryptoKey associated with the relevant global object of this
186 // [HTML], and representing the private key of the generated key pair.
187 // Step 11. Set the [[type]] internal slot of privateKey to "private"
188 // Step 12. Set the [[algorithm]] internal slot of privateKey to algorithm.
189 // Step 13. Set the [[extractable]] internal slot of privateKey to extractable.
190 // Step 14. Set the [[usages]] internal slot of privateKey to be the usage intersection of
191 // usages and [ "sign" ].
192 let private_key = CryptoKey::new(
193 cx,
194 global,
195 KeyType::Private,
196 extractable,
197 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
198 usages.usage_intersection(&[KeyUsage::Sign]),
199 Handle::Ed448PrivateKey(private_key),
200 );
201
202 // Step 15. Let result be a new CryptoKeyPair dictionary.
203 // Step 16. Set the publicKey attribute of result to be publicKey.
204 // Step 17. Set the privateKey attribute of result to be privateKey.
205 let result = CryptoKeyPair {
206 publicKey: Some(public_key),
207 privateKey: Some(private_key),
208 };
209
210 // Step 18. Return the result of converting result to an ECMAScript Object, as defined by
211 // [WebIDL].
212 // NOTE: The conversion of result to an ECMAScript Object is done in SubtleCrypto::Generate.
213 Ok(result)
214}
215
216/// <https://wicg.github.io/webcrypto-secure-curves/#ed448-operations>
217pub(crate) fn import_key(
218 cx: &mut JSContext,
219 global: &GlobalScope,
220 format: KeyFormat,
221 key_data: &[u8],
222 extractable: bool,
223 usages: Vec<KeyUsage>,
224) -> Result<DomRoot<CryptoKey>, Error> {
225 // Step 1. Let keyData be the key data to be imported.
226
227 // Step 2.
228 let key = match format {
229 // If format is "spki":
230 KeyFormat::Spki => {
231 // Step 2.1. If usages contains a value which is not "verify" then throw a SyntaxError.
232 if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
233 return Err(Error::Syntax(Some(
234 "Usages contains an entry which is not \"verify\"".into(),
235 )));
236 }
237
238 // Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
239 // algorithm over keyData.
240 // Step 2.3. If an error occurred while parsing, then throw a DataError.
241 // Step 2.4. If the algorithm object identifier field of the algorithm
242 // AlgorithmIdentifier field of spki is not equal to the id-Ed448 object identifier
243 // defined in [RFC8410], then throw a DataError.
244 // Step 2.5. If the parameters field of the algorithm AlgorithmIdentifier field of spki
245 // is present, then throw a DataError.
246 // Step 2.6. Let publicKey be the Ed448 public key identified by the subjectPublicKey
247 // field of spki.
248 let public_key = VerifyingKey::from_public_key_der(key_data).map_err(|_| {
249 Error::Data(Some(
250 "Failed to parse the Ed448 public key in SPKI format".into(),
251 ))
252 })?;
253
254 // Step 2.7. Let key be a new CryptoKey associated with the relevant global object of
255 // this [HTML], and that represents publicKey.
256 // Step 2.8. Set the [[type]] internal slot of key to "public"
257 // Step 2.9. Let algorithm be a new KeyAlgorithm.
258 // Step 2.10. Set the name attribute of algorithm to "Ed448".
259 // Step 2.11. Set the [[algorithm]] internal slot of key to algorithm.
260 let algorithm = SubtleKeyAlgorithm {
261 name: CryptoAlgorithm::Ed448,
262 };
263 CryptoKey::new(
264 cx,
265 global,
266 KeyType::Public,
267 extractable,
268 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
269 usages.normalized_value(),
270 Handle::Ed448PublicKey(public_key),
271 )
272 },
273 // If format is "pkcs8":
274 KeyFormat::Pkcs8 => {
275 // Step 2.1. If usages contains a value which is not "sign" then throw a SyntaxError.
276 if usages.iter().any(|usage| *usage != KeyUsage::Sign) {
277 return Err(Error::Syntax(Some(
278 "Usages contains an entry which is not \"sign\"".into(),
279 )));
280 }
281
282 // Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
283 // algorithm over keyData.
284 // Step 2.3. If an error occurs while parsing, then throw a DataError.
285 // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
286 // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-Ed448 object
287 // identifier defined in [RFC8410], then throw a DataError.
288 // Step 2.5. If the parameters field of the privateKeyAlgorithm
289 // PrivateKeyAlgorithmIdentifier field of privateKeyInfo is present, then throw a
290 // DataError.
291 // Step 2.6. Let curvePrivateKey be the result of performing the parse an ASN.1
292 // structure algorithm, with data as the privateKey field of privateKeyInfo, structure
293 // as the ASN.1 CurvePrivateKey structure specified in Section 7 of [RFC8410], and
294 // exactData set to true.
295 // Step 2.7. If an error occurred while parsing, then throw a DataError.
296 let curve_private_key = SigningKey::from_pkcs8_der(key_data).map_err(|_| {
297 Error::Data(Some(
298 "Failed to parse the Ed448 private key in PKCS#8 format".into(),
299 ))
300 })?;
301
302 // Step 2.8. Let key be a new CryptoKey associated with the relevant global object of
303 // this [HTML], and that represents the Ed448 private key identified by curvePrivateKey.
304 // Step 2.9. Set the [[type]] internal slot of key to "private"
305 // Step 2.10. Let algorithm be a new KeyAlgorithm.
306 // Step 2.11. Set the name attribute of algorithm to "Ed448".
307 // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
308 let algorithm = SubtleKeyAlgorithm {
309 name: CryptoAlgorithm::Ed448,
310 };
311 CryptoKey::new(
312 cx,
313 global,
314 KeyType::Private,
315 extractable,
316 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
317 usages.normalized_value(),
318 Handle::Ed448PrivateKey(curve_private_key),
319 )
320 },
321 // If format is "jwk":
322 KeyFormat::Jwk => {
323 // Step 2.1.
324 // If keyData is a JsonWebKey dictionary:
325 // Let jwk equal keyData.
326 // Otherwise:
327 // Throw a DataError.
328 let jwk = JsonWebKey::parse(cx, key_data)?;
329
330 // Step 2.2. If the d field is present and usages contains a value which is not "sign",
331 // or, if the d field is not present and usages contains a value which is not "verify"
332 // then throw a SyntaxError.
333 if jwk.d.is_some() && usages.iter().any(|usage| *usage != KeyUsage::Sign) {
334 return Err(Error::Syntax(Some(
335 "The d field is present and if usages contains an entry which is not \
336 \"sign\""
337 .into(),
338 )));
339 }
340 if jwk.d.is_none() && usages.iter().any(|usage| *usage != KeyUsage::Verify) {
341 return Err(Error::Syntax(Some(
342 "The d field is not present and if usages contains a value which is not \
343 \"verify\""
344 .into(),
345 )));
346 }
347
348 // Step 2.3. If the kty field of jwk is not "OKP", then throw a DataError.
349 if jwk.kty.as_ref().is_none_or(|kty| kty != "OKP") {
350 return Err(Error::Data(Some(
351 "The kty field of jwk is not \"OKP\"".into(),
352 )));
353 }
354
355 // Step 2.4. If the crv field of jwk is not "Ed448", then throw a DataError.
356 if jwk.crv.as_ref().is_none_or(|crv| crv != "Ed448") {
357 return Err(Error::Data(Some(
358 "The crv field of jwk is not \"Ed448\"".into(),
359 )));
360 }
361
362 // Step 2.5. If the alg field of jwk is present and is not "Ed448" or "EdDSA", then
363 // throw a DataError.
364 if jwk
365 .alg
366 .as_ref()
367 .is_some_and(|alg| !matches!(alg.str().as_ref(), "Ed448" | "EdDSA"))
368 {
369 return Err(Error::Data(Some(
370 "The 'alg' field is different from 'Ed448' and 'EdDSA'".into(),
371 )));
372 }
373
374 // Step 2.6. If usages is non-empty and the use field of jwk is present and is not
375 // "sig", then throw a DataError.
376 if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "sig") {
377 return Err(Error::Data(Some(
378 "Usages is non-empty and the use field of jwk is present and is not equal to \
379 \"sig\""
380 .into(),
381 )));
382 }
383
384 // Step 2.7. If the key_ops field of jwk is present, and is invalid according to the
385 // requirements of JSON Web Key [JWK], or it does not contain all of the specified
386 // usages values, then throw a DataError.
387 jwk.check_key_ops(&usages)?;
388
389 // Step 2.8. If the ext field of jwk is present and has the value false and extractable
390 // is true, then throw a DataError.
391 if jwk.ext.as_ref().is_some_and(|ext| !ext) && extractable {
392 return Err(Error::Data(Some(
393 "The ext field of jwk is present and has the value false \
394 and extractable is true"
395 .into(),
396 )));
397 }
398
399 // Step 2.9.
400 // If the d field is present:
401 let (handle, key_type) = if jwk.d.is_some() {
402 // Step 2.9.1. If jwk does not meet the requirements of the JWK private key format
403 // described in Section 2 of [RFC8037], then throw a DataError.
404 let d = jwk.decode_required_string_field(JwkStringField::D)?;
405 let x = jwk.decode_required_string_field(JwkStringField::X)?;
406 let private_key_bytes = d.as_slice();
407 let public_key_bytes = PublicKeyBytes(x.as_slice().try_into().map_err(|_| {
408 Error::Data(Some("Invalid length of public key in 'x' field".into()))
409 })?);
410 let private_key = SigningKey::try_from(private_key_bytes).map_err(|_| {
411 Error::Data(Some("Failed to import private key from 'd' field".into()))
412 })?;
413 let public_key = VerifyingKey::try_from(public_key_bytes).map_err(|_| {
414 Error::Data(Some("Failed to import public key from 'x' field".into()))
415 })?;
416 if private_key.verifying_key() != public_key {
417 return Err(Error::Data(Some(
418 "Public key in 'x' field does not match private key in 'd' field".into(),
419 )));
420 };
421
422 // Step 2.9.2. Let key be a new CryptoKey object that represents the Ed448 private
423 // key identified by interpreting jwk according to Section 2 of [RFC8037].
424 // NOTE: The CryptoKey is created in Step 2.10 - Step 2.12.
425 let handle = Handle::Ed448PrivateKey(private_key);
426
427 // Step 2.9.3. Set the [[type]] internal slot of Key to "private".
428 let key_type = KeyType::Private;
429
430 (handle, key_type)
431 }
432 // Otherwise:
433 else {
434 // Step 2.9.1. If jwk does not meet the requirements of the JWK public key format
435 // described in Section 2 of [RFC8037], then throw a DataError.
436 let x = jwk.decode_required_string_field(JwkStringField::X)?;
437 let public_key_bytes = PublicKeyBytes(x.as_slice().try_into().map_err(|_| {
438 Error::Data(Some("Invalid length of public key in 'x' field".into()))
439 })?);
440 let public_key = VerifyingKey::try_from(public_key_bytes).map_err(|_| {
441 Error::Data(Some("Failed to import public key from 'x' field".into()))
442 })?;
443
444 // Step 2.9.2. Let key be a new CryptoKey object that represents the Ed448 public
445 // key identified by interpreting jwk according to Section 2 of [RFC8037].
446 // NOTE: The CryptoKey is created in Step 2.10 - Step 2.12.
447 let handle = Handle::Ed448PublicKey(public_key);
448
449 // Step 2.9.3. Set the [[type]] internal slot of Key to "public".
450 let key_type = KeyType::Public;
451
452 (handle, key_type)
453 };
454
455 // Step 2.10. Let algorithm be a new instance of a KeyAlgorithm object.
456 // Step 2.11. Set the name attribute of algorithm to "Ed448".
457 // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
458 let algorithm = SubtleKeyAlgorithm {
459 name: CryptoAlgorithm::Ed448,
460 };
461 CryptoKey::new(
462 cx,
463 global,
464 key_type,
465 extractable,
466 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
467 usages.normalized_value(),
468 handle,
469 )
470 },
471 // If format is "raw":
472 KeyFormat::Raw | KeyFormat::Raw_public => {
473 // Step 2.1. If usages contains a value which is not "verify" then throw a SyntaxError.
474 if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
475 return Err(Error::Syntax(Some(
476 "Usages contains an entry which is not one of \"verify\"".into(),
477 )));
478 }
479
480 // Step 2.2. Let data be keyData.
481 let data = key_data;
482
483 // Step 2.3. If the length in bits of data is not 448 then throw a DataError.
484 // NOTE: It should be "not 456", instead of "not 448", according to
485 // <https://www.rfc-editor.org/info/rfc8032/#section-5.2.5>
486 if data.len() != 57 {
487 return Err(Error::Data(Some("The key length is not 456 bits".into())));
488 }
489
490 // Step 2.4. Let algorithm be a new KeyAlgorithm object.
491 // Step 2.5. Set the name attribute of algorithm to "Ed448".
492 let algorithm = SubtleKeyAlgorithm {
493 name: CryptoAlgorithm::Ed448,
494 };
495
496 // Step 2.6. Let key be a new CryptoKey associated with the relevant global object of
497 // this [HTML], and that represents data.
498 // Step 2.7. Set the [[type]] internal slot of key to "public"
499 // Step 2.8. Set the [[algorithm]] internal slot of key to algorithm.
500 let public_key_bytes =
501 PublicKeyBytes(data.try_into().map_err(|_| {
502 Error::Data(Some("Invalid length of public key raw bytes".into()))
503 })?);
504 let public_key = VerifyingKey::try_from(public_key_bytes).map_err(|_| {
505 Error::Data(Some("Failed to import public key from raw bytes".into()))
506 })?;
507 CryptoKey::new(
508 cx,
509 global,
510 KeyType::Public,
511 extractable,
512 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
513 usages.normalized_value(),
514 Handle::Ed448PublicKey(public_key),
515 )
516 },
517 // Otherwise:
518 _ => {
519 // throw a NotSupportedError.
520 return Err(Error::NotSupported(Some(
521 "Unsupported import key format for Ed448".into(),
522 )));
523 },
524 };
525
526 // Step 3. Return key
527 Ok(key)
528}
529
530/// <https://wicg.github.io/webcrypto-secure-curves/#ed448-operations>
531pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
532 // Step 1. Let key be the CryptoKey to be exported.
533
534 // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
535 // slot of key cannot be accessed, then throw an OperationError.
536 // NOTE: Done in Step 3.
537
538 // Step 3.
539 let result = match format {
540 // If format is "spki":
541 KeyFormat::Spki => {
542 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
543 // InvalidAccessError.
544 if key.Type() != KeyType::Public {
545 return Err(Error::InvalidAccess(Some(
546 "[[type]] internal slot of key is not \"public\"".into(),
547 )));
548 }
549
550 // Step 3.2. Let data be an instance of the subjectPublicKeyInfo ASN.1 structure defined
551 // in [RFC5280] with the following properties:
552 // * Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the following
553 // properties:
554 // * Set the algorithm object identifier to the id-Ed448 OID defined in
555 // [RFC8410].
556 // * Set the subjectPublicKey field to keyData.
557 let Handle::Ed448PublicKey(public_key) = key.handle() else {
558 return Err(Error::Operation(Some(
559 "[[handle]] internal slot of key is not an Ed448 public key".into(),
560 )));
561 };
562 let data = public_key.to_public_key_der().map_err(|_| {
563 Error::Operation(Some(
564 "Failed to convert Ed448 public key to subjectPublicKeyInfo ASN.1 structure"
565 .into(),
566 ))
567 })?;
568
569 // Step 3.3. Let result be a new ArrayBuffer associated with the relevant global object
570 // of this [HTML], and containing data.
571 // NOTE: The conversion to a new ArrayBuffer is done in SubtleCrypto::ExportKey.
572 ExportedKey::new_bytes(data.into_vec())
573 },
574 // If format is "pkcs8":
575 KeyFormat::Pkcs8 => {
576 // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
577 // InvalidAccessError.
578 if key.Type() != KeyType::Private {
579 return Err(Error::InvalidAccess(Some(
580 "[[type]] internal slot of key is not \"private\"".into(),
581 )));
582 }
583
584 // Step 3.2. Let data be an instance of the privateKeyInfo ASN.1 structure defined in
585 // [RFC5208] with the following properties:
586 // * Set the version field to 0.
587 // * Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1 type
588 // with the following properties:
589 // * Set the algorithm object identifier to the id-Ed448 OID defined in
590 // [RFC8410].
591 // * Set the privateKey field to the result of DER-encoding a CurvePrivateKey ASN.1
592 // type, as defined in Section 7 of [RFC8410], that represents the Ed448 private
593 // key represented by the [[handle]] internal slot of key
594 //
595 // NOTE: If we directly call `EncodePrivateKey::to_pkcs8_der` on `private_key`, the
596 // resultant PKCS#8 document will include the public key, which does not match the
597 // specification. Therefore, we manually construct the PrivateKeyInfoRef.
598 let Handle::Ed448PrivateKey(private_key) = key.handle() else {
599 return Err(Error::Operation(Some(
600 "[[handle]] internal slot of key is not an Ed448 private key".into(),
601 )));
602 };
603 let curve_private_key = OctetStringRef::new(private_key.as_bytes()).map_err(|_| {
604 Error::Operation(Some(
605 "Failed to construct CurvePrivateKey ASN.1 structure".into(),
606 ))
607 })?;
608 let encoded_curve_private_key: Zeroizing<Vec<u8>> = curve_private_key
609 .to_der()
610 .map_err(|_| {
611 Error::Operation(Some(
612 "Failed to encode CurvePrivateKey ASN.1 structure in DER-encoding".into(),
613 ))
614 })?
615 .into();
616 let private_key_field =
617 OctetStringRef::new(&encoded_curve_private_key).map_err(|_| {
618 Error::Operation(Some(
619 "Failed to construct privateKey field of privateKeyInfo ASN.1 structure"
620 .into(),
621 ))
622 })?;
623 let data = PrivateKeyInfoRef {
624 algorithm: AlgorithmIdentifierRef {
625 oid: ObjectIdentifier::new_unwrap(ED448_OID_STRING),
626 parameters: None,
627 },
628 private_key: private_key_field,
629 public_key: None,
630 };
631
632 // Step 3.3. Let result be a new ArrayBuffer associated with the relevant global object
633 // of this [HTML], and containing data.
634 // NOTE: The conversion to a new ArrayBuffer is done in SubtleCrypto::ExportKey.
635 ExportedKey::new_bytes(data.to_der().map_err(|_| {
636 Error::Operation(Some(
637 "Failed to encode privateKeyInfo ASN.1 structure in DER-encoding".into(),
638 ))
639 })?)
640 },
641 // If format is "jwk":
642 KeyFormat::Jwk => {
643 // Step 3.1. Let jwk be a new JsonWebKey dictionary.
644 let mut jwk = JsonWebKey::default();
645
646 // Step 3.2. Set the kty attribute of jwk to "OKP".
647 jwk.kty = Some(DOMString::from("OKP"));
648
649 // Step 3.3. Set the alg attribute of jwk to "Ed448".
650 jwk.alg = Some(DOMString::from("Ed448"));
651
652 // Step 3.4. Set the crv attribute of jwk to "Ed448".
653 jwk.crv = Some(DOMString::from("Ed448"));
654
655 // Step 3.5. Set the x attribute of jwk according to the definition in Section 2 of
656 // [RFC8037].
657 match key.handle() {
658 Handle::Ed448PrivateKey(private_key) => {
659 jwk.encode_string_field(
660 JwkStringField::X,
661 private_key.verifying_key().as_bytes().as_slice(),
662 );
663 },
664 Handle::Ed448PublicKey(public_key) => {
665 jwk.encode_string_field(JwkStringField::X, public_key.as_bytes().as_slice());
666 },
667 _ => {
668 return Err(Error::Operation(Some(
669 "[[handle]] internal slot of key is not an Ed448 key".into(),
670 )));
671 },
672 }
673
674 // Step 3.6. If the [[type]] internal slot of key is "private"
675 // Set the d attribute of jwk according to the definition in Section 2 of [RFC8037].
676 if key.Type() == KeyType::Private {
677 let Handle::Ed448PrivateKey(private_key) = key.handle() else {
678 return Err(Error::Operation(Some(
679 "[[handle]] internal slot of key is not an Ed448 private key".into(),
680 )));
681 };
682 jwk.encode_string_field(JwkStringField::D, private_key.as_bytes().as_slice());
683 }
684
685 // Step 3.7. Set the key_ops attribute of jwk to the usages attribute of key.
686 jwk.set_key_ops(&key.usages());
687
688 // Step 3.8. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
689 jwk.ext = Some(key.Extractable());
690
691 // Step 3.9. Let result be the result of converting jwk to an ECMAScript Object, as
692 // defined by [WebIDL].
693 // NOTE: The conversion to an ECMAScript Object is done by SubtleCrypto::ExportKey.
694 ExportedKey::new_jwk(jwk)
695 },
696 // If format is "raw":
697 KeyFormat::Raw | KeyFormat::Raw_public => {
698 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
699 // InvalidAccessError.
700 if key.Type() != KeyType::Public {
701 return Err(Error::InvalidAccess(Some(
702 "[[type]] internal slot of key is not \"public\"".into(),
703 )));
704 }
705
706 // Step 3.2. Let data be an octet string representing the Ed448 public key represented
707 // by the [[handle]] internal slot of key.
708 let Handle::Ed448PublicKey(public_key) = key.handle() else {
709 return Err(Error::Operation(Some(
710 "[[handle]] internal slot of key is not an Ed448 public key".into(),
711 )));
712 };
713 let data = public_key.as_bytes();
714
715 // Step 3.3. Let result be a new ArrayBuffer associated with the relevant global object
716 // of this [HTML], and containing data.
717 // NOTE: The conversion to a new ArrayBuffer is done in SubtleCrypto::ExportKey.
718 ExportedKey::new_bytes(data.to_vec())
719 },
720 // Otherwise:
721 _ => {
722 // throw a NotSupportedError.
723 return Err(Error::NotSupported(Some(
724 "Unsupported export key format for Ed448".into(),
725 )));
726 },
727 };
728
729 // Step 4. Return result.
730 Ok(result)
731}
732
733/// <https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-getPublicKey>
734/// Step 9 - 15, for Ed448
735pub(crate) fn get_public_key(
736 cx: &mut JSContext,
737 global: &GlobalScope,
738 key: &CryptoKey,
739 algorithm: &KeyAlgorithmAndDerivatives,
740 usages: Vec<KeyUsage>,
741) -> Result<DomRoot<CryptoKey>, Error> {
742 // Step 9. If usages contains an entry which is not supported for a public key by the algorithm
743 // identified by algorithm, then throw a SyntaxError.
744 //
745 // NOTE: See "importKey" operation for supported usages
746 if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
747 return Err(Error::Syntax(Some(
748 "Usages contains an entry which is not \"verify\"".into(),
749 )));
750 }
751
752 // Step 10. Let publicKey be a new CryptoKey representing the public key corresponding to the
753 // private key represented by the [[handle]] internal slot of key.
754 // Step 11. If an error occurred, then throw a OperationError.
755 // Step 12. Set the [[type]] internal slot of publicKey to "public".
756 // Step 13. Set the [[algorithm]] internal slot of publicKey to algorithm.
757 // Step 14. Set the [[extractable]] internal slot of publicKey to true.
758 // Step 15. Set the [[usages]] internal slot of publicKey to usages.
759 let Handle::Ed448PrivateKey(private_key) = key.handle() else {
760 return Err(Error::Operation(Some(
761 "[[handle]] internal slot of key is not an Ed448 private key".into(),
762 )));
763 };
764 let public_key = CryptoKey::new(
765 cx,
766 global,
767 KeyType::Public,
768 true,
769 algorithm.clone(),
770 usages,
771 Handle::Ed448PublicKey(private_key.verifying_key()),
772 );
773
774 Ok(public_key)
775}