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