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