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 elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint, ValidatePublicKey};
7use p256::NistP256;
8use p384::NistP384;
9use p521::NistP521;
10use pkcs8::der::Decode;
11use pkcs8::spki::EncodePublicKey;
12use pkcs8::{AssociatedOid, EncodePrivateKey, PrivateKeyInfo, SubjectPublicKeyInfo};
13use sec1::der::asn1::BitString;
14use sec1::{EcParameters, EcPrivateKey, EncodedPoint};
15
16use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
17 CryptoKeyMethods, KeyType, KeyUsage,
18};
19use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{JsonWebKey, KeyFormat};
20use crate::dom::bindings::error::Error;
21use crate::dom::bindings::root::DomRoot;
22use crate::dom::bindings::str::DOMString;
23use crate::dom::cryptokey::{CryptoKey, Handle};
24use crate::dom::globalscope::GlobalScope;
25use crate::dom::subtlecrypto::{
26 ALG_ECDSA, ExportedKey, JsonWebKeyExt, KeyAlgorithmAndDerivatives, NAMED_CURVE_P256,
27 NAMED_CURVE_P384, NAMED_CURVE_P521, SUPPORTED_CURVES, SubtleEcKeyAlgorithm,
28 SubtleEcKeyImportParams,
29};
30use crate::script_runtime::CanGc;
31
32/// <https://w3c.github.io/webcrypto/#ecdsa-operations-import-key>
33pub(crate) fn import_key(
34 global: &GlobalScope,
35 normalized_algorithm: &SubtleEcKeyImportParams,
36 format: KeyFormat,
37 key_data: &[u8],
38 extractable: bool,
39 usages: Vec<KeyUsage>,
40 can_gc: CanGc,
41) -> Result<DomRoot<CryptoKey>, Error> {
42 // Step 1. Let keyData be the key data to be imported.
43
44 // Step 2.
45 let key = match format {
46 // If format is "spki":
47 KeyFormat::Spki => {
48 // Step 2.1. If usages contains a value which is not "verify" then throw a SyntaxError.
49 if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
50 return Err(Error::Syntax(None));
51 }
52
53 // Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
54 // algorithm over keyData
55 // Step 2.3. If an error occurred while parsing, then throw a DataError.
56 let spki = SubjectPublicKeyInfo::<_, BitString>::from_der(key_data)
57 .map_err(|_| Error::Data)?;
58
59 // Step 2.4. If the algorithm object identifier field of the algorithm
60 // AlgorithmIdentifier field of spki is not equal to the id-ecPublicKey object
61 // identifier defined in [RFC5480], then throw a DataError.
62 if spki.algorithm.oid != elliptic_curve::ALGORITHM_OID {
63 return Err(Error::Data);
64 }
65
66 // Step 2.5. If the parameters field of the algorithm AlgorithmIdentifier field of spki
67 // is absent, then throw a DataError.
68 // Step 2.6. Let params be the parameters field of the algorithm AlgorithmIdentifier
69 // field of spki.
70 // Step 2.7. If params is not an instance of the ECParameters ASN.1 type defined in
71 // [RFC5480] that specifies a namedCurve, then throw a DataError.
72 let Some(params): Option<EcParameters> = spki.algorithm.parameters else {
73 return Err(Error::Data);
74 };
75
76 // Step 2.8. Let namedCurve be a string whose initial value is undefined.
77 // Step 2.9.
78 // If params is equivalent to the secp256r1 object identifier defined in [RFC5480]:
79 // Set namedCurve "P-256".
80 // If params is equivalent to the secp384r1 object identifier defined in [RFC5480]:
81 // Set namedCurve "P-384".
82 // If params is equivalent to the secp521r1 object identifier defined in [RFC5480]:
83 // Set namedCurve "P-521".
84 let named_curve = match params {
85 EcParameters::NamedCurve(NistP256::OID) => Some(NAMED_CURVE_P256),
86 EcParameters::NamedCurve(NistP384::OID) => Some(NAMED_CURVE_P384),
87 EcParameters::NamedCurve(NistP521::OID) => Some(NAMED_CURVE_P521),
88 _ => None,
89 };
90
91 // Step 2.10.
92 let handle = match named_curve {
93 // If namedCurve is not undefined:
94 Some(curve) => {
95 // Step 2.10.1. Let publicKey be the Elliptic Curve public key identified by
96 // performing the conversion steps defined in Section 2.3.4 of [SEC1] using the
97 // subjectPublicKey field of spki.
98 // Step 2.10.2. The uncompressed point format MUST be supported.
99 // Step 2.10.3. If the implementation does not support the compressed point
100 // format and a compressed point is provided, throw a DataError.
101 // Step 2.10.4. If a decode error occurs or an identity point is found, throw a
102 // DataError.
103 let sec1_bytes = spki.subject_public_key.as_bytes().ok_or(Error::Data)?;
104 match curve {
105 NAMED_CURVE_P256 => {
106 let public_key = p256::PublicKey::from_sec1_bytes(sec1_bytes)
107 .map_err(|_| Error::Data)?;
108 Handle::P256PublicKey(public_key)
109 },
110 NAMED_CURVE_P384 => {
111 let public_key = p384::PublicKey::from_sec1_bytes(sec1_bytes)
112 .map_err(|_| Error::Data)?;
113 Handle::P384PublicKey(public_key)
114 },
115 NAMED_CURVE_P521 => {
116 let public_key = p521::PublicKey::from_sec1_bytes(sec1_bytes)
117 .map_err(|_| Error::Data)?;
118 Handle::P521PublicKey(public_key)
119 },
120 _ => unreachable!(),
121 }
122
123 // Step 2.10.5. Let key be a new CryptoKey that represents publicKey.
124 // NOTE: CryptoKey is created in Step 2.13 - 2.17.
125 },
126 // Otherwise:
127 None => {
128 // Step 2.10.1. Perform any key import steps defined by other applicable
129 // specifications, passing format, spki and obtaining namedCurve and key.
130 // Step 2.10.2. If an error occurred or there are no applicable specifications,
131 // throw a DataError.
132 // NOTE: We currently do not support other applicable specifications.
133 return Err(Error::Data);
134 },
135 };
136
137 // Step 2.11. If namedCurve is defined, and not equal to the namedCurve member of
138 // normalizedAlgorithm, throw a DataError.
139 if named_curve.is_some_and(|curve| curve != normalized_algorithm.named_curve) {
140 return Err(Error::Data);
141 }
142
143 // Step 2.12. If the key value is not a valid point on the Elliptic Curve identified by
144 // the namedCurve member of normalizedAlgorithm throw a DataError.
145 // NOTE: Done in Step 2.10.
146
147 // Step 2.13. Set the [[type]] internal slot of key to "public"
148 // Step 2.14. Let algorithm be a new EcKeyAlgorithm.
149 // Step 2.15. Set the name attribute of algorithm to "ECDSA".
150 // Step 2.16. Set the namedCurve attribute of algorithm to namedCurve.
151 // Step 2.17. Set the [[algorithm]] internal slot of key to algorithm.
152 let algorithm = SubtleEcKeyAlgorithm {
153 name: ALG_ECDSA.to_string(),
154 named_curve: named_curve
155 .expect("named_curve must exist here")
156 .to_string(),
157 };
158 CryptoKey::new(
159 global,
160 KeyType::Public,
161 extractable,
162 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
163 usages,
164 handle,
165 can_gc,
166 )
167 },
168 // If format is "pkcs8":
169 KeyFormat::Pkcs8 => {
170 // Step 2.1. If usages contains a value which is not "sign" then throw a SyntaxError.
171 if usages.iter().any(|usage| *usage != KeyUsage::Sign) {
172 return Err(Error::Syntax(None));
173 }
174
175 // Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
176 // algorithm over keyData.
177 // Step 2.3. If an error occurs while parsing, throw a DataError.
178 let private_key_info = PrivateKeyInfo::from_der(key_data).map_err(|_| Error::Data)?;
179
180 // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
181 // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-ecPublicKey
182 // object identifier defined in [RFC5480], throw a DataError.
183 if private_key_info.algorithm.oid != elliptic_curve::ALGORITHM_OID {
184 return Err(Error::Data);
185 }
186
187 // Step 2.5. If the parameters field of the privateKeyAlgorithm
188 // PrivateKeyAlgorithmIdentifier field of privateKeyInfo is not present, then throw a
189 // DataError.
190 // Step 2.6. Let params be the parameters field of the privateKeyAlgorithm
191 // PrivateKeyAlgorithmIdentifier field of privateKeyInfo.
192 // Step 2.7. If params is not an instance of the ECParameters ASN.1 type defined in
193 // [RFC5480] that specifies a namedCurve, then throw a DataError.
194 let params: EcParameters = if let Some(params) = private_key_info.algorithm.parameters {
195 params.decode_as().map_err(|_| Error::Data)?
196 } else {
197 return Err(Error::Data);
198 };
199
200 // Step 2.8. Let namedCurve be a string whose initial value is undefined.
201 // Step 2.9.
202 // If params is equivalent to the secp256r1 object identifier defined in [RFC5480]:
203 // Set namedCurve to "P-256".
204 // If params is equivalent to the secp384r1 object identifier defined in [RFC5480]:
205 // Set namedCurve to "P-384".
206 // If params is equivalent to the secp521r1 object identifier defined in [RFC5480]:
207 // Set namedCurve to "P-521".
208 let named_curve = match params {
209 EcParameters::NamedCurve(NistP256::OID) => Some(NAMED_CURVE_P256),
210 EcParameters::NamedCurve(NistP384::OID) => Some(NAMED_CURVE_P384),
211 EcParameters::NamedCurve(NistP521::OID) => Some(NAMED_CURVE_P521),
212 _ => None,
213 };
214
215 // Step 2.10.
216 let handle = match named_curve {
217 // If namedCurve is not undefined:
218 Some(curve) => {
219 // Step 2.10.1. Let ecPrivateKey be the result of performing the parse an ASN.1
220 // structure algorithm, with data as the privateKey field of privateKeyInfo,
221 // structure as the ASN.1 ECPrivateKey structure specified in Section 3 of
222 // [RFC5915], and exactData set to true.
223 // Step 2.10.2. If an error occurred while parsing, then throw a DataError.
224 let ec_private_key = EcPrivateKey::try_from(private_key_info.private_key)
225 .map_err(|_| Error::Data)?;
226
227 // Step 2.10.3. If the parameters field of ecPrivateKey is present, and is not
228 // an instance of the namedCurve ASN.1 type defined in [RFC5480], or does not
229 // contain the same object identifier as the parameters field of the
230 // privateKeyAlgorithm PrivateKeyAlgorithmIdentifier field of privateKeyInfo,
231 // then throw a DataError.
232 if ec_private_key
233 .parameters
234 .is_some_and(|parameters| parameters != params)
235 {
236 return Err(Error::Data);
237 }
238
239 // Step 2.10.4. Let key be a new CryptoKey that represents the Elliptic Curve
240 // private key identified by performing the conversion steps defined in Section
241 // 3 of [RFC5915] using ecPrivateKey.
242 // NOTE: CryptoKey is created in Step 2.13 - 2.17.
243 match curve {
244 NAMED_CURVE_P256 => {
245 let private_key = p256::SecretKey::try_from(ec_private_key)
246 .map_err(|_| Error::Data)?;
247 Handle::P256PrivateKey(private_key)
248 },
249 NAMED_CURVE_P384 => {
250 let private_key = p384::SecretKey::try_from(ec_private_key)
251 .map_err(|_| Error::Data)?;
252 Handle::P384PrivateKey(private_key)
253 },
254 NAMED_CURVE_P521 => {
255 let private_key = p521::SecretKey::try_from(ec_private_key)
256 .map_err(|_| Error::Data)?;
257 Handle::P521PrivateKey(private_key)
258 },
259 _ => unreachable!(),
260 }
261 },
262 // Otherwise:
263 None => {
264 // Step 2.10.1. Perform any key import steps defined by other applicable
265 // specifications, passing format, privateKeyInfo and obtaining namedCurve and
266 // key.
267 // Step 2.10.2. If an error occurred or there are no applicable specifications,
268 // throw a DataError.
269 // NOTE: We currently do not support other applicable specifications.
270 return Err(Error::Data);
271 },
272 };
273
274 // Step 2.11. If namedCurve is defined, and not equal to the namedCurve member of
275 // normalizedAlgorithm, throw a DataError.
276 if named_curve.is_some_and(|curve| curve != normalized_algorithm.named_curve) {
277 return Err(Error::Data);
278 }
279
280 // Step 2.12. If the private key value is not a valid point on the Elliptic Curve
281 // identified by the namedCurve member of normalizedAlgorithm throw a DataError.
282 // NOTE: Done in Step 2.10.
283
284 // Step 2.13. Set the [[type]] internal slot of key to "private"
285 // Step 2.14. Let algorithm be a new EcKeyAlgorithm.
286 // Step 2.15. Set the name attribute of algorithm to "ECDSA".
287 // Step 2.16. Set the namedCurve attribute of algorithm to namedCurve.
288 // Step 2.17. Set the [[algorithm]] internal slot of key to algorithm.
289 let algorithm = SubtleEcKeyAlgorithm {
290 name: ALG_ECDSA.to_string(),
291 named_curve: named_curve
292 .expect("named_curve must exist here")
293 .to_string(),
294 };
295 CryptoKey::new(
296 global,
297 KeyType::Private,
298 extractable,
299 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
300 usages,
301 handle,
302 can_gc,
303 )
304 },
305 // If format is "jwk":
306 KeyFormat::Jwk => {
307 // Step 2.1.
308 // If keyData is a JsonWebKey dictionary:
309 // Let jwk equal keyData.
310 // Otherwise:
311 // Throw a DataError.
312 let jwk = JsonWebKey::parse(GlobalScope::get_cx(), key_data)?;
313
314 // Step 2.2. If the d field is present and usages contains a value which is not "sign",
315 // or, if the d field is not present and usages contains a value which is not "verify"
316 // then throw a SyntaxError.
317 if (jwk.d.is_some() && usages.iter().any(|usage| *usage != KeyUsage::Sign)) ||
318 (jwk.d.is_none() && usages.iter().any(|usage| *usage != KeyUsage::Verify))
319 {
320 return Err(Error::Syntax(None));
321 }
322
323 // Step 2.3. If the kty field of jwk is not "EC", then throw a DataError.
324 if jwk.kty.as_ref().is_none_or(|kty| kty != "EC") {
325 return Err(Error::Data);
326 }
327
328 // Step 2.4. If usages is non-empty and the use field of jwk is present and is not
329 // "sig", then throw a DataError.
330 if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "sig") {
331 return Err(Error::Data);
332 }
333
334 // Step 2.5. If the key_ops field of jwk is present, and is invalid according to the
335 // requirements of JSON Web Key [JWK], or it does not contain all of the specified
336 // usages values, then throw a DataError.
337 jwk.check_key_ops(&usages)?;
338
339 // Step 2.6. If the ext field of jwk is present and has the value false and extractable
340 // is true, then throw a DataError.
341 if jwk.ext.is_some_and(|ext| !ext) && extractable {
342 return Err(Error::Data);
343 }
344
345 // Step 2.7. Let namedCurve be a string whose value is equal to the crv field of jwk.
346 // Step 2.8. If namedCurve is not equal to the namedCurve member of
347 // normalizedAlgorithm, throw a DataError.
348 let named_curve = jwk
349 .crv
350 .filter(|crv| *crv == normalized_algorithm.named_curve)
351 .map(|crv| crv.to_string())
352 .ok_or(Error::Data)?;
353
354 // Step 2.9.
355 let (handle, key_type) =
356 // If namedCurve is "P-256", "P-384" or "P-521":
357 if matches!(
358 named_curve.as_str(),
359 NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
360 ) {
361 // Step 9.1. Let algNamedCurve be a string whose initial value is undefined.
362 // Step 9.2.
363 // If the alg field is not present:
364 // Let algNamedCurve be undefined.
365 // If the alg field is equal to the string "ES256":
366 // Let algNamedCurve be the string "P-256".
367 // If the alg field is equal to the string "ES384":
368 // Let algNamedCurve be the string "P-384".
369 // If the alg field is equal to the string "ES512":
370 // Let algNamedCurve be the string "P-521".
371 // otherwise:
372 // throw a DataError.
373 let alg = jwk.alg.map(|alg| alg.to_string());
374 let alg_named_curve = match alg.as_deref() {
375 None => None,
376 Some("ES256") => Some(NAMED_CURVE_P256),
377 Some("ES384") => Some(NAMED_CURVE_P384),
378 Some("ES521") => Some(NAMED_CURVE_P521),
379 _ => return Err(Error::Data),
380 };
381
382 // Step 9.3. If algNamedCurve is defined, and is not equal to namedCurve, throw
383 // a DataError.
384 if alg_named_curve.is_some_and(|alg_named_curve| alg_named_curve != named_curve) {
385 return Err(Error::Data);
386 }
387
388 match jwk.d {
389 // If the d field is present:
390 Some(d) => {
391 // Step 2.9.1. If jwk does not meet the requirements of Section 6.2.2 of
392 // JSON Web Algorithms [JWA], then throw a DataError.
393 let x = match jwk.x {
394 Some(x) => Base64UrlUnpadded::decode_vec(&x.str())
395 .map_err(|_| Error::Data)?,
396 None => return Err(Error::Data),
397 };
398 let y = match jwk.y {
399 Some(y) => Base64UrlUnpadded::decode_vec(&y.str())
400 .map_err(|_| Error::Data)?,
401 None => return Err(Error::Data),
402 };
403 let d =
404 Base64UrlUnpadded::decode_vec(&d.str()).map_err(|_| Error::Data)?;
405
406 // Step 2.9.2. Let key be a new CryptoKey object that represents the
407 // Elliptic Curve private key identified by interpreting jwk according to
408 // Section 6.2.2 of JSON Web Algorithms [JWA].
409 // NOTE: CryptoKey is created in Step 2.12 - 2.15.
410 let handle = match named_curve.as_str() {
411 NAMED_CURVE_P256 => {
412 let private_key =
413 p256::SecretKey::from_slice(&d).map_err(|_| Error::Data)?;
414 let mut sec1_bytes = vec![4u8];
415 sec1_bytes.extend_from_slice(&x);
416 sec1_bytes.extend_from_slice(&y);
417 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
418 .map_err(|_| Error::Data)?;
419 NistP256::validate_public_key(&private_key, &encoded_point)
420 .map_err(|_| Error::Data)?;
421 Handle::P256PrivateKey(private_key)
422 },
423 NAMED_CURVE_P384 => {
424 let private_key =
425 p384::SecretKey::from_slice(&d).map_err(|_| Error::Data)?;
426 let mut sec1_bytes = vec![4u8];
427 sec1_bytes.extend_from_slice(&x);
428 sec1_bytes.extend_from_slice(&y);
429 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
430 .map_err(|_| Error::Data)?;
431 NistP384::validate_public_key(&private_key, &encoded_point)
432 .map_err(|_| Error::Data)?;
433 Handle::P384PrivateKey(private_key)
434 },
435 NAMED_CURVE_P521 => {
436 let private_key =
437 p521::SecretKey::from_slice(&d).map_err(|_| Error::Data)?;
438 let mut sec1_bytes = vec![4u8];
439 sec1_bytes.extend_from_slice(&x);
440 sec1_bytes.extend_from_slice(&y);
441 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
442 .map_err(|_| Error::Data)?;
443 NistP521::validate_public_key(&private_key, &encoded_point)
444 .map_err(|_| Error::Data)?;
445 Handle::P521PrivateKey(private_key)
446 },
447 _ => unreachable!(),
448 };
449
450 // Step 2.9.3. Set the [[type]] internal slot of Key to "private".
451 let key_type = KeyType::Private;
452
453 (handle, key_type)
454 },
455 // Otherwise:
456 None => {
457 // Step 2.9.1. If jwk does not meet the requirements of Section 6.2.1 of
458 // JSON Web Algorithms [JWA], then throw a DataError.
459 let x = match jwk.x {
460 Some(x) => Base64UrlUnpadded::decode_vec(&x.str())
461 .map_err(|_| Error::Data)?,
462 None => return Err(Error::Data),
463 };
464 let y = match jwk.y {
465 Some(y) => Base64UrlUnpadded::decode_vec(&y.str())
466 .map_err(|_| Error::Data)?,
467 None => return Err(Error::Data),
468 };
469
470 // Step 2.9.2. Let key be a new CryptoKey object that represents the
471 // Elliptic Curve public key identified by interpreting jwk according to
472 // Section 6.2.1 of JSON Web Algorithms [JWA].
473 // NOTE: CryptoKey is created in Step 2.12 - 2.15.
474 let handle = match named_curve.as_str() {
475 NAMED_CURVE_P256 => {
476 let mut sec1_bytes = vec![4u8];
477 sec1_bytes.extend_from_slice(&x);
478 sec1_bytes.extend_from_slice(&y);
479 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
480 .map_err(|_| Error::Data)?;
481 let public_key =
482 p256::PublicKey::from_encoded_point(&encoded_point)
483 .into_option()
484 .ok_or(Error::Data)?;
485 Handle::P256PublicKey(public_key)
486 },
487 NAMED_CURVE_P384 => {
488 let mut sec1_bytes = vec![4u8];
489 sec1_bytes.extend_from_slice(&x);
490 sec1_bytes.extend_from_slice(&y);
491 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
492 .map_err(|_| Error::Data)?;
493 let public_key =
494 p384::PublicKey::from_encoded_point(&encoded_point)
495 .into_option()
496 .ok_or(Error::Data)?;
497 Handle::P384PublicKey(public_key)
498 },
499 NAMED_CURVE_P521 => {
500 let mut sec1_bytes = vec![4u8];
501 sec1_bytes.extend_from_slice(&x);
502 sec1_bytes.extend_from_slice(&y);
503 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
504 .map_err(|_| Error::Data)?;
505 let public_key =
506 p521::PublicKey::from_encoded_point(&encoded_point)
507 .into_option()
508 .ok_or(Error::Data)?;
509 Handle::P521PublicKey(public_key)
510 },
511 _ => unreachable!(),
512 };
513
514 // Step 2.9.3. Set the [[type]] internal slot of Key to "public".
515 let key_type = KeyType::Public;
516
517 (handle, key_type)
518 },
519 }
520 }
521 // Otherwise
522 else {
523 // Step 2.9.1. Perform any key import steps defined by other applicable
524 // specifications, passing format, jwk and obtaining key.
525 // Step 2.9.2. If an error occurred or there are no applicable specifications,
526 // throw a DataError.
527 // NOTE: We currently do not support other applicable specifications.
528 return Err(Error::Data);
529 };
530
531 // Step 2.10. If the key value is not a valid point on the Elliptic Curve identified by
532 // the namedCurve member of normalizedAlgorithm throw a DataError.
533 // NOTE: Done in Step 2.9.
534
535 // Step 2.11. Let algorithm be a new instance of an EcKeyAlgorithm object.
536 // Step 2.12. Set the name attribute of algorithm to "ECDSA".
537 // Step 2.13. Set the namedCurve attribute of algorithm to namedCurve.
538 // Step 2.14. Set the [[algorithm]] internal slot of key to algorithm.
539 let algorithm = SubtleEcKeyAlgorithm {
540 name: ALG_ECDSA.to_string(),
541 named_curve,
542 };
543 CryptoKey::new(
544 global,
545 key_type,
546 extractable,
547 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
548 usages,
549 handle,
550 can_gc,
551 )
552 },
553 // If format is "raw":
554 KeyFormat::Raw => {
555 // Step 2.1. If the namedCurve member of normalizedAlgorithm is not a named curve, then
556 // throw a DataError.
557 if !SUPPORTED_CURVES
558 .iter()
559 .any(|&supported_curve| supported_curve == normalized_algorithm.named_curve)
560 {
561 return Err(Error::Data);
562 }
563
564 // Step 2.2. If usages contains a value which is not "verify" then throw a SyntaxError.
565 if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
566 return Err(Error::Syntax(None));
567 }
568
569 // Step 2.3.
570 // If namedCurve is "P-256", "P-384" or "P-521":
571 let handle = if matches!(
572 normalized_algorithm.named_curve.as_str(),
573 NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
574 ) {
575 // Step 2.3.1. Let Q be the elliptic curve point on the curve identified by the
576 // namedCurve member of normalizedAlgorithm identified by performing the conversion
577 // steps defined in Section 2.3.4 of [SEC1] on keyData.
578 // Step 2.3.2. The uncompressed point format MUST be supported.
579 // Step 2.3.3. If the implementation does not support the compressed point format
580 // and a compressed point is provided, throw a DataError.
581 // Step 2.3.4. If a decode error occurs or an identity point is found, throw a
582 // DataError.
583 match normalized_algorithm.named_curve.as_str() {
584 NAMED_CURVE_P256 => {
585 let q =
586 p256::PublicKey::from_sec1_bytes(key_data).map_err(|_| Error::Data)?;
587 Handle::P256PublicKey(q)
588 },
589 NAMED_CURVE_P384 => {
590 let q =
591 p384::PublicKey::from_sec1_bytes(key_data).map_err(|_| Error::Data)?;
592 Handle::P384PublicKey(q)
593 },
594 NAMED_CURVE_P521 => {
595 let q =
596 p521::PublicKey::from_sec1_bytes(key_data).map_err(|_| Error::Data)?;
597 Handle::P521PublicKey(q)
598 },
599 _ => unreachable!(),
600 }
601
602 // Step 2.3.5. Let key be a new CryptoKey that represents Q.
603 // NOTE: CryptoKey is created in Step 2.7 - 2.8.
604 }
605 // Otherwise:
606 else {
607 // Step. 2.3.1. Perform any key import steps defined by other applicable
608 // specifications, passing format, keyData and obtaining key.
609 // Step. 2.3.2. If an error occured 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);
613 };
614
615 // Step 2.4. Let algorithm be a new EcKeyAlgorithm object.
616 // Step 2.5. Set the name attribute of algorithm to "ECDSA".
617 // Step 2.6. Set the namedCurve attribute of algorithm to equal the namedCurve member
618 // of normalizedAlgorithm.
619 let algorithm = SubtleEcKeyAlgorithm {
620 name: ALG_ECDSA.to_string(),
621 named_curve: normalized_algorithm.named_curve.clone(),
622 };
623
624 // Step 2.7. Set the [[type]] internal slot of key to "public"
625 // Step 2.8. Set the [[algorithm]] internal slot of key to algorithm.
626 CryptoKey::new(
627 global,
628 KeyType::Public,
629 extractable,
630 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
631 usages,
632 handle,
633 can_gc,
634 )
635 },
636 };
637
638 // Step 3. Return key.
639 Ok(key)
640}
641
642/// <https://w3c.github.io/webcrypto/#ecdsa-operations-export-key>
643pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
644 // Step 1. Let key be the CryptoKey to be exported.
645
646 // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
647 // slot of key cannot be accessed, then throw an OperationError.
648 // NOTE: Done in Step 3.
649
650 // Step 3.
651 let result = match format {
652 // If format is "spki":
653 KeyFormat::Spki => {
654 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
655 // InvalidAccessError.
656 if key.Type() != KeyType::Public {
657 return Err(Error::InvalidAccess);
658 }
659
660 // Step 3.2.
661 // Let data be an instance of the SubjectPublicKeyInfo ASN.1 structure defined in
662 // [RFC5280] with the following properties:
663 // * Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the
664 // following properties:
665 // * Set the algorithm field to the OID id-ecPublicKey defined in [RFC5480].
666 // * Set the parameters field to an instance of the ECParameters ASN.1 type
667 // defined in [RFC5480] as follows:
668 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
669 // "P-256", "P-384" or "P-521":
670 // Let keyData be the byte sequence that represents the Elliptic Curve
671 // public key represented by the [[handle]] internal slot of key
672 // according to the encoding rules specified in Section 2.2 of
673 // [RFC5480] and using the uncompressed form. and keyData.
674 // If the namedCurve attribute of the [[algorithm]] internal slot
675 // of key is "P-256":
676 // Set parameters to the namedCurve choice with value equal to
677 // the object identifier secp256r1 defined in [RFC5480]
678 // If the namedCurve attribute of the [[algorithm]] internal slot
679 // of key is "P-384":
680 // Set parameters to the namedCurve choice with value equal to
681 // the object identifier secp384r1 defined in [RFC5480]
682 // If the namedCurve attribute of the [[algorithm]] internal slot
683 // of key is "P-521":
684 // Set parameters to the namedCurve choice with value equal to
685 // the object identifier secp521r1 defined in [RFC5480]
686 // Otherwise:
687 // 1. Perform any key export steps defined by other applicable
688 // specifications, passing format and the namedCurve attribute of
689 // the [[algorithm]] internal slot of key and obtaining
690 // namedCurveOid and keyData.
691 // 2. Set parameters to the namedCurve choice with value equal to the
692 // object identifier namedCurveOid.
693 // * Set the subjectPublicKey field to keyData.
694 // NOTE: We currently do not support other applicable specifications.
695 let data = match key.handle() {
696 Handle::P256PublicKey(public_key) => public_key.to_public_key_der(),
697 Handle::P384PublicKey(public_key) => public_key.to_public_key_der(),
698 Handle::P521PublicKey(public_key) => public_key.to_public_key_der(),
699 _ => return Err(Error::Operation),
700 }
701 .map_err(|_| Error::Operation)?;
702
703 // Step 3.3. Let result be the result of DER-encoding data.
704 ExportedKey::Raw(data.to_vec())
705 },
706 // If format is "pkcs8":
707 KeyFormat::Pkcs8 => {
708 // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
709 // InvalidAccessError.
710 if key.Type() != KeyType::Private {
711 return Err(Error::InvalidAccess);
712 }
713
714 // Step 3.2.
715 // Let data be an instance of the PrivateKeyInfo ASN.1 structure defined in [RFC5208]
716 // with the following properties:
717 // * Set the version field to 0.
718 // * Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1
719 // type with the following properties:
720 // * Set the algorithm field to the OID id-ecPublicKey defined in [RFC5480].
721 // * Set the parameters field to an instance of the ECParameters ASN.1 type
722 // defined in [RFC5480] as follows:
723 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
724 // "P-256", "P-384" or "P-521":
725 // Let keyData be the result of DER-encoding an instance of the
726 // ECPrivateKey structure defined in Section 3 of [RFC5915] for the
727 // Elliptic Curve private key represented by the [[handle]] internal
728 // slot of key and that conforms to the following:
729 // * The parameters field is present, and is equivalent to the
730 // parameters field of the privateKeyAlgorithm field of this
731 // PrivateKeyInfo ASN.1 structure.
732 // * The publicKey field is present and represents the Elliptic
733 // Curve public key associated with the Elliptic Curve private key
734 // represented by the [[handle]] internal slot of key.
735 // * If the namedCurve attribute of the [[algorithm]] internal slot
736 // of key is "P-256":
737 // Set parameters to the namedCurve choice with value equal to
738 // the object identifier secp256r1 defined in [RFC5480]
739 // * If the namedCurve attribute of the [[algorithm]] internal slot
740 // of key is "P-384":
741 // Set parameters to the namedCurve choice with value equal to
742 // the object identifier secp384r1 defined in [RFC5480]
743 // * If the namedCurve attribute of the [[algorithm]] internal slot
744 // of key is "P-521":
745 // Set parameters to the namedCurve choice with value equal to
746 // the object identifier secp521r1 defined in [RFC5480]
747 // Otherwise:
748 // 1. Perform any key export steps defined by other applicable
749 // specifications, passing format and the namedCurve attribute of
750 // the [[algorithm]] internal slot of key and obtaining
751 // namedCurveOid and keyData.
752 // 2. Set parameters to the namedCurve choice with value equal to the
753 // object identifier namedCurveOid.
754 // * Set the privateKey field to keyData.
755 // NOTE: We currently do not support other applicable specifications.
756 let data = match key.handle() {
757 Handle::P256PrivateKey(private_key) => private_key.to_pkcs8_der(),
758 Handle::P384PrivateKey(private_key) => private_key.to_pkcs8_der(),
759 Handle::P521PrivateKey(private_key) => private_key.to_pkcs8_der(),
760 _ => return Err(Error::Operation),
761 }
762 .map_err(|_| Error::Operation)?;
763
764 // Step 3.3. Let result be the result of DER-encoding data.
765 ExportedKey::Raw(data.as_bytes().to_vec())
766 },
767 // If format is "jwk":
768 KeyFormat::Jwk => {
769 // Step 3.1. Let jwk be a new JsonWebKey dictionary.
770 // Step 3.2. Set the kty attribute of jwk to "EC".
771 let mut jwk = JsonWebKey {
772 kty: Some(DOMString::from("EC")),
773 ..Default::default()
774 };
775
776 // Step 3.3.
777 let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() else {
778 return Err(Error::Operation);
779 };
780 // If the namedCurve attribute of the [[algorithm]] internal slot of key is "P-256",
781 // "P-384" or "P-521":
782 if matches!(
783 algorithm.named_curve.as_str(),
784 NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
785 ) {
786 // Step 3.3.1.
787 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
788 // "P-256":
789 // Set the crv attribute of jwk to "P-256"
790 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
791 // "P-384":
792 // Set the crv attribute of jwk to "P-384"
793 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
794 // "P-521":
795 // Set the crv attribute of jwk to "P-521"
796 jwk.crv = Some(DOMString::from(algorithm.named_curve.as_str()));
797
798 // Step 3.3.2. Set the x attribute of jwk according to the definition in Section
799 // 6.2.1.2 of JSON Web Algorithms [JWA].
800 // Step 3.3.3. Set the y attribute of jwk according to the definition in Section
801 // 6.2.1.3 of JSON Web Algorithms [JWA].
802 let (x, y) = match key.handle() {
803 Handle::P256PublicKey(public_key) => {
804 let encoded_point = public_key.to_encoded_point(false);
805 (
806 encoded_point.x().ok_or(Error::Operation)?.to_vec(),
807 encoded_point.y().ok_or(Error::Operation)?.to_vec(),
808 )
809 },
810 Handle::P384PublicKey(public_key) => {
811 let encoded_point = public_key.to_encoded_point(false);
812 (
813 encoded_point.x().ok_or(Error::Operation)?.to_vec(),
814 encoded_point.y().ok_or(Error::Operation)?.to_vec(),
815 )
816 },
817 Handle::P521PublicKey(public_key) => {
818 let encoded_point = public_key.to_encoded_point(false);
819 (
820 encoded_point.x().ok_or(Error::Operation)?.to_vec(),
821 encoded_point.y().ok_or(Error::Operation)?.to_vec(),
822 )
823 },
824 Handle::P256PrivateKey(private_key) => {
825 let public_key = private_key.public_key();
826 let encoded_point = public_key.to_encoded_point(false);
827 (
828 encoded_point.x().ok_or(Error::Operation)?.to_vec(),
829 encoded_point.y().ok_or(Error::Operation)?.to_vec(),
830 )
831 },
832 Handle::P384PrivateKey(private_key) => {
833 let public_key = private_key.public_key();
834 let encoded_point = public_key.to_encoded_point(false);
835 (
836 encoded_point.x().ok_or(Error::Operation)?.to_vec(),
837 encoded_point.y().ok_or(Error::Operation)?.to_vec(),
838 )
839 },
840 Handle::P521PrivateKey(private_key) => {
841 let public_key = private_key.public_key();
842 let encoded_point = public_key.to_encoded_point(false);
843 (
844 encoded_point.x().ok_or(Error::Operation)?.to_vec(),
845 encoded_point.y().ok_or(Error::Operation)?.to_vec(),
846 )
847 },
848 _ => return Err(Error::Operation),
849 };
850 jwk.x = Some(Base64UrlUnpadded::encode_string(&x).into());
851 jwk.y = Some(Base64UrlUnpadded::encode_string(&y).into());
852
853 // Step 3.3.4.
854 // If the [[type]] internal slot of key is "private"
855 // Set the d attribute of jwk according to the definition in Section 6.2.2.1 of
856 // JSON Web Algorithms [JWA].
857 if key.Type() == KeyType::Private {
858 let d = match key.handle() {
859 Handle::P256PrivateKey(private_key) => private_key.to_bytes().to_vec(),
860 Handle::P384PrivateKey(private_key) => private_key.to_bytes().to_vec(),
861 Handle::P521PrivateKey(private_key) => private_key.to_bytes().to_vec(),
862 _ => return Err(Error::NotSupported),
863 };
864 jwk.d = Some(Base64UrlUnpadded::encode_string(&d).into());
865 }
866 }
867 // Otherwise:
868 else {
869 // Step 3.3.1. Perform any key export steps defined by other applicable
870 // specifications, passing format and the namedCurve attribute of the [[algorithm]]
871 // internal slot of key and obtaining namedCurve and a new value of jwk.
872 // Step 3.3.2. Set the crv attribute of jwk to namedCurve.
873 // NOTE: We currently do not support other applicable specifications.
874 unreachable!()
875 }
876
877 // Step 3.4. Set the key_ops attribute of jwk to the usages attribute of key.
878 jwk.key_ops = Some(
879 key.usages()
880 .iter()
881 .map(|usage| DOMString::from(usage.as_str()))
882 .collect::<Vec<DOMString>>(),
883 );
884
885 // Step 3.4. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
886 jwk.ext = Some(key.Extractable());
887
888 // Step 3.4. Let result be jwk.
889 ExportedKey::Jwk(Box::new(jwk))
890 },
891 // If format is "raw":
892 KeyFormat::Raw => {
893 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
894 // InvalidAccessError.
895 if key.Type() != KeyType::Public {
896 return Err(Error::InvalidAccess);
897 }
898
899 // Step 3.2.
900 // If the namedCurve attribute of the [[algorithm]] internal slot of key is "P-256",
901 // "P-384" or "P-521":
902 // Let data be a byte sequence representing the Elliptic Curve point Q represented
903 // by the [[handle]] internal slot of key according to [SEC1] 2.3.3 using the
904 // uncompressed format.
905 // Otherwise:
906 // Perform any key export steps defined by other applicable specifications, passing
907 // format and the namedCurve attribute of the [[algorithm]] internal slot of key
908 // and obtaining namedCurve and data.
909 // NOTE: We currently do not support other applicable specifications.
910 let named_curve =
911 if let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() {
912 algorithm.named_curve.as_str()
913 } else {
914 return Err(Error::Operation);
915 };
916 let data = if matches!(
917 named_curve,
918 NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
919 ) {
920 match key.handle() {
921 Handle::P256PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
922 Handle::P384PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
923 Handle::P521PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
924 _ => return Err(Error::Operation),
925 }
926 } else {
927 return Err(Error::NotSupported);
928 };
929
930 // Step 3.3. Let result be data.
931 ExportedKey::Raw(data)
932 },
933 // Otherwise: throw a NotSupportedError. (Unreachable)
934 };
935
936 // Step 4. Return result.
937 Ok(result)
938}