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