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