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