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