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