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