script/dom/subtlecrypto/ecdsa_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 base64ct::{Base64UrlUnpadded, Encoding};
6use digest::Digest;
7use ecdsa::signature::hazmat::{PrehashVerifier, RandomizedPrehashSigner};
8use ecdsa::{Signature, SigningKey, VerifyingKey};
9use elliptic_curve::SecretKey;
10use elliptic_curve::rand_core::OsRng;
11use elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint, ValidatePublicKey};
12use p256::NistP256;
13use p384::NistP384;
14use p521::NistP521;
15use pkcs8::der::Decode;
16use pkcs8::spki::EncodePublicKey;
17use pkcs8::{AssociatedOid, EncodePrivateKey, PrivateKeyInfo, SubjectPublicKeyInfo};
18use sec1::der::asn1::BitString;
19use sec1::{EcParameters, EcPrivateKey, EncodedPoint};
20use sha1::Sha1;
21use sha2::{Sha256, Sha384, Sha512};
22
23use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
24 CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
25};
26use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{JsonWebKey, KeyFormat};
27use crate::dom::bindings::error::Error;
28use crate::dom::bindings::root::DomRoot;
29use crate::dom::bindings::str::DOMString;
30use crate::dom::cryptokey::{CryptoKey, Handle};
31use crate::dom::globalscope::GlobalScope;
32use crate::dom::subtlecrypto::{
33 ALG_ECDSA, ALG_SHA1, ALG_SHA256, ALG_SHA384, ALG_SHA512, ExportedKey, JsonWebKeyExt,
34 KeyAlgorithmAndDerivatives, NAMED_CURVE_P256, NAMED_CURVE_P384, NAMED_CURVE_P521,
35 SUPPORTED_CURVES, SubtleEcKeyAlgorithm, SubtleEcKeyGenParams, SubtleEcKeyImportParams,
36 SubtleEcdsaParams,
37};
38use crate::script_runtime::CanGc;
39
40const P256_PREHASH_LENGTH: usize = 32;
41const P384_PREHASH_LENGTH: usize = 48;
42const P521_PREHASH_LENGTH: usize = 66;
43
44/// <https://w3c.github.io/webcrypto/#ecdsa-operations-sign>
45pub(crate) fn sign(
46 normalized_algorithm: &SubtleEcdsaParams,
47 key: &CryptoKey,
48 message: &[u8],
49) -> Result<Vec<u8>, Error> {
50 // Step 1. If the [[type]] internal slot of key is not "private", then throw an
51 // InvalidAccessError.
52 if key.Type() != KeyType::Private {
53 return Err(Error::InvalidAccess(None));
54 }
55
56 // Step 2. Let hashAlgorithm be the hash member of normalizedAlgorithm.
57 let hash_algorithm = &normalized_algorithm.hash;
58
59 // Step 3. Let M be the result of performing the digest operation specified by hashAlgorithm
60 // using message.
61 let m = match hash_algorithm.name.as_str() {
62 ALG_SHA1 => Sha1::new_with_prefix(message).finalize().to_vec(),
63 ALG_SHA256 => Sha256::new_with_prefix(message).finalize().to_vec(),
64 ALG_SHA384 => Sha384::new_with_prefix(message).finalize().to_vec(),
65 ALG_SHA512 => Sha512::new_with_prefix(message).finalize().to_vec(),
66 _ => return Err(Error::NotSupported(None)),
67 };
68
69 // Step 4. Let d be the ECDSA private key associated with key.
70 // Step 5. Let params be the EC domain parameters associated with key.
71 // Step 6.
72 // If the namedCurve attribute of the [[algorithm]] internal slot of key is "P-256", "P-384" or
73 // "P-521":
74 // 1. Perform the ECDSA signing process, as specified in [RFC6090], Section 5.4.2, with M
75 // as the message, using params as the EC domain parameters, and with d as the private
76 // key.
77 // 2. Let r and s be the pair of integers resulting from performing the ECDSA signing
78 // process.
79 // 3. Let result be an empty byte sequence.
80 // 4. Let n be the smallest integer such that n * 8 is greater than the logarithm to base 2
81 // of the order of the base point of the elliptic curve identified by params.
82 // 5. Convert r to a byte sequence of length n and append it to result.
83 // 6. Convert s to a byte sequence of length n and append it to result.
84 // Otherwise, the namedCurve attribute of the [[algorithm]] internal slot of key is a value
85 // specified in an applicable specification:
86 // Perform the ECDSA signature steps specified in that specification, passing in M, params
87 // and d and resulting in result.
88 // NOTE: We currently do not support other applicable specifications.
89 let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() else {
90 return Err(Error::Operation(None));
91 };
92 let result = match algorithm.named_curve.as_str() {
93 NAMED_CURVE_P256 => {
94 // P-256 expects prehash with length at least 32 bytes. If M is shorter than 32 bytes,
95 // expand it by left padding with zeros.
96 let m = expand_prehash(m, P256_PREHASH_LENGTH);
97
98 let Handle::P256PrivateKey(d) = key.handle() else {
99 return Err(Error::Operation(None));
100 };
101 let signing_key = SigningKey::<NistP256>::from(d);
102 let signature: Signature<NistP256> = signing_key
103 .sign_prehash_with_rng(&mut OsRng, m.as_slice())
104 .map_err(|_| Error::Operation(None))?;
105 signature.to_vec()
106 },
107 NAMED_CURVE_P384 => {
108 // P-384 expects prehash with length at least 48 bytes. If M is shorter than 48 bytes,
109 // expand it by left padding with zeros.
110 let m = expand_prehash(m, P384_PREHASH_LENGTH);
111
112 let Handle::P384PrivateKey(d) = key.handle() else {
113 return Err(Error::Operation(None));
114 };
115 let signing_key = SigningKey::<NistP384>::from(d);
116 let signature: Signature<NistP384> = signing_key
117 .sign_prehash_with_rng(&mut OsRng, m.as_slice())
118 .map_err(|_| Error::Abort(None))?;
119 signature.to_vec()
120 },
121 NAMED_CURVE_P521 => {
122 // P-521 expects prehash with length at least 66 bytes. If M is shorter than 66 bytes,
123 // expand it by left padding with zeros.
124 let m = expand_prehash(m, P521_PREHASH_LENGTH);
125
126 let Handle::P521PrivateKey(d) = key.handle() else {
127 return Err(Error::Operation(None));
128 };
129 let signing_key = p521::ecdsa::SigningKey::from_slice(d.to_bytes().as_slice())
130 .map_err(|_| Error::Operation(None))?;
131 let signature: Signature<NistP521> = signing_key
132 .sign_prehash_with_rng(&mut OsRng, m.as_slice())
133 .map_err(|_| Error::Operation(None))?;
134 signature.to_vec()
135 },
136 _ => return Err(Error::NotSupported(None)),
137 };
138
139 // Step 7. Return result.
140 Ok(result)
141}
142
143/// <https://w3c.github.io/webcrypto/#ecdsa-operations-verify>
144pub(crate) fn verify(
145 normalized_algorithm: &SubtleEcdsaParams,
146 key: &CryptoKey,
147 message: &[u8],
148 signature: &[u8],
149) -> Result<bool, Error> {
150 // Step 1. If the [[type]] internal slot of key is not "public", then throw an
151 // InvalidAccessError.
152 if key.Type() != KeyType::Public {
153 return Err(Error::InvalidAccess(None));
154 }
155
156 // Step 2. Let hashAlgorithm be the hash member of normalizedAlgorithm.
157 let hash_algorithm = &normalized_algorithm.hash;
158
159 // Step 3. Let M be the result of performing the digest operation specified by hashAlgorithm
160 // using message.
161 let m = match hash_algorithm.name.as_str() {
162 ALG_SHA1 => Sha1::new_with_prefix(message).finalize().to_vec(),
163 ALG_SHA256 => Sha256::new_with_prefix(message).finalize().to_vec(),
164 ALG_SHA384 => Sha384::new_with_prefix(message).finalize().to_vec(),
165 ALG_SHA512 => Sha512::new_with_prefix(message).finalize().to_vec(),
166 _ => return Err(Error::NotSupported(None)),
167 };
168
169 // Step 4. Let Q be the ECDSA public key associated with key.
170 // Step 5. Let params be the EC domain parameters associated with key.
171 // Step 6.
172 // If the namedCurve attribute of the [[algorithm]] internal slot of key is "P-256", "P-384" or
173 // "P-521":
174 // 1. Let n be the smallest integer such that n * 8 is greater than the logarithm to base 2
175 // of the order of the base point of the elliptic curve identified by params.
176 // 2. If signature does not have a length of n * 2 bytes, then return false.
177 // 3. Let r be the result of converting the first n bytes of signature to an integer.
178 // 4. Let s be the result of converting the last n bytes of signature to an integer.
179 // 5. Perform the ECDSA verifying process, as specified in [RFC6090], Section 5.4.3, with M
180 // as the received message, (r, s) as the signature and using params as the EC domain
181 // parameters, and Q as the public key.
182 // Otherwise, the namedCurve attribute of the [[algorithm]] internal slot of key is a value
183 // specified in an applicable specification:
184 // Perform the ECDSA verification steps specified in that specification passing in M,
185 // signature, params and Q and resulting in an indication of whether or not the purported
186 // signature is valid.
187 // Step 7. Let result be a boolean with the value true if the signature is valid and the value
188 // false otherwise.
189 // NOTE: We currently do not support other applicable specifications.
190 let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() else {
191 return Err(Error::Operation(None));
192 };
193 let result = match algorithm.named_curve.as_str() {
194 NAMED_CURVE_P256 => {
195 // P-256 expects prehash with length at least 32 bytes. If M is shorter than 32 bytes,
196 // expand it by left padding with zeros.
197 let m = expand_prehash(m, P256_PREHASH_LENGTH);
198
199 let Handle::P256PublicKey(q) = key.handle() else {
200 return Err(Error::Operation(None));
201 };
202 match Signature::<NistP256>::from_slice(signature) {
203 Ok(signature) => {
204 let verifying_key = VerifyingKey::<NistP256>::from(q);
205 verifying_key.verify_prehash(&m, &signature).is_ok()
206 },
207 Err(_) => false,
208 }
209 },
210 NAMED_CURVE_P384 => {
211 // P-384 expects prehash with length at least 48 bytes. If M is shorter than 48 bytes,
212 // expand it by left padding with zeros.
213 let m = expand_prehash(m, P384_PREHASH_LENGTH);
214
215 let Handle::P384PublicKey(q) = key.handle() else {
216 return Err(Error::Operation(None));
217 };
218 match Signature::<NistP384>::from_slice(signature) {
219 Ok(signature) => {
220 let verifying_key = VerifyingKey::<NistP384>::from(q);
221 verifying_key.verify_prehash(&m, &signature).is_ok()
222 },
223 Err(_) => false,
224 }
225 },
226 NAMED_CURVE_P521 => {
227 // P-521 expects prehash with length at least 66 bytes. If M is shorter than 66 bytes,
228 // expand it by left padding with zeros.
229 let m = expand_prehash(m, P521_PREHASH_LENGTH);
230
231 let Handle::P521PublicKey(q) = key.handle() else {
232 return Err(Error::Operation(None));
233 };
234 match (
235 Signature::<NistP521>::from_slice(signature),
236 p521::ecdsa::VerifyingKey::from_sec1_bytes(q.to_sec1_bytes().to_vec().as_slice()),
237 ) {
238 (Ok(signature), Ok(verifying_key)) => {
239 verifying_key.verify_prehash(&m, &signature).is_ok()
240 },
241 _ => false,
242 }
243 },
244 _ => return Err(Error::NotSupported(None)),
245 };
246
247 // Step 8. Return result.
248 Ok(result)
249}
250
251/// <https://w3c.github.io/webcrypto/#ecdsa-operations-generate-key>
252pub(crate) fn generate_key(
253 global: &GlobalScope,
254 normalized_algorithm: &SubtleEcKeyGenParams,
255 extractable: bool,
256 usages: Vec<KeyUsage>,
257 can_gc: CanGc,
258) -> Result<CryptoKeyPair, Error> {
259 // Step 1. If usages contains a value which is not one of "sign" or "verify", then throw a
260 // SyntaxError.
261 if usages
262 .iter()
263 .any(|usage| !matches!(usage, KeyUsage::Sign | KeyUsage::Verify))
264 {
265 return Err(Error::Syntax(None));
266 }
267
268 // Step 2.
269 // If the namedCurve member of normalizedAlgorithm is "P-256", "P-384" or "P-521":
270 // Generate an Elliptic Curve key pair, as defined in [RFC6090] with domain parameters for
271 // the curve identified by the namedCurve member of normalizedAlgorithm.
272 // If the namedCurve member of normalizedAlgorithm is a value specified in an applicable
273 // specification:
274 // Perform the ECDSA generation steps specified in that specification, passing in
275 // normalizedAlgorithm and resulting in an elliptic curve key pair.
276 // Otherwise:
277 // throw a NotSupportedError
278 // Step 3. If performing the key generation operation results in an error, then throw an
279 // OperationError.
280 // NOTE: We currently do not support other applicable specifications.
281 let (private_key_handle, public_key_handle) = match normalized_algorithm.named_curve.as_str() {
282 NAMED_CURVE_P256 => {
283 let private_key = SecretKey::<NistP256>::random(&mut OsRng);
284 let public_key = private_key.public_key();
285 (
286 Handle::P256PrivateKey(private_key),
287 Handle::P256PublicKey(public_key),
288 )
289 },
290 NAMED_CURVE_P384 => {
291 let private_key = SecretKey::<NistP384>::random(&mut OsRng);
292 let public_key = private_key.public_key();
293 (
294 Handle::P384PrivateKey(private_key),
295 Handle::P384PublicKey(public_key),
296 )
297 },
298 NAMED_CURVE_P521 => {
299 let private_key = SecretKey::<NistP521>::random(&mut OsRng);
300 let public_key = private_key.public_key();
301 (
302 Handle::P521PrivateKey(private_key),
303 Handle::P521PublicKey(public_key),
304 )
305 },
306 _ => return Err(Error::NotSupported(None)),
307 };
308
309 // Step 4. Let algorithm be a new EcKeyAlgorithm object.
310 // Step 5. Set the name attribute of algorithm to "ECDSA".
311 // Step 6. Set the namedCurve attribute of algorithm to equal the namedCurve member of
312 // normalizedAlgorithm.
313 let algorithm = SubtleEcKeyAlgorithm {
314 name: ALG_ECDSA.to_string(),
315 named_curve: normalized_algorithm.named_curve.clone(),
316 };
317
318 // Step 7. Let publicKey be a new CryptoKey representing the public key of the generated key pair.
319 // Step 8. Set the [[type]] internal slot of publicKey to "public"
320 // Step 9. Set the [[algorithm]] internal slot of publicKey to algorithm.
321 // Step 10. Set the [[extractable]] internal slot of publicKey to true.
322 // Step 11. Set the [[usages]] internal slot of publicKey to be the usage intersection of
323 // usages and [ "verify" ].
324 let public_key = CryptoKey::new(
325 global,
326 KeyType::Public,
327 true,
328 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm.clone()),
329 usages
330 .iter()
331 .filter(|usage| **usage == KeyUsage::Verify)
332 .cloned()
333 .collect(),
334 public_key_handle,
335 can_gc,
336 );
337
338 // Step 12. Let privateKey be a new CryptoKey representing the private key of the generated key pair.
339 // Step 13. Set the [[type]] internal slot of privateKey to "private"
340 // Step 14. Set the [[algorithm]] internal slot of privateKey to algorithm.
341 // Step 15. Set the [[extractable]] internal slot of privateKey to extractable.
342 // Step 16. Set the [[usages]] internal slot of privateKey to be the usage intersection of
343 // usages and [ "sign" ].
344 let private_key = CryptoKey::new(
345 global,
346 KeyType::Private,
347 extractable,
348 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
349 usages
350 .iter()
351 .filter(|usage| **usage == KeyUsage::Sign)
352 .cloned()
353 .collect(),
354 private_key_handle,
355 can_gc,
356 );
357
358 // Step 17. Let result be a new CryptoKeyPair dictionary.
359 // Step 18. Set the publicKey attribute of result to be publicKey.
360 // Step 19. Set the privateKey attribute of result to be privateKey.
361 let result = CryptoKeyPair {
362 publicKey: Some(public_key),
363 privateKey: Some(private_key),
364 };
365
366 // Step 20. Return result.
367 Ok(result)
368}
369
370/// <https://w3c.github.io/webcrypto/#ecdsa-operations-import-key>
371pub(crate) fn import_key(
372 global: &GlobalScope,
373 normalized_algorithm: &SubtleEcKeyImportParams,
374 format: KeyFormat,
375 key_data: &[u8],
376 extractable: bool,
377 usages: Vec<KeyUsage>,
378 can_gc: CanGc,
379) -> Result<DomRoot<CryptoKey>, Error> {
380 // Step 1. Let keyData be the key data to be imported.
381
382 // Step 2.
383 let key = match format {
384 // If format is "spki":
385 KeyFormat::Spki => {
386 // Step 2.1. If usages contains a value which is not "verify" then throw a SyntaxError.
387 if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
388 return Err(Error::Syntax(None));
389 }
390
391 // Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
392 // algorithm over keyData
393 // Step 2.3. If an error occurred while parsing, then throw a DataError.
394 let spki = SubjectPublicKeyInfo::<_, BitString>::from_der(key_data)
395 .map_err(|_| Error::Data(None))?;
396
397 // Step 2.4. If the algorithm object identifier field of the algorithm
398 // AlgorithmIdentifier field of spki is not equal to the id-ecPublicKey object
399 // identifier defined in [RFC5480], then throw a DataError.
400 if spki.algorithm.oid != elliptic_curve::ALGORITHM_OID {
401 return Err(Error::Data(None));
402 }
403
404 // Step 2.5. If the parameters field of the algorithm AlgorithmIdentifier field of spki
405 // is absent, then throw a DataError.
406 // Step 2.6. Let params be the parameters field of the algorithm AlgorithmIdentifier
407 // field of spki.
408 // Step 2.7. If params is not an instance of the ECParameters ASN.1 type defined in
409 // [RFC5480] that specifies a namedCurve, then throw a DataError.
410 let Some(params): Option<EcParameters> = spki.algorithm.parameters else {
411 return Err(Error::Data(None));
412 };
413
414 // Step 2.8. Let namedCurve be a string whose initial value is undefined.
415 // Step 2.9.
416 // If params is equivalent to the secp256r1 object identifier defined in [RFC5480]:
417 // Set namedCurve "P-256".
418 // If params is equivalent to the secp384r1 object identifier defined in [RFC5480]:
419 // Set namedCurve "P-384".
420 // If params is equivalent to the secp521r1 object identifier defined in [RFC5480]:
421 // Set namedCurve "P-521".
422 let named_curve = match params {
423 EcParameters::NamedCurve(NistP256::OID) => Some(NAMED_CURVE_P256),
424 EcParameters::NamedCurve(NistP384::OID) => Some(NAMED_CURVE_P384),
425 EcParameters::NamedCurve(NistP521::OID) => Some(NAMED_CURVE_P521),
426 _ => None,
427 };
428
429 // Step 2.10.
430 let handle = match named_curve {
431 // If namedCurve is not undefined:
432 Some(curve) => {
433 // Step 2.10.1. Let publicKey be the Elliptic Curve public key identified by
434 // performing the conversion steps defined in Section 2.3.4 of [SEC1] using the
435 // subjectPublicKey field of spki.
436 // Step 2.10.2. The uncompressed point format MUST be supported.
437 // Step 2.10.3. If the implementation does not support the compressed point
438 // format and a compressed point is provided, throw a DataError.
439 // Step 2.10.4. If a decode error occurs or an identity point is found, throw a
440 // DataError.
441 let sec1_bytes = spki
442 .subject_public_key
443 .as_bytes()
444 .ok_or(Error::Data(None))?;
445 match curve {
446 NAMED_CURVE_P256 => {
447 let public_key = p256::PublicKey::from_sec1_bytes(sec1_bytes)
448 .map_err(|_| Error::Data(None))?;
449 Handle::P256PublicKey(public_key)
450 },
451 NAMED_CURVE_P384 => {
452 let public_key = p384::PublicKey::from_sec1_bytes(sec1_bytes)
453 .map_err(|_| Error::Data(None))?;
454 Handle::P384PublicKey(public_key)
455 },
456 NAMED_CURVE_P521 => {
457 let public_key = p521::PublicKey::from_sec1_bytes(sec1_bytes)
458 .map_err(|_| Error::Data(None))?;
459 Handle::P521PublicKey(public_key)
460 },
461 _ => unreachable!(),
462 }
463
464 // Step 2.10.5. Let key be a new CryptoKey that represents publicKey.
465 // NOTE: CryptoKey is created in Step 2.13 - 2.17.
466 },
467 // Otherwise:
468 None => {
469 // Step 2.10.1. Perform any key import steps defined by other applicable
470 // specifications, passing format, spki and obtaining namedCurve and key.
471 // Step 2.10.2. If an error occurred or there are no applicable specifications,
472 // throw a DataError.
473 // NOTE: We currently do not support other applicable specifications.
474 return Err(Error::Data(None));
475 },
476 };
477
478 // Step 2.11. If namedCurve is defined, and not equal to the namedCurve member of
479 // normalizedAlgorithm, throw a DataError.
480 if named_curve.is_some_and(|curve| curve != normalized_algorithm.named_curve) {
481 return Err(Error::Data(None));
482 }
483
484 // Step 2.12. If the key value is not a valid point on the Elliptic Curve identified by
485 // the namedCurve member of normalizedAlgorithm throw a DataError.
486 // NOTE: Done in Step 2.10.
487
488 // Step 2.13. Set the [[type]] internal slot of key to "public"
489 // Step 2.14. Let algorithm be a new EcKeyAlgorithm.
490 // Step 2.15. Set the name attribute of algorithm to "ECDSA".
491 // Step 2.16. Set the namedCurve attribute of algorithm to namedCurve.
492 // Step 2.17. Set the [[algorithm]] internal slot of key to algorithm.
493 let algorithm = SubtleEcKeyAlgorithm {
494 name: ALG_ECDSA.to_string(),
495 named_curve: named_curve
496 .expect("named_curve must exist here")
497 .to_string(),
498 };
499 CryptoKey::new(
500 global,
501 KeyType::Public,
502 extractable,
503 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
504 usages,
505 handle,
506 can_gc,
507 )
508 },
509 // If format is "pkcs8":
510 KeyFormat::Pkcs8 => {
511 // Step 2.1. If usages contains a value which is not "sign" then throw a SyntaxError.
512 if usages.iter().any(|usage| *usage != KeyUsage::Sign) {
513 return Err(Error::Syntax(None));
514 }
515
516 // Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
517 // algorithm over keyData.
518 // Step 2.3. If an error occurs while parsing, throw a DataError.
519 let private_key_info =
520 PrivateKeyInfo::from_der(key_data).map_err(|_| Error::Data(None))?;
521
522 // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
523 // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-ecPublicKey
524 // object identifier defined in [RFC5480], throw a DataError.
525 if private_key_info.algorithm.oid != elliptic_curve::ALGORITHM_OID {
526 return Err(Error::Data(None));
527 }
528
529 // Step 2.5. If the parameters field of the privateKeyAlgorithm
530 // PrivateKeyAlgorithmIdentifier field of privateKeyInfo is not present, then throw a
531 // DataError.
532 // Step 2.6. Let params be the parameters field of the privateKeyAlgorithm
533 // PrivateKeyAlgorithmIdentifier field of privateKeyInfo.
534 // Step 2.7. If params is not an instance of the ECParameters ASN.1 type defined in
535 // [RFC5480] that specifies a namedCurve, then throw a DataError.
536 let params: EcParameters = if let Some(params) = private_key_info.algorithm.parameters {
537 params.decode_as().map_err(|_| Error::Data(None))?
538 } else {
539 return Err(Error::Data(None));
540 };
541
542 // Step 2.8. Let namedCurve be a string whose initial value is undefined.
543 // Step 2.9.
544 // If params is equivalent to the secp256r1 object identifier defined in [RFC5480]:
545 // Set namedCurve to "P-256".
546 // If params is equivalent to the secp384r1 object identifier defined in [RFC5480]:
547 // Set namedCurve to "P-384".
548 // If params is equivalent to the secp521r1 object identifier defined in [RFC5480]:
549 // Set namedCurve to "P-521".
550 let named_curve = match params {
551 EcParameters::NamedCurve(NistP256::OID) => Some(NAMED_CURVE_P256),
552 EcParameters::NamedCurve(NistP384::OID) => Some(NAMED_CURVE_P384),
553 EcParameters::NamedCurve(NistP521::OID) => Some(NAMED_CURVE_P521),
554 _ => None,
555 };
556
557 // Step 2.10.
558 let handle = match named_curve {
559 // If namedCurve is not undefined:
560 Some(curve) => {
561 // Step 2.10.1. Let ecPrivateKey be the result of performing the parse an ASN.1
562 // structure algorithm, with data as the privateKey field of privateKeyInfo,
563 // structure as the ASN.1 ECPrivateKey structure specified in Section 3 of
564 // [RFC5915], and exactData set to true.
565 // Step 2.10.2. If an error occurred while parsing, then throw a DataError.
566 let ec_private_key = EcPrivateKey::try_from(private_key_info.private_key)
567 .map_err(|_| Error::Data(None))?;
568
569 // Step 2.10.3. If the parameters field of ecPrivateKey is present, and is not
570 // an instance of the namedCurve ASN.1 type defined in [RFC5480], or does not
571 // contain the same object identifier as the parameters field of the
572 // privateKeyAlgorithm PrivateKeyAlgorithmIdentifier field of privateKeyInfo,
573 // then throw a DataError.
574 if ec_private_key
575 .parameters
576 .is_some_and(|parameters| parameters != params)
577 {
578 return Err(Error::Data(None));
579 }
580
581 // Step 2.10.4. Let key be a new CryptoKey that represents the Elliptic Curve
582 // private key identified by performing the conversion steps defined in Section
583 // 3 of [RFC5915] using ecPrivateKey.
584 // NOTE: CryptoKey is created in Step 2.13 - 2.17.
585 match curve {
586 NAMED_CURVE_P256 => {
587 let private_key = p256::SecretKey::try_from(ec_private_key)
588 .map_err(|_| Error::Data(None))?;
589 Handle::P256PrivateKey(private_key)
590 },
591 NAMED_CURVE_P384 => {
592 let private_key = p384::SecretKey::try_from(ec_private_key)
593 .map_err(|_| Error::Data(None))?;
594 Handle::P384PrivateKey(private_key)
595 },
596 NAMED_CURVE_P521 => {
597 let private_key = p521::SecretKey::try_from(ec_private_key)
598 .map_err(|_| Error::Data(None))?;
599 Handle::P521PrivateKey(private_key)
600 },
601 _ => unreachable!(),
602 }
603 },
604 // Otherwise:
605 None => {
606 // Step 2.10.1. Perform any key import steps defined by other applicable
607 // specifications, passing format, privateKeyInfo and obtaining namedCurve and
608 // key.
609 // Step 2.10.2. If an error occurred or there are no applicable specifications,
610 // throw a DataError.
611 // NOTE: We currently do not support other applicable specifications.
612 return Err(Error::Data(None));
613 },
614 };
615
616 // Step 2.11. If namedCurve is defined, and not equal to the namedCurve member of
617 // normalizedAlgorithm, throw a DataError.
618 if named_curve.is_some_and(|curve| curve != normalized_algorithm.named_curve) {
619 return Err(Error::Data(None));
620 }
621
622 // Step 2.12. If the private key value is not a valid point on the Elliptic Curve
623 // identified by the namedCurve member of normalizedAlgorithm throw a DataError.
624 // NOTE: Done in Step 2.10.
625
626 // Step 2.13. Set the [[type]] internal slot of key to "private"
627 // Step 2.14. Let algorithm be a new EcKeyAlgorithm.
628 // Step 2.15. Set the name attribute of algorithm to "ECDSA".
629 // Step 2.16. Set the namedCurve attribute of algorithm to namedCurve.
630 // Step 2.17. Set the [[algorithm]] internal slot of key to algorithm.
631 let algorithm = SubtleEcKeyAlgorithm {
632 name: ALG_ECDSA.to_string(),
633 named_curve: named_curve
634 .expect("named_curve must exist here")
635 .to_string(),
636 };
637 CryptoKey::new(
638 global,
639 KeyType::Private,
640 extractable,
641 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
642 usages,
643 handle,
644 can_gc,
645 )
646 },
647 // If format is "jwk":
648 KeyFormat::Jwk => {
649 // Step 2.1.
650 // If keyData is a JsonWebKey dictionary:
651 // Let jwk equal keyData.
652 // Otherwise:
653 // Throw a DataError.
654 let jwk = JsonWebKey::parse(GlobalScope::get_cx(), key_data)?;
655
656 // Step 2.2. If the d field is present and usages contains a value which is not "sign",
657 // or, if the d field is not present and usages contains a value which is not "verify"
658 // then throw a SyntaxError.
659 if (jwk.d.is_some() && usages.iter().any(|usage| *usage != KeyUsage::Sign)) ||
660 (jwk.d.is_none() && usages.iter().any(|usage| *usage != KeyUsage::Verify))
661 {
662 return Err(Error::Syntax(None));
663 }
664
665 // Step 2.3. If the kty field of jwk is not "EC", then throw a DataError.
666 if jwk.kty.as_ref().is_none_or(|kty| kty != "EC") {
667 return Err(Error::Data(None));
668 }
669
670 // Step 2.4. If usages is non-empty and the use field of jwk is present and is not
671 // "sig", then throw a DataError.
672 if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "sig") {
673 return Err(Error::Data(None));
674 }
675
676 // Step 2.5. If the key_ops field of jwk is present, and is invalid according to the
677 // requirements of JSON Web Key [JWK], or it does not contain all of the specified
678 // usages values, then throw a DataError.
679 jwk.check_key_ops(&usages)?;
680
681 // Step 2.6. If the ext field of jwk is present and has the value false and extractable
682 // is true, then throw a DataError.
683 if jwk.ext.is_some_and(|ext| !ext) && extractable {
684 return Err(Error::Data(None));
685 }
686
687 // Step 2.7. Let namedCurve be a string whose value is equal to the crv field of jwk.
688 // Step 2.8. If namedCurve is not equal to the namedCurve member of
689 // normalizedAlgorithm, throw a DataError.
690 let named_curve = jwk
691 .crv
692 .filter(|crv| *crv == normalized_algorithm.named_curve)
693 .map(|crv| crv.to_string())
694 .ok_or(Error::Data(None))?;
695
696 // Step 2.9.
697 let (handle, key_type) =
698 // If namedCurve is "P-256", "P-384" or "P-521":
699 if matches!(
700 named_curve.as_str(),
701 NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
702 ) {
703 // Step 9.1. Let algNamedCurve be a string whose initial value is undefined.
704 // Step 9.2.
705 // If the alg field is not present:
706 // Let algNamedCurve be undefined.
707 // If the alg field is equal to the string "ES256":
708 // Let algNamedCurve be the string "P-256".
709 // If the alg field is equal to the string "ES384":
710 // Let algNamedCurve be the string "P-384".
711 // If the alg field is equal to the string "ES512":
712 // Let algNamedCurve be the string "P-521".
713 // otherwise:
714 // throw a DataError.
715 let alg = jwk.alg.map(|alg| alg.to_string());
716 let alg_named_curve = match alg.as_deref() {
717 None => None,
718 Some("ES256") => Some(NAMED_CURVE_P256),
719 Some("ES384") => Some(NAMED_CURVE_P384),
720 Some("ES521") => Some(NAMED_CURVE_P521),
721 _ => return Err(Error::Data(None)),
722 };
723
724 // Step 9.3. If algNamedCurve is defined, and is not equal to namedCurve, throw
725 // a DataError.
726 if alg_named_curve.is_some_and(|alg_named_curve| alg_named_curve != named_curve) {
727 return Err(Error::Data(None));
728 }
729
730 match jwk.d {
731 // If the d field is present:
732 Some(d) => {
733 // Step 2.9.1. If jwk does not meet the requirements of Section 6.2.2 of
734 // JSON Web Algorithms [JWA], then throw a DataError.
735 let x = match jwk.x {
736 Some(x) => Base64UrlUnpadded::decode_vec(&x.str())
737 .map_err(|_| Error::Data(None))?,
738 None => return Err(Error::Data(None)),
739 };
740 let y = match jwk.y {
741 Some(y) => Base64UrlUnpadded::decode_vec(&y.str())
742 .map_err(|_| Error::Data(None))?,
743 None => return Err(Error::Data(None)),
744 };
745 let d =
746 Base64UrlUnpadded::decode_vec(&d.str()).map_err(|_| Error::Data(None))?;
747
748 // Step 2.9.2. Let key be a new CryptoKey object that represents the
749 // Elliptic Curve private key identified by interpreting jwk according to
750 // Section 6.2.2 of JSON Web Algorithms [JWA].
751 // NOTE: CryptoKey is created in Step 2.12 - 2.15.
752 let handle = match named_curve.as_str() {
753 NAMED_CURVE_P256 => {
754 let private_key =
755 p256::SecretKey::from_slice(&d).map_err(|_| Error::Data(None))?;
756 let mut sec1_bytes = vec![4u8];
757 sec1_bytes.extend_from_slice(&x);
758 sec1_bytes.extend_from_slice(&y);
759 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
760 .map_err(|_| Error::Data(None))?;
761 NistP256::validate_public_key(&private_key, &encoded_point)
762 .map_err(|_| Error::Data(None))?;
763 Handle::P256PrivateKey(private_key)
764 },
765 NAMED_CURVE_P384 => {
766 let private_key =
767 p384::SecretKey::from_slice(&d).map_err(|_| Error::Data(None))?;
768 let mut sec1_bytes = vec![4u8];
769 sec1_bytes.extend_from_slice(&x);
770 sec1_bytes.extend_from_slice(&y);
771 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
772 .map_err(|_| Error::Data(None))?;
773 NistP384::validate_public_key(&private_key, &encoded_point)
774 .map_err(|_| Error::Data(None))?;
775 Handle::P384PrivateKey(private_key)
776 },
777 NAMED_CURVE_P521 => {
778 let private_key =
779 p521::SecretKey::from_slice(&d).map_err(|_| Error::Data(None))?;
780 let mut sec1_bytes = vec![4u8];
781 sec1_bytes.extend_from_slice(&x);
782 sec1_bytes.extend_from_slice(&y);
783 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
784 .map_err(|_| Error::Data(None))?;
785 NistP521::validate_public_key(&private_key, &encoded_point)
786 .map_err(|_| Error::Data(None))?;
787 Handle::P521PrivateKey(private_key)
788 },
789 _ => unreachable!(),
790 };
791
792 // Step 2.9.3. Set the [[type]] internal slot of Key to "private".
793 let key_type = KeyType::Private;
794
795 (handle, key_type)
796 },
797 // Otherwise:
798 None => {
799 // Step 2.9.1. If jwk does not meet the requirements of Section 6.2.1 of
800 // JSON Web Algorithms [JWA], then throw a DataError.
801 let x = match jwk.x {
802 Some(x) => Base64UrlUnpadded::decode_vec(&x.str())
803 .map_err(|_| Error::Data(None))?,
804 None => return Err(Error::Data(None)),
805 };
806 let y = match jwk.y {
807 Some(y) => Base64UrlUnpadded::decode_vec(&y.str())
808 .map_err(|_| Error::Data(None))?,
809 None => return Err(Error::Data(None)),
810 };
811
812 // Step 2.9.2. Let key be a new CryptoKey object that represents the
813 // Elliptic Curve public key identified by interpreting jwk according to
814 // Section 6.2.1 of JSON Web Algorithms [JWA].
815 // NOTE: CryptoKey is created in Step 2.12 - 2.15.
816 let handle = match named_curve.as_str() {
817 NAMED_CURVE_P256 => {
818 let mut sec1_bytes = vec![4u8];
819 sec1_bytes.extend_from_slice(&x);
820 sec1_bytes.extend_from_slice(&y);
821 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
822 .map_err(|_| Error::Data(None))?;
823 let public_key =
824 p256::PublicKey::from_encoded_point(&encoded_point)
825 .into_option()
826 .ok_or(Error::Data(None))?;
827 Handle::P256PublicKey(public_key)
828 },
829 NAMED_CURVE_P384 => {
830 let mut sec1_bytes = vec![4u8];
831 sec1_bytes.extend_from_slice(&x);
832 sec1_bytes.extend_from_slice(&y);
833 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
834 .map_err(|_| Error::Data(None))?;
835 let public_key =
836 p384::PublicKey::from_encoded_point(&encoded_point)
837 .into_option()
838 .ok_or(Error::Data(None))?;
839 Handle::P384PublicKey(public_key)
840 },
841 NAMED_CURVE_P521 => {
842 let mut sec1_bytes = vec![4u8];
843 sec1_bytes.extend_from_slice(&x);
844 sec1_bytes.extend_from_slice(&y);
845 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
846 .map_err(|_| Error::Data(None))?;
847 let public_key =
848 p521::PublicKey::from_encoded_point(&encoded_point)
849 .into_option()
850 .ok_or(Error::Data(None))?;
851 Handle::P521PublicKey(public_key)
852 },
853 _ => unreachable!(),
854 };
855
856 // Step 2.9.3. Set the [[type]] internal slot of Key to "public".
857 let key_type = KeyType::Public;
858
859 (handle, key_type)
860 },
861 }
862 }
863 // Otherwise
864 else {
865 // Step 2.9.1. Perform any key import steps defined by other applicable
866 // specifications, passing format, jwk and obtaining key.
867 // Step 2.9.2. If an error occurred or there are no applicable specifications,
868 // throw a DataError.
869 // NOTE: We currently do not support other applicable specifications.
870 return Err(Error::Data(None));
871 };
872
873 // Step 2.10. If the key value is not a valid point on the Elliptic Curve identified by
874 // the namedCurve member of normalizedAlgorithm throw a DataError.
875 // NOTE: Done in Step 2.9.
876
877 // Step 2.11. Let algorithm be a new instance of an EcKeyAlgorithm object.
878 // Step 2.12. Set the name attribute of algorithm to "ECDSA".
879 // Step 2.13. Set the namedCurve attribute of algorithm to namedCurve.
880 // Step 2.14. Set the [[algorithm]] internal slot of key to algorithm.
881 let algorithm = SubtleEcKeyAlgorithm {
882 name: ALG_ECDSA.to_string(),
883 named_curve,
884 };
885 CryptoKey::new(
886 global,
887 key_type,
888 extractable,
889 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
890 usages,
891 handle,
892 can_gc,
893 )
894 },
895 // If format is "raw":
896 KeyFormat::Raw | KeyFormat::Raw_public => {
897 // Step 2.1. If the namedCurve member of normalizedAlgorithm is not a named curve, then
898 // throw a DataError.
899 if !SUPPORTED_CURVES
900 .iter()
901 .any(|&supported_curve| supported_curve == normalized_algorithm.named_curve)
902 {
903 return Err(Error::Data(None));
904 }
905
906 // Step 2.2. If usages contains a value which is not "verify" then throw a SyntaxError.
907 if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
908 return Err(Error::Syntax(None));
909 }
910
911 // Step 2.3.
912 // If namedCurve is "P-256", "P-384" or "P-521":
913 let handle = if matches!(
914 normalized_algorithm.named_curve.as_str(),
915 NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
916 ) {
917 // Step 2.3.1. Let Q be the elliptic curve point on the curve identified by the
918 // namedCurve member of normalizedAlgorithm identified by performing the conversion
919 // steps defined in Section 2.3.4 of [SEC1] on keyData.
920 // Step 2.3.2. The uncompressed point format MUST be supported.
921 // Step 2.3.3. If the implementation does not support the compressed point format
922 // and a compressed point is provided, throw a DataError.
923 // Step 2.3.4. If a decode error occurs or an identity point is found, throw a
924 // DataError.
925 match normalized_algorithm.named_curve.as_str() {
926 NAMED_CURVE_P256 => {
927 let q = p256::PublicKey::from_sec1_bytes(key_data)
928 .map_err(|_| Error::Data(None))?;
929 Handle::P256PublicKey(q)
930 },
931 NAMED_CURVE_P384 => {
932 let q = p384::PublicKey::from_sec1_bytes(key_data)
933 .map_err(|_| Error::Data(None))?;
934 Handle::P384PublicKey(q)
935 },
936 NAMED_CURVE_P521 => {
937 let q = p521::PublicKey::from_sec1_bytes(key_data)
938 .map_err(|_| Error::Data(None))?;
939 Handle::P521PublicKey(q)
940 },
941 _ => unreachable!(),
942 }
943
944 // Step 2.3.5. Let key be a new CryptoKey that represents Q.
945 // NOTE: CryptoKey is created in Step 2.7 - 2.8.
946 }
947 // Otherwise:
948 else {
949 // Step. 2.3.1. Perform any key import steps defined by other applicable
950 // specifications, passing format, keyData and obtaining key.
951 // Step. 2.3.2. If an error occurred or there are no applicable specifications,
952 // throw a DataError.
953 // NOTE: We currently do not support other applicable specifications.
954 return Err(Error::Data(None));
955 };
956
957 // Step 2.4. Let algorithm be a new EcKeyAlgorithm object.
958 // Step 2.5. Set the name attribute of algorithm to "ECDSA".
959 // Step 2.6. Set the namedCurve attribute of algorithm to equal the namedCurve member
960 // of normalizedAlgorithm.
961 let algorithm = SubtleEcKeyAlgorithm {
962 name: ALG_ECDSA.to_string(),
963 named_curve: normalized_algorithm.named_curve.clone(),
964 };
965
966 // Step 2.7. Set the [[type]] internal slot of key to "public"
967 // Step 2.8. Set the [[algorithm]] internal slot of key to algorithm.
968 CryptoKey::new(
969 global,
970 KeyType::Public,
971 extractable,
972 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
973 usages,
974 handle,
975 can_gc,
976 )
977 },
978 // Otherwise:
979 _ => {
980 // throw a NotSupportedError.
981 return Err(Error::NotSupported(None));
982 },
983 };
984
985 // Step 3. Return key.
986 Ok(key)
987}
988
989/// <https://w3c.github.io/webcrypto/#ecdsa-operations-export-key>
990pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
991 // Step 1. Let key be the CryptoKey to be exported.
992
993 // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
994 // slot of key cannot be accessed, then throw an OperationError.
995 // NOTE: Done in Step 3.
996
997 // Step 3.
998 let result = match format {
999 // If format is "spki":
1000 KeyFormat::Spki => {
1001 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
1002 // InvalidAccessError.
1003 if key.Type() != KeyType::Public {
1004 return Err(Error::InvalidAccess(None));
1005 }
1006
1007 // Step 3.2.
1008 // Let data be an instance of the SubjectPublicKeyInfo ASN.1 structure defined in
1009 // [RFC5280] with the following properties:
1010 // * Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the
1011 // following properties:
1012 // * Set the algorithm field to the OID id-ecPublicKey defined in [RFC5480].
1013 // * Set the parameters field to an instance of the ECParameters ASN.1 type
1014 // defined in [RFC5480] as follows:
1015 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
1016 // "P-256", "P-384" or "P-521":
1017 // Let keyData be the byte sequence that represents the Elliptic Curve
1018 // public key represented by the [[handle]] internal slot of key
1019 // according to the encoding rules specified in Section 2.2 of
1020 // [RFC5480] and using the uncompressed form. and keyData.
1021 // If the namedCurve attribute of the [[algorithm]] internal slot
1022 // of key is "P-256":
1023 // Set parameters to the namedCurve choice with value equal to
1024 // the object identifier secp256r1 defined in [RFC5480]
1025 // If the namedCurve attribute of the [[algorithm]] internal slot
1026 // of key is "P-384":
1027 // Set parameters to the namedCurve choice with value equal to
1028 // the object identifier secp384r1 defined in [RFC5480]
1029 // If the namedCurve attribute of the [[algorithm]] internal slot
1030 // of key is "P-521":
1031 // Set parameters to the namedCurve choice with value equal to
1032 // the object identifier secp521r1 defined in [RFC5480]
1033 // Otherwise:
1034 // 1. Perform any key export steps defined by other applicable
1035 // specifications, passing format and the namedCurve attribute of
1036 // the [[algorithm]] internal slot of key and obtaining
1037 // namedCurveOid and keyData.
1038 // 2. Set parameters to the namedCurve choice with value equal to the
1039 // object identifier namedCurveOid.
1040 // * Set the subjectPublicKey field to keyData.
1041 // NOTE: We currently do not support other applicable specifications.
1042 let data = match key.handle() {
1043 Handle::P256PublicKey(public_key) => public_key.to_public_key_der(),
1044 Handle::P384PublicKey(public_key) => public_key.to_public_key_der(),
1045 Handle::P521PublicKey(public_key) => public_key.to_public_key_der(),
1046 _ => return Err(Error::Operation(None)),
1047 }
1048 .map_err(|_| Error::Operation(None))?;
1049
1050 // Step 3.3. Let result be the result of DER-encoding data.
1051 ExportedKey::Bytes(data.to_vec())
1052 },
1053 // If format is "pkcs8":
1054 KeyFormat::Pkcs8 => {
1055 // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
1056 // InvalidAccessError.
1057 if key.Type() != KeyType::Private {
1058 return Err(Error::InvalidAccess(None));
1059 }
1060
1061 // Step 3.2.
1062 // Let data be an instance of the PrivateKeyInfo ASN.1 structure defined in [RFC5208]
1063 // with the following properties:
1064 // * Set the version field to 0.
1065 // * Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1
1066 // type with the following properties:
1067 // * Set the algorithm field to the OID id-ecPublicKey defined in [RFC5480].
1068 // * Set the parameters field to an instance of the ECParameters ASN.1 type
1069 // defined in [RFC5480] as follows:
1070 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
1071 // "P-256", "P-384" or "P-521":
1072 // Let keyData be the result of DER-encoding an instance of the
1073 // ECPrivateKey structure defined in Section 3 of [RFC5915] for the
1074 // Elliptic Curve private key represented by the [[handle]] internal
1075 // slot of key and that conforms to the following:
1076 // * The parameters field is present, and is equivalent to the
1077 // parameters field of the privateKeyAlgorithm field of this
1078 // PrivateKeyInfo ASN.1 structure.
1079 // * The publicKey field is present and represents the Elliptic
1080 // Curve public key associated with the Elliptic Curve private key
1081 // represented by the [[handle]] internal slot of key.
1082 // * If the namedCurve attribute of the [[algorithm]] internal slot
1083 // of key is "P-256":
1084 // Set parameters to the namedCurve choice with value equal to
1085 // the object identifier secp256r1 defined in [RFC5480]
1086 // * If the namedCurve attribute of the [[algorithm]] internal slot
1087 // of key is "P-384":
1088 // Set parameters to the namedCurve choice with value equal to
1089 // the object identifier secp384r1 defined in [RFC5480]
1090 // * If the namedCurve attribute of the [[algorithm]] internal slot
1091 // of key is "P-521":
1092 // Set parameters to the namedCurve choice with value equal to
1093 // the object identifier secp521r1 defined in [RFC5480]
1094 // Otherwise:
1095 // 1. Perform any key export steps defined by other applicable
1096 // specifications, passing format and the namedCurve attribute of
1097 // the [[algorithm]] internal slot of key and obtaining
1098 // namedCurveOid and keyData.
1099 // 2. Set parameters to the namedCurve choice with value equal to the
1100 // object identifier namedCurveOid.
1101 // * Set the privateKey field to keyData.
1102 // NOTE: We currently do not support other applicable specifications.
1103 let data = match key.handle() {
1104 Handle::P256PrivateKey(private_key) => private_key.to_pkcs8_der(),
1105 Handle::P384PrivateKey(private_key) => private_key.to_pkcs8_der(),
1106 Handle::P521PrivateKey(private_key) => private_key.to_pkcs8_der(),
1107 _ => return Err(Error::Operation(None)),
1108 }
1109 .map_err(|_| Error::Operation(None))?;
1110
1111 // Step 3.3. Let result be the result of DER-encoding data.
1112 ExportedKey::Bytes(data.as_bytes().to_vec())
1113 },
1114 // If format is "jwk":
1115 KeyFormat::Jwk => {
1116 // Step 3.1. Let jwk be a new JsonWebKey dictionary.
1117 // Step 3.2. Set the kty attribute of jwk to "EC".
1118 let mut jwk = JsonWebKey {
1119 kty: Some(DOMString::from("EC")),
1120 ..Default::default()
1121 };
1122
1123 // Step 3.3.
1124 let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() else {
1125 return Err(Error::Operation(None));
1126 };
1127 // If the namedCurve attribute of the [[algorithm]] internal slot of key is "P-256",
1128 // "P-384" or "P-521":
1129 if matches!(
1130 algorithm.named_curve.as_str(),
1131 NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
1132 ) {
1133 // Step 3.3.1.
1134 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
1135 // "P-256":
1136 // Set the crv attribute of jwk to "P-256"
1137 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
1138 // "P-384":
1139 // Set the crv attribute of jwk to "P-384"
1140 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
1141 // "P-521":
1142 // Set the crv attribute of jwk to "P-521"
1143 jwk.crv = Some(DOMString::from(algorithm.named_curve.as_str()));
1144
1145 // Step 3.3.2. Set the x attribute of jwk according to the definition in Section
1146 // 6.2.1.2 of JSON Web Algorithms [JWA].
1147 // Step 3.3.3. Set the y attribute of jwk according to the definition in Section
1148 // 6.2.1.3 of JSON Web Algorithms [JWA].
1149 let (x, y) = match key.handle() {
1150 Handle::P256PublicKey(public_key) => {
1151 let encoded_point = public_key.to_encoded_point(false);
1152 (
1153 encoded_point.x().ok_or(Error::Operation(None))?.to_vec(),
1154 encoded_point.y().ok_or(Error::Operation(None))?.to_vec(),
1155 )
1156 },
1157 Handle::P384PublicKey(public_key) => {
1158 let encoded_point = public_key.to_encoded_point(false);
1159 (
1160 encoded_point.x().ok_or(Error::Operation(None))?.to_vec(),
1161 encoded_point.y().ok_or(Error::Operation(None))?.to_vec(),
1162 )
1163 },
1164 Handle::P521PublicKey(public_key) => {
1165 let encoded_point = public_key.to_encoded_point(false);
1166 (
1167 encoded_point.x().ok_or(Error::Operation(None))?.to_vec(),
1168 encoded_point.y().ok_or(Error::Operation(None))?.to_vec(),
1169 )
1170 },
1171 Handle::P256PrivateKey(private_key) => {
1172 let public_key = private_key.public_key();
1173 let encoded_point = public_key.to_encoded_point(false);
1174 (
1175 encoded_point.x().ok_or(Error::Operation(None))?.to_vec(),
1176 encoded_point.y().ok_or(Error::Operation(None))?.to_vec(),
1177 )
1178 },
1179 Handle::P384PrivateKey(private_key) => {
1180 let public_key = private_key.public_key();
1181 let encoded_point = public_key.to_encoded_point(false);
1182 (
1183 encoded_point.x().ok_or(Error::Operation(None))?.to_vec(),
1184 encoded_point.y().ok_or(Error::Operation(None))?.to_vec(),
1185 )
1186 },
1187 Handle::P521PrivateKey(private_key) => {
1188 let public_key = private_key.public_key();
1189 let encoded_point = public_key.to_encoded_point(false);
1190 (
1191 encoded_point.x().ok_or(Error::Operation(None))?.to_vec(),
1192 encoded_point.y().ok_or(Error::Operation(None))?.to_vec(),
1193 )
1194 },
1195 _ => return Err(Error::Operation(None)),
1196 };
1197 jwk.x = Some(Base64UrlUnpadded::encode_string(&x).into());
1198 jwk.y = Some(Base64UrlUnpadded::encode_string(&y).into());
1199
1200 // Step 3.3.4.
1201 // If the [[type]] internal slot of key is "private"
1202 // Set the d attribute of jwk according to the definition in Section 6.2.2.1 of
1203 // JSON Web Algorithms [JWA].
1204 if key.Type() == KeyType::Private {
1205 let d = match key.handle() {
1206 Handle::P256PrivateKey(private_key) => private_key.to_bytes().to_vec(),
1207 Handle::P384PrivateKey(private_key) => private_key.to_bytes().to_vec(),
1208 Handle::P521PrivateKey(private_key) => private_key.to_bytes().to_vec(),
1209 _ => return Err(Error::NotSupported(None)),
1210 };
1211 jwk.d = Some(Base64UrlUnpadded::encode_string(&d).into());
1212 }
1213 }
1214 // Otherwise:
1215 else {
1216 // Step 3.3.1. Perform any key export steps defined by other applicable
1217 // specifications, passing format and the namedCurve attribute of the [[algorithm]]
1218 // internal slot of key and obtaining namedCurve and a new value of jwk.
1219 // Step 3.3.2. Set the crv attribute of jwk to namedCurve.
1220 // NOTE: We currently do not support other applicable specifications.
1221 unreachable!()
1222 }
1223
1224 // Step 3.4. Set the key_ops attribute of jwk to the usages attribute of key.
1225 jwk.key_ops = Some(
1226 key.usages()
1227 .iter()
1228 .map(|usage| DOMString::from(usage.as_str()))
1229 .collect::<Vec<DOMString>>(),
1230 );
1231
1232 // Step 3.4. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
1233 jwk.ext = Some(key.Extractable());
1234
1235 // Step 3.4. Let result be jwk.
1236 ExportedKey::Jwk(Box::new(jwk))
1237 },
1238 // If format is "raw":
1239 KeyFormat::Raw | KeyFormat::Raw_public => {
1240 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
1241 // InvalidAccessError.
1242 if key.Type() != KeyType::Public {
1243 return Err(Error::InvalidAccess(None));
1244 }
1245
1246 // Step 3.2.
1247 // If the namedCurve attribute of the [[algorithm]] internal slot of key is "P-256",
1248 // "P-384" or "P-521":
1249 // Let data be a byte sequence representing the Elliptic Curve point Q represented
1250 // by the [[handle]] internal slot of key according to [SEC1] 2.3.3 using the
1251 // uncompressed format.
1252 // Otherwise:
1253 // Perform any key export steps defined by other applicable specifications, passing
1254 // format and the namedCurve attribute of the [[algorithm]] internal slot of key
1255 // and obtaining namedCurve and data.
1256 // NOTE: We currently do not support other applicable specifications.
1257 let named_curve =
1258 if let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() {
1259 algorithm.named_curve.as_str()
1260 } else {
1261 return Err(Error::Operation(None));
1262 };
1263 let data = if matches!(
1264 named_curve,
1265 NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
1266 ) {
1267 match key.handle() {
1268 Handle::P256PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
1269 Handle::P384PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
1270 Handle::P521PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
1271 _ => return Err(Error::Operation(None)),
1272 }
1273 } else {
1274 return Err(Error::NotSupported(None));
1275 };
1276
1277 // Step 3.3. Let result be data.
1278 ExportedKey::Bytes(data)
1279 },
1280 // Otherwise:
1281 _ => {
1282 // throw a NotSupportedError.
1283 return Err(Error::NotSupported(None));
1284 },
1285 };
1286
1287 // Step 4. Return result.
1288 Ok(result)
1289}
1290
1291/// A helper function that expand a prehash to a specified length if the prehash is shorter than
1292/// the specified length.
1293///
1294/// If the length of `prehash` is less than `length`, return a prehash with length `length`
1295/// constructed by left-padding `prehash` with zeros. Otherwire, return the unmodified `prehash`.
1296fn expand_prehash(prehash: Vec<u8>, length: usize) -> Vec<u8> {
1297 if prehash.len() < length {
1298 let mut left_padded_prehash = vec![0u8; length];
1299 left_padded_prehash[length - prehash.len()..].copy_from_slice(&prehash);
1300 left_padded_prehash
1301 } else {
1302 prehash
1303 }
1304}