script/dom/subtlecrypto/ecdh_operation.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 base64ct::{Base64UrlUnpadded, Encoding};
6use elliptic_curve::SecretKey;
7use elliptic_curve::rand_core::OsRng;
8use elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint, ValidatePublicKey};
9use p256::NistP256;
10use p384::NistP384;
11use p521::NistP521;
12use pkcs8::der::Decode;
13use pkcs8::spki::EncodePublicKey;
14use pkcs8::{
15 AssociatedOid, EncodePrivateKey, ObjectIdentifier, PrivateKeyInfo, SubjectPublicKeyInfo,
16};
17use sec1::der::asn1::BitString;
18use sec1::{EcPrivateKey, EncodedPoint};
19
20use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
21 CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
22};
23use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{JsonWebKey, KeyFormat};
24use crate::dom::bindings::error::Error;
25use crate::dom::bindings::root::DomRoot;
26use crate::dom::bindings::str::DOMString;
27use crate::dom::cryptokey::{CryptoKey, Handle};
28use crate::dom::globalscope::GlobalScope;
29use crate::dom::subtlecrypto::{
30 ALG_ECDH, ExportedKey, JsonWebKeyExt, KeyAlgorithmAndDerivatives, NAMED_CURVE_P256,
31 NAMED_CURVE_P384, NAMED_CURVE_P521, SUPPORTED_CURVES, SubtleEcKeyAlgorithm,
32 SubtleEcKeyGenParams, SubtleEcKeyImportParams,
33};
34use crate::script_runtime::CanGc;
35
36/// <https://w3c.github.io/webcrypto/#ecdh-operations-generate-key>
37pub(crate) fn generate_key(
38 global: &GlobalScope,
39 normalized_algorithm: &SubtleEcKeyGenParams,
40 extractable: bool,
41 usages: Vec<KeyUsage>,
42 can_gc: CanGc,
43) -> Result<CryptoKeyPair, Error> {
44 // Step 1. If usages contains an entry which is not "deriveKey" or "deriveBits" then throw a
45 // SyntaxError.
46 if usages
47 .iter()
48 .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
49 {
50 return Err(Error::Syntax(None));
51 }
52
53 // Step 2.
54 // If the namedCurve member of normalizedAlgorithm is "P-256", "P-384" or "P-521":
55 // Generate an Elliptic Curve key pair, as defined in [RFC6090] with domain parameters for
56 // the curve identified by the namedCurve member of normalizedAlgorithm.
57 // If the namedCurve member of normalizedAlgorithm is a value specified in an applicable
58 // specification that specifies the use of that value with ECDH:
59 // Perform the ECDH generation steps specified in that specification, passing in
60 // normalizedAlgorithm and resulting in an elliptic curve key pair.
61 // Otherwise:
62 // throw a NotSupportedError
63 // Step 3. If performing the operation results in an error, then throw a OperationError.
64 let (private_key_handle, public_key_handle) = match normalized_algorithm.named_curve.as_str() {
65 NAMED_CURVE_P256 => {
66 let private_key = SecretKey::<NistP256>::random(&mut OsRng);
67 let public_key = private_key.public_key();
68 (
69 Handle::P256PrivateKey(private_key),
70 Handle::P256PublicKey(public_key),
71 )
72 },
73 NAMED_CURVE_P384 => {
74 let private_key = SecretKey::<NistP384>::random(&mut OsRng);
75 let public_key = private_key.public_key();
76 (
77 Handle::P384PrivateKey(private_key),
78 Handle::P384PublicKey(public_key),
79 )
80 },
81 NAMED_CURVE_P521 => {
82 let private_key = SecretKey::<NistP521>::random(&mut OsRng);
83 let public_key = private_key.public_key();
84 (
85 Handle::P521PrivateKey(private_key),
86 Handle::P521PublicKey(public_key),
87 )
88 },
89 _ => return Err(Error::NotSupported),
90 };
91
92 // Step 4. Let algorithm be a new EcKeyAlgorithm object.
93 // Step 5. Set the name member of algorithm to "ECDH".
94 // Step 6. Set the namedCurve attribute of algorithm to equal the namedCurve member of
95 // normalizedAlgorithm.
96 let algorithm = SubtleEcKeyAlgorithm {
97 name: ALG_ECDH.to_string(),
98 named_curve: normalized_algorithm.named_curve.clone(),
99 };
100
101 // Step 7. Let publicKey be a new CryptoKey representing the public key of the generated key pair.
102 // Step 8. Set the [[type]] internal slot of publicKey to "public"
103 // Step 9. Set the [[algorithm]] internal slot of publicKey to algorithm.
104 // Step 10. Set the [[extractable]] internal slot of publicKey to true.
105 // Step 11. Set the [[usages]] internal slot of publicKey to be the empty list.
106 let public_key = CryptoKey::new(
107 global,
108 KeyType::Public,
109 true,
110 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm.clone()),
111 Vec::new(),
112 public_key_handle,
113 can_gc,
114 );
115
116 // Step 12. Let privateKey be a new CryptoKey representing the private key of the generated key pair.
117 // Step 13. Set the [[type]] internal slot of privateKey to "private"
118 // Step 14. Set the [[algorithm]] internal slot of privateKey to algorithm.
119 // Step 15. Set the [[extractable]] internal slot of privateKey to extractable.
120 // Step 16. Set the [[usages]] internal slot of privateKey to be the usage intersection of
121 // usages and [ "deriveKey", "deriveBits" ].
122 let private_key = CryptoKey::new(
123 global,
124 KeyType::Private,
125 extractable,
126 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
127 usages
128 .iter()
129 .filter(|usage| matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
130 .cloned()
131 .collect(),
132 private_key_handle,
133 can_gc,
134 );
135
136 // Step 17. Let result be a new CryptoKeyPair dictionary.
137 // Step 18. Set the publicKey attribute of result to be publicKey.
138 // Step 19. Set the privateKey attribute of result to be privateKey.
139 let result = CryptoKeyPair {
140 publicKey: Some(public_key),
141 privateKey: Some(private_key),
142 };
143
144 // Step 20. Return result.
145 Ok(result)
146}
147
148/// <https://w3c.github.io/webcrypto/#ecdh-operations-import-key>
149pub(crate) fn import_key(
150 global: &GlobalScope,
151 normalized_algorithm: &SubtleEcKeyImportParams,
152 format: KeyFormat,
153 key_data: &[u8],
154 extractable: bool,
155 usages: Vec<KeyUsage>,
156 can_gc: CanGc,
157) -> Result<DomRoot<CryptoKey>, Error> {
158 // Step 1. Let keyData be the key data to be imported.
159
160 // Step 2.
161 let key = match format {
162 KeyFormat::Spki => {
163 // Step 2.1. If usages is not empty then throw a SyntaxError.
164 if !usages.is_empty() {
165 return Err(Error::Syntax(None));
166 }
167
168 // Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
169 // algorithm over keyData
170 // Step 2.3. If an error occurred while parsing, then throw a DataError.
171 let spki = SubjectPublicKeyInfo::<_, BitString>::from_der(key_data)
172 .map_err(|_| Error::Data)?;
173
174 // Step 2.4. If the algorithm object identifier field of the algorithm
175 // AlgorithmIdentifier field of spki is not equal to the id-ecPublicKey object
176 // identifier defined in [RFC5480], then throw a DataError.
177 if spki.algorithm.oid != elliptic_curve::ALGORITHM_OID {
178 return Err(Error::Data);
179 }
180
181 // Step 2.5. If the parameters field of the algorithm AlgorithmIdentifier field of spki
182 // is absent, then throw a DataError.
183 // Step 2.6. Let params be the parameters field of the algorithm AlgorithmIdentifier
184 // field of spki.
185 // Step 2.7. If params is not an instance of the ECParameters ASN.1 type defined in
186 // [RFC5480] that specifies a namedCurve, then throw a DataError.
187 let Some(params): Option<ObjectIdentifier> = spki.algorithm.parameters else {
188 return Err(Error::Data);
189 };
190
191 // Step 2.8. Let namedCurve be a string whose initial value is undefined.
192 // Step 2.9.
193 // If params is equivalent to the secp256r1 object identifier defined in [RFC5480]:
194 // Set namedCurve "P-256".
195 // If params is equivalent to the secp384r1 object identifier defined in [RFC5480]:
196 // Set namedCurve "P-384".
197 // If params is equivalent to the secp521r1 object identifier defined in [RFC5480]:
198 // Set namedCurve "P-521".
199 let named_curve = match params {
200 NistP256::OID => Some(NAMED_CURVE_P256),
201 NistP384::OID => Some(NAMED_CURVE_P384),
202 NistP521::OID => Some(NAMED_CURVE_P521),
203 _ => None,
204 };
205
206 // Step 2.10.
207 let handle = match named_curve {
208 // If namedCurve is not undefined:
209 Some(curve) => {
210 // Step 2.10.1. Let publicKey be the Elliptic Curve public key identified by
211 // performing the conversion steps defined in Section 2.3.4 of [SEC1] to the
212 // subjectPublicKey field of spki.
213 // Step 2.10.2. The uncompressed point format MUST be supported.
214 // Step 2.10.3. If the implementation does not support the compressed point
215 // format and a compressed point is provided, throw a DataError.
216 // Step 2.10.4. If a decode error occurs or an identity point is found, throw a
217 // DataError.
218 let sec1_bytes = spki.subject_public_key.as_bytes().ok_or(Error::Data)?;
219 match curve {
220 NAMED_CURVE_P256 => {
221 let public_key = p256::PublicKey::from_sec1_bytes(sec1_bytes)
222 .map_err(|_| Error::Data)?;
223 Handle::P256PublicKey(public_key)
224 },
225 NAMED_CURVE_P384 => {
226 let public_key = p384::PublicKey::from_sec1_bytes(sec1_bytes)
227 .map_err(|_| Error::Data)?;
228 Handle::P384PublicKey(public_key)
229 },
230 NAMED_CURVE_P521 => {
231 let public_key = p521::PublicKey::from_sec1_bytes(sec1_bytes)
232 .map_err(|_| Error::Data)?;
233 Handle::P521PublicKey(public_key)
234 },
235 _ => unreachable!(),
236 }
237
238 // Step 2.10.5. Let key be a new CryptoKey that represents publicKey.
239 // NOTE: CryptoKey is created in Step 2.13 - 2.17.
240 },
241 // Otherwise:
242 None => {
243 // Step 2.10.1. Perform any key import steps defined by other applicable
244 // specifications, passing format, spki and obtaining namedCurve and key.
245 // Step 2.10.2. If an error occurred or there are no applicable specifications,
246 // throw a DataError.
247 // NOTE: We currently do not support applicable specifications.
248 return Err(Error::NotSupported);
249 },
250 };
251
252 // Step 2.11. If namedCurve is defined, and not equal to the namedCurve member of
253 // normalizedAlgorithm, throw a DataError.
254 if named_curve.is_some_and(|curve| curve != normalized_algorithm.named_curve) {
255 return Err(Error::Data);
256 }
257
258 // Step 2.12. If the key value is not a valid point on the Elliptic Curve identified by
259 // the namedCurve member of normalizedAlgorithm throw a DataError.
260 // NOTE: Done in Step 2.10.
261
262 // Step 2.13. Set the [[type]] internal slot of key to "public"
263 // Step 2.14. Let algorithm be a new EcKeyAlgorithm.
264 // Step 2.15. Set the name attribute of algorithm to "ECDH".
265 // Step 2.16. Set the namedCurve attribute of algorithm to namedCurve.
266 // Step 2.17. Set the [[algorithm]] internal slot of key to algorithm.
267 let algorithm = SubtleEcKeyAlgorithm {
268 name: ALG_ECDH.to_string(),
269 named_curve: named_curve
270 .expect("named_curve must exist here")
271 .to_string(),
272 };
273 CryptoKey::new(
274 global,
275 KeyType::Public,
276 extractable,
277 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
278 usages,
279 handle,
280 can_gc,
281 )
282 },
283 KeyFormat::Pkcs8 => {
284 // Step 2.1. If usages contains an entry which is not "deriveKey" or "deriveBits" then
285 // throw a SyntaxError.
286 if usages
287 .iter()
288 .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
289 {
290 return Err(Error::Syntax(None));
291 }
292
293 // Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
294 // algorithm over keyData.
295 // Step 2.3. If an error occurs while parsing, throw a DataError.
296 let private_key_info = PrivateKeyInfo::from_der(key_data).map_err(|_| Error::Data)?;
297
298 // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
299 // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-ecPublicKey
300 // object identifier defined in [RFC5480], throw a DataError.
301 if private_key_info.algorithm.oid != elliptic_curve::ALGORITHM_OID {
302 return Err(Error::Data);
303 }
304
305 // Step 2.5. If the parameters field of the privateKeyAlgorithm
306 // PrivateKeyAlgorithmIdentifier field of privateKeyInfo is not present, throw a
307 // DataError.
308 // Step 2.6. Let params be the parameters field of the privateKeyAlgorithm
309 // PrivateKeyAlgorithmIdentifier field of privateKeyInfo.
310 // Step 2.7. If params is not an instance of the ECParameters ASN.1 type defined in
311 // [RFC5480] that specifies a namedCurve, then throw a DataError.
312 let params: ObjectIdentifier =
313 if let Some(params) = private_key_info.algorithm.parameters {
314 params.decode_as().map_err(|_| Error::Data)?
315 } else {
316 return Err(Error::Data);
317 };
318
319 // Step 2.8. Let namedCurve be a string whose initial value is undefined.
320 // Step 2.9.
321 // If params is equivalent to the secp256r1 object identifier defined in [RFC5480]:
322 // Set namedCurve to "P-256".
323 // If params is equivalent to the secp384r1 object identifier defined in [RFC5480]:
324 // Set namedCurve to "P-384".
325 // If params is equivalent to the secp521r1 object identifier defined in [RFC5480]:
326 // Set namedCurve to "P-521".
327 let named_curve = match params {
328 NistP256::OID => Some(NAMED_CURVE_P256),
329 NistP384::OID => Some(NAMED_CURVE_P384),
330 NistP521::OID => Some(NAMED_CURVE_P521),
331 _ => None,
332 };
333
334 // Step 2.10.
335 let handle = match named_curve {
336 // If namedCurve is not undefined:
337 Some(curve) => {
338 // Step 2.10.1. Let ecPrivateKey be the result of performing the parse an ASN.1
339 // structure algorithm, with data as the privateKey field of privateKeyInfo,
340 // structure as the ASN.1 ECPrivateKey structure specified in Section 3 of
341 // [RFC5915], and exactData set to true.
342 // Step 2.10.2. If an error occurred while parsing, then throw a DataError.
343 let ec_private_key = EcPrivateKey::try_from(private_key_info.private_key)
344 .map_err(|_| Error::Data)?;
345
346 // Step 2.10.3. If the parameters field of ecPrivateKey is present, and is not
347 // an instance of the namedCurve ASN.1 type defined in [RFC5480], or does not
348 // contain the same object identifier as the parameters field of the
349 // privateKeyAlgorithm PrivateKeyAlgorithmIdentifier field of privateKeyInfo,
350 // throw a DataError.
351 if ec_private_key.parameters.is_some_and(|parameters| {
352 parameters
353 .named_curve()
354 .is_none_or(|parameters| parameters != params)
355 }) {
356 return Err(Error::Data);
357 }
358
359 // Step 2.10.4. Let key be a new CryptoKey that represents the Elliptic Curve
360 // private key identified by performing the conversion steps defined in Section
361 // 3 of [RFC5915] using ecPrivateKey.
362 // NOTE: CryptoKey is created in Step 2.13 - 2.17.
363 match curve {
364 NAMED_CURVE_P256 => {
365 let private_key = p256::SecretKey::try_from(ec_private_key)
366 .map_err(|_| Error::Data)?;
367 Handle::P256PrivateKey(private_key)
368 },
369 NAMED_CURVE_P384 => {
370 let private_key = p384::SecretKey::try_from(ec_private_key)
371 .map_err(|_| Error::Data)?;
372 Handle::P384PrivateKey(private_key)
373 },
374 NAMED_CURVE_P521 => {
375 let private_key = p521::SecretKey::try_from(ec_private_key)
376 .map_err(|_| Error::Data)?;
377 Handle::P521PrivateKey(private_key)
378 },
379 _ => unreachable!(),
380 }
381 },
382 // Otherwise:
383 None => {
384 // Step 2.10.1. Perform any key import steps defined by other applicable
385 // specifications, passing format, privateKeyInfo and obtaining namedCurve and
386 // key.
387 // Step 2.10.2. If an error occurred or there are no applicable specifications,
388 // throw a DataError.
389 // NOTE: We currently do not support applicable specifications.
390 return Err(Error::NotSupported);
391 },
392 };
393
394 // Step 2.11. If namedCurve is defined, and not equal to the namedCurve member of
395 // normalizedAlgorithm, throw a DataError.
396 if named_curve.is_some_and(|curve| curve != normalized_algorithm.named_curve) {
397 return Err(Error::Data);
398 }
399
400 // Step 2.12. If the key value is not a valid point on the Elliptic Curve identified by
401 // the namedCurve member of normalizedAlgorithm throw a DataError.
402 // NOTE: Done in Step 2.10.
403
404 // Step 2.13. Set the [[type]] internal slot of key to "private".
405 // Step 2.14. Let algorithm be a new EcKeyAlgorithm.
406 // Step 2.15. Set the name attribute of algorithm to "ECDH".
407 // Step 2.16. Set the namedCurve attribute of algorithm to namedCurve.
408 // Step 2.17. Set the [[algorithm]] internal slot of key to algorithm.
409 let algorithm = SubtleEcKeyAlgorithm {
410 name: ALG_ECDH.to_string(),
411 named_curve: named_curve
412 .expect("named_curve must exist here")
413 .to_string(),
414 };
415 CryptoKey::new(
416 global,
417 KeyType::Private,
418 extractable,
419 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
420 usages,
421 handle,
422 can_gc,
423 )
424 },
425 KeyFormat::Jwk => {
426 // Step 2.1.
427 // If keyData is a JsonWebKey dictionary:
428 // Let jwk equal keyData.
429 // Otherwise:
430 // Throw a DataError.
431 let jwk = JsonWebKey::parse(GlobalScope::get_cx(), key_data)?;
432
433 // Step 2.2. If the d field is present and if usages contains an entry which is not
434 // "deriveKey" or "deriveBits" then throw a SyntaxError.
435 if jwk.d.as_ref().is_some() &&
436 usages
437 .iter()
438 .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
439 {
440 return Err(Error::Syntax(None));
441 }
442
443 // Step 2.3. If the d field is not present and if usages is not empty then throw a
444 // SyntaxError.
445 if jwk.d.as_ref().is_none() && !usages.is_empty() {
446 return Err(Error::Syntax(None));
447 }
448
449 // Step 2.4. If the kty field of jwk is not "EC", then throw a DataError.
450 if jwk.kty.as_ref().is_none_or(|kty| kty != "EC") {
451 return Err(Error::Data);
452 }
453
454 // Step 2.5. If usages is non-empty and the use field of jwk is present and is not
455 // equal to "enc" then throw a DataError.
456 if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "enc") {
457 return Err(Error::Data);
458 }
459
460 // Step 2.6. If the key_ops field of jwk is present, and is invalid according to the
461 // requirements of JSON Web Key [JWK], or it does not contain all of the specified
462 // usages values, then throw a DataError.
463 jwk.check_key_ops(&usages)?;
464
465 // Step 2.7. If the ext field of jwk is present and has the value false and extractable
466 // is true, then throw a DataError.
467 if jwk.ext.is_some_and(|ext| !ext) && extractable {
468 return Err(Error::Data);
469 }
470
471 // Step 2.8. Let namedCurve be a string whose value is equal to the crv field of jwk.
472 // Step 2.9. If namedCurve is not equal to the namedCurve member of
473 // normalizedAlgorithm, throw a DataError.
474 let named_curve = jwk
475 .crv
476 .filter(|crv| *crv == normalized_algorithm.named_curve)
477 .map(|crv| crv.to_string())
478 .ok_or(Error::Data)?;
479
480 // Step 2.10.
481 // If namedCurve is "P-256", "P-384" or "P-521":
482 let (handle, key_type) =
483 if matches!(
484 named_curve.as_str(),
485 NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
486 ) {
487 match jwk.d {
488 // If the d field is present:
489 Some(d) => {
490 // Step 2.10.1. If jwk does not meet the requirements of Section 6.2.2 of
491 // JSON Web Algorithms [JWA], then throw a DataError.
492 let x = match jwk.x {
493 Some(x) => Base64UrlUnpadded::decode_vec(&x.str())
494 .map_err(|_| Error::Data)?,
495 None => return Err(Error::Data),
496 };
497 let y = match jwk.y {
498 Some(y) => Base64UrlUnpadded::decode_vec(&y.str())
499 .map_err(|_| Error::Data)?,
500 None => return Err(Error::Data),
501 };
502 let d =
503 Base64UrlUnpadded::decode_vec(&d.str()).map_err(|_| Error::Data)?;
504
505 // Step 2.10.2. Let key be a new CryptoKey object that represents the
506 // Elliptic Curve private key identified by interpreting jwk according to
507 // Section 6.2.2 of JSON Web Algorithms [JWA].
508 // NOTE: CryptoKey is created in Step 2.12 - 2.15.
509 let handle = match named_curve.as_str() {
510 NAMED_CURVE_P256 => {
511 let private_key =
512 p256::SecretKey::from_slice(&d).map_err(|_| Error::Data)?;
513 let mut sec1_bytes = vec![4u8];
514 sec1_bytes.extend_from_slice(&x);
515 sec1_bytes.extend_from_slice(&y);
516 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
517 .map_err(|_| Error::Data)?;
518 NistP256::validate_public_key(&private_key, &encoded_point)
519 .map_err(|_| Error::Data)?;
520 Handle::P256PrivateKey(private_key)
521 },
522 NAMED_CURVE_P384 => {
523 let private_key =
524 p384::SecretKey::from_slice(&d).map_err(|_| Error::Data)?;
525 let mut sec1_bytes = vec![4u8];
526 sec1_bytes.extend_from_slice(&x);
527 sec1_bytes.extend_from_slice(&y);
528 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
529 .map_err(|_| Error::Data)?;
530 NistP384::validate_public_key(&private_key, &encoded_point)
531 .map_err(|_| Error::Data)?;
532 Handle::P384PrivateKey(private_key)
533 },
534 NAMED_CURVE_P521 => {
535 let private_key =
536 p521::SecretKey::from_slice(&d).map_err(|_| Error::Data)?;
537 let mut sec1_bytes = vec![4u8];
538 sec1_bytes.extend_from_slice(&x);
539 sec1_bytes.extend_from_slice(&y);
540 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
541 .map_err(|_| Error::Data)?;
542 NistP521::validate_public_key(&private_key, &encoded_point)
543 .map_err(|_| Error::Data)?;
544 Handle::P521PrivateKey(private_key)
545 },
546 _ => unreachable!(),
547 };
548
549 // Step 2.10.3. Set the [[type]] internal slot of Key to "private".
550 let key_type = KeyType::Private;
551
552 (handle, key_type)
553 },
554 // Otherwise:
555 None => {
556 // Step 2.10.1. If jwk does not meet the requirements of Section 6.2.1 of
557 // JSON Web Algorithms [JWA], then throw a DataError.
558 let x = match jwk.x {
559 Some(x) => Base64UrlUnpadded::decode_vec(&x.str())
560 .map_err(|_| Error::Data)?,
561 None => return Err(Error::Data),
562 };
563 let y = match jwk.y {
564 Some(y) => Base64UrlUnpadded::decode_vec(&y.str())
565 .map_err(|_| Error::Data)?,
566 None => return Err(Error::Data),
567 };
568
569 // Step 2.10.2. Let key be a new CryptoKey object that represents the
570 // Elliptic Curve public key identified by interpreting jwk according to
571 // Section 6.2.1 of JSON Web Algorithms [JWA].
572 // NOTE: CryptoKey is created in Step 2.12 - 2.15.
573 let handle = match named_curve.as_str() {
574 NAMED_CURVE_P256 => {
575 let mut sec1_bytes = vec![4u8];
576 sec1_bytes.extend_from_slice(&x);
577 sec1_bytes.extend_from_slice(&y);
578 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
579 .map_err(|_| Error::Data)?;
580 let public_key =
581 p256::PublicKey::from_encoded_point(&encoded_point)
582 .into_option()
583 .ok_or(Error::Data)?;
584 Handle::P256PublicKey(public_key)
585 },
586 NAMED_CURVE_P384 => {
587 let mut sec1_bytes = vec![4u8];
588 sec1_bytes.extend_from_slice(&x);
589 sec1_bytes.extend_from_slice(&y);
590 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
591 .map_err(|_| Error::Data)?;
592 let public_key =
593 p384::PublicKey::from_encoded_point(&encoded_point)
594 .into_option()
595 .ok_or(Error::Data)?;
596 Handle::P384PublicKey(public_key)
597 },
598 NAMED_CURVE_P521 => {
599 let mut sec1_bytes = vec![4u8];
600 sec1_bytes.extend_from_slice(&x);
601 sec1_bytes.extend_from_slice(&y);
602 let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
603 .map_err(|_| Error::Data)?;
604 let public_key =
605 p521::PublicKey::from_encoded_point(&encoded_point)
606 .into_option()
607 .ok_or(Error::Data)?;
608 Handle::P521PublicKey(public_key)
609 },
610 _ => unreachable!(),
611 };
612
613 // Step 2.10.3. Set the [[type]] internal slot of Key to "public".
614 let key_type = KeyType::Public;
615
616 (handle, key_type)
617 },
618 }
619 }
620 // Otherwise
621 else {
622 // Step 2.10.1. Perform any key import steps defined by other applicable
623 // specifications, passing format, jwk and obtaining key.
624 // Step 2.10.2. If an error occurred or there are no applicable specifications,
625 // throw a DataError.
626 // NOTE: We currently do not support applicable specifications.
627 return Err(Error::NotSupported);
628 };
629
630 // Step 2.11. If the key value is not a valid point on the Elliptic Curve identified by
631 // the namedCurve member of normalizedAlgorithm throw a DataError.
632 // NOTE: Done in Step 2.10.
633
634 // Step 2.12. Let algorithm be a new instance of an EcKeyAlgorithm object.
635 // Step 2.13. Set the name attribute of algorithm to "ECDH".
636 // Step 2.14. Set the namedCurve attribute of algorithm to namedCurve.
637 // Step 2.15. Set the [[algorithm]] internal slot of key to algorithm.
638 let algorithm = SubtleEcKeyAlgorithm {
639 name: ALG_ECDH.to_string(),
640 named_curve,
641 };
642 CryptoKey::new(
643 global,
644 key_type,
645 extractable,
646 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
647 usages,
648 handle,
649 can_gc,
650 )
651 },
652 KeyFormat::Raw => {
653 // Step 2.1. If the namedCurve member of normalizedAlgorithm is not a named curve, then
654 // throw a DataError.
655 if !SUPPORTED_CURVES
656 .iter()
657 .any(|&supported_curve| supported_curve == normalized_algorithm.named_curve)
658 {
659 return Err(Error::Data);
660 }
661
662 // Step 2.2. If usages is not the empty list, then throw a SyntaxError.
663 if !usages.is_empty() {
664 return Err(Error::Syntax(None));
665 }
666
667 // Step 2.3.
668 // If namedCurve is "P-256", "P-384" or "P-521":
669 let handle = if matches!(
670 normalized_algorithm.named_curve.as_str(),
671 NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
672 ) {
673 // Step 2.3.1. Let Q be the Elliptic Curve public key on the curve identified by
674 // the namedCurve member of normalizedAlgorithm identified by performing the
675 // conversion steps defined in Section 2.3.4 of [SEC1] to keyData.
676 // Step 2.3.1. The uncompressed point format MUST be supported.
677 // Step 2.3.1. If the implementation does not support the compressed point format
678 // and a compressed point is provided, throw a DataError.
679 // Step 2.3.1. If a decode error occurs or an identity point is found, throw a
680 // DataError.
681 match normalized_algorithm.named_curve.as_str() {
682 NAMED_CURVE_P256 => {
683 let q =
684 p256::PublicKey::from_sec1_bytes(key_data).map_err(|_| Error::Data)?;
685 Handle::P256PublicKey(q)
686 },
687 NAMED_CURVE_P384 => {
688 let q =
689 p384::PublicKey::from_sec1_bytes(key_data).map_err(|_| Error::Data)?;
690 Handle::P384PublicKey(q)
691 },
692 NAMED_CURVE_P521 => {
693 let q =
694 p521::PublicKey::from_sec1_bytes(key_data).map_err(|_| Error::Data)?;
695 Handle::P521PublicKey(q)
696 },
697 _ => unreachable!(),
698 }
699
700 // Step 2.3.1. Let key be a new CryptoKey that represents Q.
701 // NOTE: CryptoKey is created in Step 2.7 - 2.8.
702 }
703 // Otherwise:
704 else {
705 // Step. 2.3.1. Perform any key import steps defined by other applicable
706 // specifications, passing format, keyData and obtaining key.
707 // Step. 2.3.2. If an error occured or there are no applicable specifications,
708 // throw a DataError.
709 // NOTE: We currently do not support applicable specifications.
710 return Err(Error::NotSupported);
711 };
712
713 // Step 2.4. Let algorithm be a new EcKeyAlgorithm object.
714 // Step 2.5. Set the name attribute of algorithm to "ECDH".
715 // Step 2.6. Set the namedCurve attribute of algorithm to equal the namedCurve member
716 // of normalizedAlgorithm.
717 let algorithm = SubtleEcKeyAlgorithm {
718 name: ALG_ECDH.to_string(),
719 named_curve: normalized_algorithm.named_curve.clone(),
720 };
721
722 // Step 2.7. Set the [[type]] internal slot of key to "public"
723 // Step 2.8. Set the [[algorithm]] internal slot of key to algorithm.
724 CryptoKey::new(
725 global,
726 KeyType::Public,
727 extractable,
728 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
729 usages,
730 handle,
731 can_gc,
732 )
733 },
734 };
735
736 // Step 3. Return key.
737 Ok(key)
738}
739
740/// <https://w3c.github.io/webcrypto/#ecdh-operations-export-key>
741pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
742 // Step 1. Let key be the CryptoKey to be exported.
743
744 // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
745 // slot of key cannot be accessed, then throw an OperationError.
746 // NOTE: Done in Step 3.
747
748 // Step 3.
749 let result = match format {
750 KeyFormat::Spki => {
751 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
752 // InvalidAccessError.
753 if key.Type() != KeyType::Public {
754 return Err(Error::InvalidAccess);
755 }
756
757 // Step 3.2.
758 // Let data be an instance of the SubjectPublicKeyInfo ASN.1 structure defined in
759 // [RFC5280] with the following properties:
760 // * Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the
761 // following properties:
762 // * Set the algorithm field to the OID id-ecPublicKey defined in [RFC5480].
763 // * Set the parameters field to an instance of the ECParameters ASN.1 type
764 // defined in [RFC5480] as follows:
765 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
766 // "P-256", "P-384" or "P-521":
767 // Let keyData be the byte sequence that represents the Elliptic Curve
768 // public key represented by the [[handle]] internal slot of key
769 // according to the encoding rules specified in Section 2.3.3 of [SEC1]
770 // and using the uncompressed form.
771 // If the namedCurve attribute of the [[algorithm]] internal slot
772 // of key is "P-256":
773 // Set parameters to the namedCurve choice with value equal to
774 // the object identifier secp256r1 defined in [RFC5480]
775 // If the namedCurve attribute of the [[algorithm]] internal slot
776 // of key is "P-384":
777 // Set parameters to the namedCurve choice with value equal to
778 // the object identifier secp384r1 defined in [RFC5480]
779 // If the namedCurve attribute of the [[algorithm]] internal slot
780 // of key is "P-521":
781 // Set parameters to the namedCurve choice with value equal to
782 // the object identifier secp521r1 defined in [RFC5480]
783 // Otherwise:
784 // 1. Perform any key export steps defined by other applicable
785 // specifications, passing format and the namedCurve attribute of
786 // the [[algorithm]] internal slot of key and obtaining
787 // namedCurveOid and keyData.
788 // 2. Set parameters to the namedCurve choice with value equal to the
789 // object identifier namedCurveOid.
790 // * Set the subjectPublicKey field to keyData
791 let data = match key.handle() {
792 Handle::P256PublicKey(public_key) => public_key.to_public_key_der(),
793 Handle::P384PublicKey(public_key) => public_key.to_public_key_der(),
794 Handle::P521PublicKey(public_key) => public_key.to_public_key_der(),
795 _ => return Err(Error::Operation),
796 }
797 .map_err(|_| Error::Operation)?;
798
799 ExportedKey::Raw(data.to_vec())
800 },
801 KeyFormat::Pkcs8 => {
802 // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
803 // InvalidAccessError.
804 if key.Type() != KeyType::Private {
805 return Err(Error::InvalidAccess);
806 }
807
808 // Step 3.2.
809 // Let data be an instance of the PrivateKeyInfo ASN.1 structure defined in [RFC5208]
810 // with the following properties:
811 // * Set the version field to 0.
812 // * Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1
813 // type with the following properties:
814 // * Set the algorithm field to the OID id-ecPublicKey defined in [RFC5480].
815 // * Set the parameters field to an instance of the ECParameters ASN.1 type
816 // defined in [RFC5480] as follows:
817 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
818 // "P-256", "P-384" or "P-521":
819 // Let keyData be the result of DER-encoding an instance of the
820 // ECPrivateKey structure defined in Section 3 of [RFC5915] for the
821 // Elliptic Curve private key represented by the [[handle]] internal
822 // slot of key and that conforms to the following:
823 // * The parameters field is present, and is equivalent to the
824 // parameters field of the privateKeyAlgorithm field of this
825 // PrivateKeyInfo ASN.1 structure.
826 // * The publicKey field is present and represents the Elliptic
827 // Curve public key associated with the Elliptic Curve private key
828 // represented by the [[handle]] internal slot of key.
829 // * If the namedCurve attribute of the [[algorithm]] internal slot
830 // of key is "P-256":
831 // Set parameters to the namedCurve choice with value equal to
832 // the object identifier secp256r1 defined in [RFC5480]
833 // * If the namedCurve attribute of the [[algorithm]] internal slot
834 // of key is "P-384":
835 // Set parameters to the namedCurve choice with value equal to
836 // the object identifier secp384r1 defined in [RFC5480]
837 // * If the namedCurve attribute of the [[algorithm]] internal slot
838 // of key is "P-521":
839 // Set parameters to the namedCurve choice with value equal to
840 // the object identifier secp521r1 defined in [RFC5480]
841 // Otherwise:
842 // 1. Perform any key export steps defined by other applicable
843 // specifications, passing format and the namedCurve attribute of
844 // the [[algorithm]] internal slot of key and obtaining
845 // namedCurveOid and keyData.
846 // 2. Set parameters to the namedCurve choice with value equal to the
847 // object identifier namedCurveOid.
848 // * Set the privateKey field to keyData.
849 let data = match key.handle() {
850 Handle::P256PrivateKey(private_key) => private_key.to_pkcs8_der(),
851 Handle::P384PrivateKey(private_key) => private_key.to_pkcs8_der(),
852 Handle::P521PrivateKey(private_key) => private_key.to_pkcs8_der(),
853 _ => return Err(Error::Operation),
854 }
855 .map_err(|_| Error::Operation)?;
856
857 ExportedKey::Raw(data.as_bytes().to_vec())
858 },
859 KeyFormat::Jwk => {
860 // Step 3.1. Let jwk be a new JsonWebKey dictionary.
861 // Step 3.2. Set the kty attribute of jwk to "EC".
862 let mut jwk = JsonWebKey {
863 kty: Some(DOMString::from("EC")),
864 ..Default::default()
865 };
866
867 // Step 3.3.
868 let named_curve =
869 if let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() {
870 algorithm.named_curve.as_str()
871 } else {
872 return Err(Error::Operation);
873 };
874 // If the namedCurve attribute of the [[algorithm]] internal slot of key is "P-256",
875 // "P-384" or "P-521":
876 if matches!(
877 named_curve,
878 NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
879 ) {
880 // Step 3.3.1.
881 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
882 // "P-256":
883 // Set the crv attribute of jwk to "P-256"
884 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
885 // "P-384":
886 // Set the crv attribute of jwk to "P-384"
887 // If the namedCurve attribute of the [[algorithm]] internal slot of key is
888 // "P-521":
889 // Set the crv attribute of jwk to "P-521"
890 jwk.crv = Some(DOMString::from(named_curve));
891
892 // Step 3.3.2. Set the x attribute of jwk according to the definition in Section
893 // 6.2.1.2 of JSON Web Algorithms [JWA].
894 // Step 3.3.3. Set the y attribute of jwk according to the definition in Section
895 // 6.2.1.3 of JSON Web Algorithms [JWA].
896 let (x, y) = match key.handle() {
897 Handle::P256PublicKey(public_key) => {
898 let encoded_point = public_key.to_encoded_point(false);
899 (
900 encoded_point.x().ok_or(Error::Operation)?.to_vec(),
901 encoded_point.y().ok_or(Error::Operation)?.to_vec(),
902 )
903 },
904 Handle::P384PublicKey(public_key) => {
905 let encoded_point = public_key.to_encoded_point(false);
906 (
907 encoded_point.x().ok_or(Error::Operation)?.to_vec(),
908 encoded_point.y().ok_or(Error::Operation)?.to_vec(),
909 )
910 },
911 Handle::P521PublicKey(public_key) => {
912 let encoded_point = public_key.to_encoded_point(false);
913 (
914 encoded_point.x().ok_or(Error::Operation)?.to_vec(),
915 encoded_point.y().ok_or(Error::Operation)?.to_vec(),
916 )
917 },
918 Handle::P256PrivateKey(private_key) => {
919 let public_key = private_key.public_key();
920 let encoded_point = public_key.to_encoded_point(false);
921 (
922 encoded_point.x().ok_or(Error::Operation)?.to_vec(),
923 encoded_point.y().ok_or(Error::Operation)?.to_vec(),
924 )
925 },
926 Handle::P384PrivateKey(private_key) => {
927 let public_key = private_key.public_key();
928 let encoded_point = public_key.to_encoded_point(false);
929 (
930 encoded_point.x().ok_or(Error::Operation)?.to_vec(),
931 encoded_point.y().ok_or(Error::Operation)?.to_vec(),
932 )
933 },
934 Handle::P521PrivateKey(private_key) => {
935 let public_key = private_key.public_key();
936 let encoded_point = public_key.to_encoded_point(false);
937 (
938 encoded_point.x().ok_or(Error::Operation)?.to_vec(),
939 encoded_point.y().ok_or(Error::Operation)?.to_vec(),
940 )
941 },
942 _ => return Err(Error::Operation),
943 };
944 jwk.x = Some(Base64UrlUnpadded::encode_string(&x).into());
945 jwk.y = Some(Base64UrlUnpadded::encode_string(&y).into());
946
947 // Step 3.3.4.
948 // If the [[type]] internal slot of key is "private"
949 // Set the d attribute of jwk according to the definition in Section 6.2.2.1 of
950 // JSON Web Algorithms [JWA].
951 if key.Type() == KeyType::Private {
952 let d = match key.handle() {
953 Handle::P256PrivateKey(private_key) => private_key.to_bytes().to_vec(),
954 Handle::P384PrivateKey(private_key) => private_key.to_bytes().to_vec(),
955 Handle::P521PrivateKey(private_key) => private_key.to_bytes().to_vec(),
956 _ => return Err(Error::NotSupported),
957 };
958 jwk.d = Some(Base64UrlUnpadded::encode_string(&d).into());
959 }
960 }
961 // Otherwise:
962 else {
963 // Step 3.3.1. Perform any key export steps defined by other applicable
964 // specifications, passing format and the namedCurve attribute of the [[algorithm]]
965 // internal slot of key and obtaining namedCurve and a new value of jwk.
966 // Step 3.3.2. Set the crv attribute of jwk to namedCurve.
967 // NOTE: We currently do not support applicable specifications.
968 }
969
970 // Step 3.4. Set the key_ops attribute of jwk to the usages attribute of key.
971 jwk.key_ops = Some(
972 key.usages()
973 .iter()
974 .map(|usage| DOMString::from(usage.as_str()))
975 .collect::<Vec<DOMString>>(),
976 );
977
978 // Step 3.4. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
979 jwk.ext = Some(key.Extractable());
980
981 // Step 3.4. Let result be jwk.
982 ExportedKey::Jwk(Box::new(jwk))
983 },
984 KeyFormat::Raw => {
985 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
986 // InvalidAccessError.
987 if key.Type() != KeyType::Public {
988 return Err(Error::InvalidAccess);
989 }
990
991 // Step 3.2.
992 // If the namedCurve attribute of the [[algorithm]] internal slot of key is "P-256",
993 // "P-384" or "P-521":
994 // Let data be the byte sequence that represents the Elliptic Curve public key
995 // represented by the [[handle]] internal slot of key according to the encoding
996 // rules specified in Section 2.3.3 of [SEC1] and using the uncompressed form.
997 // Otherwise:
998 // Perform any key export steps defined by other applicable specifications, passing
999 // format and the namedCurve attribute of the [[algorithm]] internal slot of key
1000 // and obtaining namedCurve and data.
1001 // NOTE: We currently do not support applicable specifications.
1002 let named_curve =
1003 if let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() {
1004 algorithm.named_curve.as_str()
1005 } else {
1006 return Err(Error::Operation);
1007 };
1008 let data = if matches!(
1009 named_curve,
1010 NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
1011 ) {
1012 match key.handle() {
1013 Handle::P256PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
1014 Handle::P384PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
1015 Handle::P521PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
1016 _ => return Err(Error::Operation),
1017 }
1018 } else {
1019 return Err(Error::NotSupported);
1020 };
1021
1022 // Step 3.3. Let result be data.
1023 ExportedKey::Raw(data)
1024 },
1025 };
1026
1027 // Step 4. Return result.
1028 Ok(result)
1029}