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