script/dom/webcrypto/subtlecrypto/rsa_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 base64ct::{Base64UrlUnpadded, Encoding};
6use crypto_bigint::NonZero;
7use js::context::JSContext;
8use rsa::pkcs8::spki::{DecodePublicKey, EncodePublicKey};
9use rsa::pkcs8::{DecodePrivateKey, EncodePrivateKey};
10use rsa::traits::{PrivateKeyParts, PublicKeyParts};
11use rsa::{BoxedUint, RsaPrivateKey, RsaPublicKey};
12
13use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
14 CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
15};
16use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{
17 AlgorithmIdentifier, JsonWebKey, KeyFormat, RsaOtherPrimesInfo,
18};
19use crate::dom::bindings::error::Error;
20use crate::dom::bindings::root::DomRoot;
21use crate::dom::bindings::str::DOMString;
22use crate::dom::cryptokey::{CryptoKey, Handle, KeyUsageVecHelper};
23use crate::dom::globalscope::GlobalScope;
24use crate::dom::subtlecrypto::{
25 CryptoAlgorithm, DigestOperation, ExportedKey, JsonWebKeyExt, JwkStringField,
26 KeyAlgorithmAndDerivatives, NormalizedAlgorithm, RsaHashedImportParams, RsaHashedKeyAlgorithm,
27 RsaHashedKeyGenParams, normalize_algorithm,
28};
29
30pub(crate) enum RsaAlgorithm {
31 RsassaPkcs1v1_5,
32 RsaPss,
33 RsaOaep,
34}
35
36/// <https://w3c.github.io/webcrypto/#rsassa-pkcs1-operations-generate-key>
37/// <https://w3c.github.io/webcrypto/#rsa-pss-operations-generate-key>
38/// <https://w3c.github.io/webcrypto/#rsa-oaep-operations-generate-key>
39pub(crate) fn generate_key(
40 rsa_algorithm: RsaAlgorithm,
41 cx: &mut JSContext,
42 global: &GlobalScope,
43 normalized_algorithm: &RsaHashedKeyGenParams,
44 extractable: bool,
45 usages: Vec<KeyUsage>,
46) -> Result<CryptoKeyPair, Error> {
47 match rsa_algorithm {
48 RsaAlgorithm::RsassaPkcs1v1_5 | RsaAlgorithm::RsaPss => {
49 // Step 1. If usages contains an entry which is not "sign" or "verify", then throw a
50 // SyntaxError.
51 if usages
52 .iter()
53 .any(|usage| !matches!(usage, KeyUsage::Sign | KeyUsage::Verify))
54 {
55 return Err(Error::Syntax(Some(
56 "Usages contains an entry which is not \"sign\" or \"verify\"".to_string(),
57 )));
58 }
59 },
60 RsaAlgorithm::RsaOaep => {
61 // Step 1. If usages contains an entry which is not "encrypt", "decrypt", "wrapKey" or
62 // "unwrapKey", then throw a SyntaxError.
63 if usages.iter().any(|usage| {
64 !matches!(
65 usage,
66 KeyUsage::Encrypt | KeyUsage::Decrypt | KeyUsage::WrapKey | KeyUsage::UnwrapKey
67 )
68 }) {
69 return Err(Error::Syntax(Some(
70 "Usages contains an entry which is not \"encrypt\", \"decrypt\", \
71 \"wrapKey\" or \"unwrapKey\""
72 .to_string(),
73 )));
74 }
75 },
76 }
77
78 // Step 2. Perform the validate RSA key generation parameters algorithm with
79 // normalizedAlgorithm.
80 normalized_algorithm.validate_parameters()?;
81
82 // Step 3. Generate an RSA key pair, as defined in [RFC3447], with RSA modulus length equal to
83 // the modulusLength attribute of normalizedAlgorithm and RSA public exponent equal to the
84 // publicExponent attribute of normalizedAlgorithm.
85 // Step 4. If generation of the key pair fails, then throw an OperationError.
86 let mut rng = rand::rng();
87 let modulus_length = normalized_algorithm.modulus_length as usize;
88 let public_exponent = BoxedUint::from_be_slice_vartime(&normalized_algorithm.public_exponent);
89 let private_key = RsaPrivateKey::new_with_exp(&mut rng, modulus_length, public_exponent)
90 .map_err(|_| Error::Operation(Some("Failed to generate RSA private key".into())))?;
91 let public_key = private_key.to_public_key();
92
93 // Step 5. Let algorithm be a new RsaHashedKeyAlgorithm dictionary.
94 // Step 7. Set the modulusLength attribute of algorithm to equal the modulusLength attribute of
95 // normalizedAlgorithm.
96 // Step 8. Set the publicExponent attribute of algorithm to equal the publicExponent attribute
97 // of normalizedAlgorithm.
98 // Step 9. Set the hash attribute of algorithm to equal the hash member of normalizedAlgorithm.
99 let algorithm = RsaHashedKeyAlgorithm {
100 name: match rsa_algorithm {
101 // Step 6. Set the name attribute of algorithm to "RSASSA-PKCS1-v1_5".
102 RsaAlgorithm::RsassaPkcs1v1_5 => CryptoAlgorithm::RsassaPkcs1V1_5,
103 // Step 6. Set the name attribute of algorithm to "RSA-PSS".
104 RsaAlgorithm::RsaPss => CryptoAlgorithm::RsaPss,
105 // Step 6. Set the name attribute of algorithm to "RSA-OAEP".
106 RsaAlgorithm::RsaOaep => CryptoAlgorithm::RsaOaep,
107 },
108 modulus_length: normalized_algorithm.modulus_length,
109 public_exponent: normalized_algorithm.public_exponent.clone(),
110 hash: normalized_algorithm.hash.clone(),
111 };
112
113 // Step 10. Let publicKey be a new CryptoKey representing the public key of the generated key
114 // pair.
115 // Step 11. Set the [[type]] internal slot of publicKey to "public"
116 // Step 12. Set the [[algorithm]] internal slot of publicKey to algorithm.
117 // Step 13. Set the [[extractable]] internal slot of publicKey to true.
118 let intersected_usages = match rsa_algorithm {
119 RsaAlgorithm::RsassaPkcs1v1_5 | RsaAlgorithm::RsaPss => {
120 // Step 14. Set the [[usages]] internal slot of publicKey to be the usage intersection
121 // of usages and [ "verify" ].
122 usages.usage_intersection(&[KeyUsage::Verify])
123 },
124 RsaAlgorithm::RsaOaep => {
125 // Step 14. Set the [[usages]] internal slot of publicKey to be the usage intersection
126 // of usages and [ "encrypt", "wrapKey" ].
127 usages.usage_intersection(&[KeyUsage::Encrypt, KeyUsage::WrapKey])
128 },
129 };
130 let public_key = CryptoKey::new(
131 cx,
132 global,
133 KeyType::Public,
134 true,
135 KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm.clone()),
136 intersected_usages,
137 Handle::RsaPublicKey(public_key),
138 );
139
140 // Step 15. Let privateKey be a new CryptoKey representing the private key of the generated key
141 // pair.
142 // Step 16. Set the [[type]] internal slot of privateKey to "private"
143 // Step 17. Set the [[algorithm]] internal slot of privateKey to algorithm.
144 // Step 18. Set the [[extractable]] internal slot of privateKey to extractable.
145 let intersected_usages = match rsa_algorithm {
146 RsaAlgorithm::RsassaPkcs1v1_5 | RsaAlgorithm::RsaPss => {
147 // Step 19. Set the [[usages]] internal slot of privateKey to be the usage intersection
148 // of usages and [ "sign" ].
149 usages.usage_intersection(&[KeyUsage::Sign])
150 },
151 RsaAlgorithm::RsaOaep => {
152 // Step 19. Set the [[usages]] internal slot of privateKey to be the usage intersection
153 // of usages and [ "decrypt", "unwrapKey" ].
154 usages.usage_intersection(&[KeyUsage::Decrypt, KeyUsage::UnwrapKey])
155 },
156 };
157 let private_key = CryptoKey::new(
158 cx,
159 global,
160 KeyType::Private,
161 extractable,
162 KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm),
163 intersected_usages,
164 Handle::RsaPrivateKey(private_key),
165 );
166
167 // Step 20. Let result be a new CryptoKeyPair dictionary.
168 // Step 21. Set the publicKey attribute of result to be publicKey.
169 // Step 22. Set the privateKey attribute of result to be privateKey.
170 let result = CryptoKeyPair {
171 publicKey: Some(public_key),
172 privateKey: Some(private_key),
173 };
174
175 // Step 23. Return result.
176 Ok(result)
177}
178
179/// <https://w3c.github.io/webcrypto/#rsassa-pkcs1-operations-import-key>
180/// <https://w3c.github.io/webcrypto/#rsa-pss-operations-import-key>
181/// <https://w3c.github.io/webcrypto/#rsa-oaep-operations-import-key>
182///
183/// This implementation is based on the specification for RSA-PSS.
184/// When format is "jwk", Step 2.7 in the specification for RSASSA-PKCS1-v1_5 is skipped since it is redundent.
185/// When format is "jwk", Step 2.2 and 2.3 in the specification of RSA-OAEP are combined into a single step.
186#[allow(clippy::too_many_arguments)]
187pub(crate) fn import_key(
188 rsa_algorithm: RsaAlgorithm,
189 cx: &mut JSContext,
190 global: &GlobalScope,
191 normalized_algorithm: &RsaHashedImportParams,
192 format: KeyFormat,
193 key_data: &[u8],
194 extractable: bool,
195 usages: Vec<KeyUsage>,
196) -> Result<DomRoot<CryptoKey>, Error> {
197 // Step 1. Let keyData be the key data to be imported.
198
199 // Step 2.
200 let (key_handle, key_type) = match format {
201 // If format is "spki":
202 KeyFormat::Spki => {
203 match &rsa_algorithm {
204 RsaAlgorithm::RsassaPkcs1v1_5 | RsaAlgorithm::RsaPss => {
205 // Step 2.1. If usages contains an entry which is not "verify" then throw a
206 // SyntaxError.
207 if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
208 return Err(Error::Syntax(Some(
209 "Usages contains an entry which is not \"verify\"".to_string(),
210 )));
211 }
212 },
213 RsaAlgorithm::RsaOaep => {
214 // Step 2.1. If usages contains an entry which is not "encrypt" or "wrapKey",
215 // then throw a SyntaxError.
216 if usages
217 .iter()
218 .any(|usage| !matches!(usage, KeyUsage::Encrypt | KeyUsage::WrapKey))
219 {
220 return Err(Error::Syntax(Some(
221 "Usages contains an entry which is not \"encrypt\" or \"wrapKey\""
222 .to_string(),
223 )));
224 }
225 },
226 }
227
228 // Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
229 // algorithm over keyData.
230 // Step 2.3. If an error occurred while parsing, then throw a DataError.
231 // Step 2.4. If the algorithm object identifier field of the algorithm
232 // AlgorithmIdentifier field of spki is not equal to the rsaEncryption object
233 // identifier defined in [RFC3447], then throw a DataError.
234 // Step 2.5. Let publicKey be the result of performing the parse an ASN.1 structure
235 // algorithm, with data as the subjectPublicKeyInfo field of spki, structure as the
236 // RSAPublicKey structure specified in Section A.1.1 of [RFC3447], and exactData set to
237 // true.
238 // Step 2.6. If an error occurred while parsing, or it can be determined that publicKey
239 // is not a valid public key according to [RFC3447], then throw a DataError.
240 let public_key = RsaPublicKey::from_public_key_der(key_data).map_err(|_| {
241 Error::Data(Some(
242 "Failed to import RSA public key in SPKI format".into(),
243 ))
244 })?;
245
246 // Step 2.7. Let key be a new CryptoKey that represents the RSA public key identified
247 // by publicKey.
248 // Step 2.8. Set the [[type]] internal slot of key to "public"
249 // NOTE: Done in Step 3-8.
250 let key_handle = Handle::RsaPublicKey(public_key);
251 let key_type = KeyType::Public;
252 (key_handle, key_type)
253 },
254 // If format is "pkcs8":
255 KeyFormat::Pkcs8 => {
256 match &rsa_algorithm {
257 RsaAlgorithm::RsassaPkcs1v1_5 | RsaAlgorithm::RsaPss => {
258 // Step 2.1. If usages contains an entry which is not "sign" then throw a
259 // SyntaxError.
260 if usages.iter().any(|usage| *usage != KeyUsage::Sign) {
261 return Err(Error::Syntax(Some(
262 "Usages contains an entry which is not \"sign\"".to_string(),
263 )));
264 }
265 },
266 RsaAlgorithm::RsaOaep => {
267 // Step 2.1. If usages contains an entry which is not "decrypt" or "unwrapKey",
268 // then throw a SyntaxError.
269 if usages
270 .iter()
271 .any(|usage| !matches!(usage, KeyUsage::Decrypt | KeyUsage::UnwrapKey))
272 {
273 return Err(Error::Syntax(Some(
274 "Usages contains an entry which is not \"decrypt\" or \"unwrapKey\""
275 .to_string(),
276 )));
277 }
278 },
279 }
280
281 // Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
282 // algorithm over keyData.
283 // Step 2.3. If an error occurred while parsing, then throw a DataError.
284 // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
285 // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the rsaEncryption object
286 // identifier defined in [RFC3447], then throw a DataError.
287 // Step 2.5. Let rsaPrivateKey be the result of performing the parse an ASN.1 structure
288 // algorithm, with data as the privateKey field of privateKeyInfo, structure as the
289 // RSAPrivateKey structure specified in Section A.1.2 of [RFC3447], and exactData set
290 // to true.
291 // Step 2.6. If an error occurred while parsing, or if rsaPrivateKey is not a valid RSA
292 // private key according to [RFC3447], then throw a DataError.
293 let rsa_private_key = RsaPrivateKey::from_pkcs8_der(key_data).map_err(|_| {
294 Error::Data(Some(
295 "Failed to import RSA private key in PKCS#8 format".into(),
296 ))
297 })?;
298
299 // Step 2.7. Let key be a new CryptoKey that represents the RSA private key identified
300 // by rsaPrivateKey.
301 // Step 2.8. Set the [[type]] internal slot of key to "private"
302 // NOTE: Done in Step 3-8.
303 let key_handle = Handle::RsaPrivateKey(rsa_private_key);
304 let key_type = KeyType::Private;
305 (key_handle, key_type)
306 },
307 // If format is "jwk":
308 KeyFormat::Jwk => {
309 // Step 2.1.
310 // If keyData is a JsonWebKey dictionary:
311 // Let jwk equal keyData.
312 // Otherwise:
313 // Throw a DataError.
314 let jwk = JsonWebKey::parse(cx, key_data)?;
315
316 match &rsa_algorithm {
317 RsaAlgorithm::RsassaPkcs1v1_5 | RsaAlgorithm::RsaPss => {
318 // Step 2.2. If the d field of jwk is present and usages contains an entry
319 // which is not "sign", or, if the d field of jwk is not present and usages
320 // contains an entry which is not "verify" then throw a SyntaxError.
321 if jwk.d.is_some() && usages.iter().any(|usage| *usage != KeyUsage::Sign) {
322 return Err(Error::Syntax(Some(
323 "The d field of jwk is present and usages contains an entry which is \
324 not \"sign\""
325 .to_string(),
326 )));
327 }
328 if jwk.d.is_none() && usages.iter().any(|usage| *usage != KeyUsage::Verify) {
329 return Err(Error::Syntax(Some(
330 "The d field of jwk is not present and usages contains an entry which \
331 is not \"verify\""
332 .to_string(),
333 )));
334 }
335 },
336 RsaAlgorithm::RsaOaep => {
337 // Step 2.2.
338 // * If the d field of jwk is present and usages contains an entry which is not
339 // "decrypt" or "unwrapKey", then throw a SyntaxError.
340 // * If the d field of jwk is not present and usages contains an entry which is
341 // not "encrypt" or "wrapKey", then throw a SyntaxError.
342 if jwk.d.is_some() &&
343 usages.iter().any(|usage| {
344 !matches!(usage, KeyUsage::Decrypt | KeyUsage::UnwrapKey)
345 })
346 {
347 return Err(Error::Syntax(Some(
348 "The d field of jwk is present and usages contains an entry which is \
349 not \"decrypt\" or \"unwrapKey\""
350 .to_string(),
351 )));
352 }
353 if jwk.d.is_none() &&
354 usages
355 .iter()
356 .any(|usage| !matches!(usage, KeyUsage::Encrypt | KeyUsage::WrapKey))
357 {
358 return Err(Error::Syntax(Some(
359 "The d field of jwk is not present and usages contains an entry which \
360 is not \"encrypt\" or \"wrapKey\""
361 .to_string(),
362 )));
363 }
364 },
365 }
366
367 // Step 2.3. If the kty field of jwk is not a case-sensitive string match to "RSA",
368 // then throw a DataError.
369 if jwk.kty.as_ref().is_none_or(|kty| kty != "RSA") {
370 return Err(Error::Data(Some(
371 "The kty field of jwk is not a case-sensitive string match to \"RSA\""
372 .to_string(),
373 )));
374 }
375
376 match &rsa_algorithm {
377 RsaAlgorithm::RsassaPkcs1v1_5 | RsaAlgorithm::RsaPss => {
378 // Step 2.4. If usages is non-empty and the use field of jwk is present and is
379 // not a case-sensitive string match to "sig", then throw a DataError.
380 if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "sig") {
381 return Err(Error::Data(Some(
382 "Usages is non-empty and the use field of jwk is present and \
383 is not a case-sensitive string match to \"sig\""
384 .to_string(),
385 )));
386 }
387 },
388 RsaAlgorithm::RsaOaep => {
389 // Step 2.4. If usages is non-empty and the use field of jwk is present and is
390 // not a case-sensitive string match to "enc", then throw a DataError.
391 if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "enc") {
392 return Err(Error::Data(Some(
393 "Usages is non-empty and the use field of jwk is present and \
394 is not a case-sensitive string match to \"enc\""
395 .to_string(),
396 )));
397 }
398 },
399 }
400
401 // Step 2.5. If the key_ops field of jwk is present, and is invalid according to the
402 // requirements of JSON Web Key [JWK] or does not contain all of the specified usages
403 // values, then throw a DataError.
404 jwk.check_key_ops(&usages)?;
405
406 // Step 2.6. If the ext field of jwk is present and has the value false and extractable
407 // is true, then throw a DataError.
408 if jwk.ext.is_some_and(|ext| !ext) && extractable {
409 return Err(Error::Data(Some(
410 "The ext field of jwk is present and \
411 has the value false and extractable is true"
412 .to_string(),
413 )));
414 }
415
416 let hash = match &rsa_algorithm {
417 RsaAlgorithm::RsassaPkcs1v1_5 => {
418 // Step 2.7.
419 // If the alg field of jwk is not present:
420 // Let hash be undefined.
421 // If the alg field is equal to the string "RS1":
422 // Let hash be the string "SHA-1".
423 // If the alg field is equal to the string "RS256":
424 // Let hash be the string "SHA-256".
425 // If the alg field is equal to the string "RS384":
426 // Let hash be the string "SHA-384".
427 // If the alg field is equal to the string "RS512":
428 // Let hash be the string "SHA-512".
429 // Otherwise:
430 // Perform any key import steps defined by other applicable specifications,
431 // passing format, jwk and obtaining hash.
432 // If an error occurred or there are no applicable specifications, throw a
433 // DataError.
434 match &jwk.alg {
435 None => None,
436 Some(alg) => match &*alg.str() {
437 "RS1" => Some("SHA-1"),
438 "RS256" => Some("SHA-256"),
439 "RS384" => Some("SHA-384"),
440 "RS512" => Some("SHA-512"),
441 _ => None,
442 },
443 }
444 },
445 RsaAlgorithm::RsaPss => {
446 // Step 2.7.
447 // If the alg field of jwk is not present:
448 // Let hash be undefined.
449 // If the alg field is equal to the string "PS1":
450 // Let hash be the string "SHA-1".
451 // If the alg field is equal to the string "PS256":
452 // Let hash be the string "SHA-256".
453 // If the alg field is equal to the string "PS384":
454 // Let hash be the string "SHA-384".
455 // If the alg field is equal to the string "PS512":
456 // Let hash be the string "SHA-512".
457 // Otherwise:
458 // Perform any key import steps defined by other applicable specifications,
459 // passing format, jwk and obtaining hash.
460 // If an error occurred or there are no applicable specifications, throw a
461 // DataError.
462 match &jwk.alg {
463 None => None,
464 Some(alg) => match &*alg.str() {
465 "PS1" => Some("SHA-1"),
466 "PS256" => Some("SHA-256"),
467 "PS384" => Some("SHA-384"),
468 "PS512" => Some("SHA-512"),
469 _ => None,
470 },
471 }
472 },
473 RsaAlgorithm::RsaOaep => {
474 // Step 2.7.
475 // If the alg field of jwk is not present:
476 // Let hash be undefined.
477 // If the alg field of jwk is equal to "RSA-OAEP":
478 // Let hash be the string "SHA-1".
479 // If the alg field of jwk is equal to "RSA-OAEP-256":
480 // Let hash be the string "SHA-256".
481 // If the alg field of jwk is equal to "RSA-OAEP-384":
482 // Let hash be the string "SHA-384".
483 // If the alg field of jwk is equal to "RSA-OAEP-512":
484 // Let hash be the string "SHA-512".
485 // Otherwise:
486 // Perform any key import steps defined by other applicable specifications,
487 // passing format, jwk and obtaining hash.
488 // If an error occurred or there are no applicable specifications, throw a
489 // DataError.
490 match &jwk.alg {
491 None => None,
492 Some(alg) => match &*alg.str() {
493 "RSA-OAEP" => Some("SHA-1"),
494 "RSA-OAEP-256" => Some("SHA-256"),
495 "RSA-OAEP-384" => Some("SHA-384"),
496 "RSA-OAEP-512" => Some("SHA-512"),
497 _ => None,
498 },
499 }
500 },
501 };
502
503 // Step 2.8. If hash is not undefined:
504 if let Some(hash) = hash {
505 // Step 2.8.1. Let normalizedHash be the result of normalize an algorithm with alg
506 // set to hash and op set to digest.
507 let normalized_hash = normalize_algorithm::<DigestOperation>(
508 cx,
509 &AlgorithmIdentifier::String(DOMString::from(hash)),
510 )?;
511
512 // Step 2.8.2. If normalizedHash is not equal to the hash member of
513 // normalizedAlgorithm, throw a DataError.
514 if normalized_hash.name() != normalized_algorithm.hash.name() {
515 return Err(Error::Data(Some(
516 "The normalizedHash is not equal to the hash member of normalizedAlgorithm"
517 .to_string(),
518 )));
519 }
520 }
521
522 // Step 2.9.
523 // If the d field of jwk is present:
524 if jwk.d.is_some() {
525 // Step 2.9.1. If jwk does not meet the requirements of Section 6.3.2 of JSON Web
526 // Algorithms [JWA], then throw a DataError.
527 let n = jwk.decode_required_string_field(JwkStringField::N)?;
528 let e = jwk.decode_required_string_field(JwkStringField::E)?;
529 let d = jwk.decode_required_string_field(JwkStringField::D)?;
530 let p = jwk.decode_optional_string_field(JwkStringField::P)?;
531 let q = jwk.decode_optional_string_field(JwkStringField::Q)?;
532 let dp = jwk.decode_optional_string_field(JwkStringField::DP)?;
533 let dq = jwk.decode_optional_string_field(JwkStringField::DQ)?;
534 let qi = jwk.decode_optional_string_field(JwkStringField::QI)?;
535 let mut primes = match (p, q, dp, dq, qi) {
536 (Some(p), Some(q), Some(_dp), Some(_dq), Some(_qi)) => vec![p, q],
537 (None, None, None, None, None) => Vec::new(),
538 _ => return Err(Error::Data(Some(
539 "The p, q, dp, dq, qi fields of jwk must be either all-present or all-absent"
540 .to_string(),
541 ))),
542 };
543 jwk.decode_primes_from_oth_field(&mut primes)?;
544
545 // Step 2.9.2. Let privateKey represents the RSA private key identified by
546 // interpreting jwk according to Section 6.3.2 of JSON Web Algorithms [JWA].
547 // Step 2.9.3. If privateKey is not a valid RSA private key according to [RFC3447],
548 // then throw a DataError.
549 let private_key = RsaPrivateKey::from_components(
550 BoxedUint::from_be_slice_vartime(&n),
551 BoxedUint::from_be_slice_vartime(&e),
552 BoxedUint::from_be_slice_vartime(&d),
553 primes
554 .into_iter()
555 .map(|prime| BoxedUint::from_be_slice_vartime(&prime))
556 .collect(),
557 )
558 .map_err(|_| {
559 Error::Data(Some(
560 "Failed to construct RSA private key from values in jwk".to_string(),
561 ))
562 })?;
563
564 // Step 2.9.4. Let key be a new CryptoKey object that represents privateKey.
565 // Step 2.9.5. Set the [[type]] internal slot of key to "private"
566 // NOTE: Done in Step 3-8.
567 let key_handle = Handle::RsaPrivateKey(private_key);
568 let key_type = KeyType::Private;
569 (key_handle, key_type)
570 }
571 // Otherwise:
572 else {
573 // Step 2.9.1. If jwk does not meet the requirements of Section 6.3.1 of JSON Web
574 // Algorithms [JWA], then throw a DataError.
575 let n = jwk.decode_required_string_field(JwkStringField::N)?;
576 let e = jwk.decode_required_string_field(JwkStringField::E)?;
577
578 // Step 2.9.2. Let publicKey represent the RSA public key identified by
579 // interpreting jwk according to Section 6.3.1 of JSON Web Algorithms [JWA].
580 // Step 2.9.3. If publicKey can be determined to not be a valid RSA public key
581 // according to [RFC3447], then throw a DataError.
582 let public_key = RsaPublicKey::new(
583 BoxedUint::from_be_slice_vartime(&n),
584 BoxedUint::from_be_slice_vartime(&e),
585 )
586 .map_err(|_| {
587 Error::Data(Some(
588 "Failed to construct RSA public key from values in jwk".into(),
589 ))
590 })?;
591
592 // Step 2.9.4. Let key be a new CryptoKey representing publicKey.
593 // Step 2.9.5. Set the [[type]] internal slot of key to "public"
594 // NOTE: Done in Step 3-8.
595 let key_handle = Handle::RsaPublicKey(public_key);
596 let key_type = KeyType::Public;
597 (key_handle, key_type)
598 }
599 },
600 // Otherwise:
601 _ => {
602 // throw a NotSupportedError.
603 return Err(Error::NotSupported(Some(
604 "Unsupported import key format for RSA key".to_string(),
605 )));
606 },
607 };
608
609 // Step 3. Let algorithm be a new RsaHashedKeyAlgorithm dictionary.
610 // Step 5. Set the modulusLength attribute of algorithm to the length, in bits, of the RSA
611 // public modulus.
612 // Step 6. Set the publicExponent attribute of algorithm to the BigInteger representation of
613 // the RSA public exponent.
614 // Step 7. Set the hash attribute of algorithm to the hash member of normalizedAlgorithm.
615 // Step 8. Set the [[algorithm]] internal slot of key to algorithm
616 let (modulus_length, public_exponent) = match &key_handle {
617 Handle::RsaPrivateKey(private_key) => (
618 private_key.size() as u32 * 8,
619 private_key.e().to_be_bytes_trimmed_vartime().to_vec(),
620 ),
621 Handle::RsaPublicKey(public_key) => (
622 public_key.size() as u32 * 8,
623 public_key.e().to_be_bytes_trimmed_vartime().to_vec(),
624 ),
625 _ => unreachable!(),
626 };
627 let algorithm = RsaHashedKeyAlgorithm {
628 name: match &rsa_algorithm {
629 RsaAlgorithm::RsassaPkcs1v1_5 => {
630 // Step 4. Set the name attribute of algorithm to "RSASSA-PKCS1-v1_5"
631 CryptoAlgorithm::RsassaPkcs1V1_5
632 },
633 RsaAlgorithm::RsaPss => {
634 // Step 4. Set the name attribute of algorithm to "RSA-PSS"
635 CryptoAlgorithm::RsaPss
636 },
637 RsaAlgorithm::RsaOaep => {
638 // Step 4. Set the name attribute of algorithm to "RSA-OAEP"
639 CryptoAlgorithm::RsaOaep
640 },
641 },
642 modulus_length,
643 public_exponent,
644 hash: normalized_algorithm.hash.clone(),
645 };
646 let key = CryptoKey::new(
647 cx,
648 global,
649 key_type,
650 extractable,
651 KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm),
652 usages.normalized_value(),
653 key_handle,
654 );
655
656 // Step 9. Return key.
657 Ok(key)
658}
659
660/// <https://w3c.github.io/webcrypto/#rsassa-pkcs1-operations-export-key>
661/// <https://w3c.github.io/webcrypto/#rsa-pss-operations-export-key>
662/// <https://w3c.github.io/webcrypto/#rsa-oaep-operations-export-key>
663pub(crate) fn export_key(
664 rsa_algorithm: RsaAlgorithm,
665 format: KeyFormat,
666 key: &CryptoKey,
667) -> Result<ExportedKey, Error> {
668 // Step 1. Let key be the key to be exported.
669
670 // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
671 // slot of key cannot be accessed, then throw an OperationError.
672 // NOTE: Done in Step 3.
673
674 // Step 3.
675 let result = match format {
676 // If format is "spki"
677 KeyFormat::Spki => {
678 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
679 // InvalidAccessError.
680 if key.Type() != KeyType::Public {
681 return Err(Error::InvalidAccess(Some(
682 "The [[type]] internal slot of key is not \"public\"".to_string(),
683 )));
684 }
685
686 // Step 3.2.
687 // Let data be an instance of the SubjectPublicKeyInfo ASN.1 structure defined in
688 // [RFC5280] with the following properties:
689 //
690 // Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the following
691 // properties:
692 //
693 // Set the algorithm field to the OID rsaEncryption defined in [RFC3447].
694 //
695 // Set the params field to the ASN.1 type NULL.
696 //
697 // Set the subjectPublicKey field to the result of DER-encoding an RSAPublicKey
698 // ASN.1 type, as defined in [RFC3447], Appendix A.1.1, that represents the RSA
699 // public key represented by the [[handle]] internal slot of key
700 let Handle::RsaPublicKey(public_key) = key.handle() else {
701 return Err(Error::Operation(Some(
702 "The [[handle]] internal slot of key is not an RSA public key".to_string(),
703 )));
704 };
705 let data = public_key.to_public_key_der().map_err(|_| {
706 Error::Operation(Some(
707 "Failed to convert RSA public key to SubjectPublicKeyInfo".to_string(),
708 ))
709 })?;
710
711 // Step 3.3. Let result be the result of DER-encoding data.
712 ExportedKey::new_bytes(data.into_vec())
713 },
714 // If format is "pkcs8":
715 KeyFormat::Pkcs8 => {
716 // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
717 // InvalidAccessError.
718 if key.Type() != KeyType::Private {
719 return Err(Error::InvalidAccess(Some(
720 "The [[type]] internal slot of key is not \"private\"".to_string(),
721 )));
722 }
723
724 // Step 3.2.
725 // Let data be an instance of the PrivateKeyInfo ASN.1 structure defined in [RFC5208]
726 // with the following properties:
727 //
728 // Set the version field to 0.
729 //
730 // Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1 type
731 // with the following properties:
732 //
733 // Set the algorithm field to the OID rsaEncryption defined in [RFC3447].
734 //
735 // Set the params field to the ASN.1 type NULL.
736 //
737 // Set the privateKey field to the result of DER-encoding an RSAPrivateKey ASN.1
738 // type, as defined in [RFC3447], Appendix A.1.2, that represents the RSA private
739 // key represented by the [[handle]] internal slot of key
740 let Handle::RsaPrivateKey(private_key) = key.handle() else {
741 return Err(Error::Operation(Some(
742 "The [[handle]] internal slot of key is not an RSA private key".to_string(),
743 )));
744 };
745 let data = private_key.to_pkcs8_der().map_err(|_| {
746 Error::Operation(Some(
747 "Failed to convert RSA private key to PrivateKeyInfo".to_string(),
748 ))
749 })?;
750
751 // Step 3.3. Let result be the result of DER-encoding data.
752 ExportedKey::new_bytes(data.as_bytes().to_vec())
753 },
754 // If format is "jwk":
755 KeyFormat::Jwk => {
756 // Step 3.1. Let jwk be a new JsonWebKey dictionary.
757 let mut jwk = JsonWebKey::default();
758
759 // Step 3.2. Set the kty attribute of jwk to the string "RSA".
760 jwk.kty = Some(DOMString::from_static("RSA"));
761
762 // Step 3.3. Let hash be the name attribute of the hash attribute of the [[algorithm]]
763 // internal slot of key.
764 let KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm) = key.algorithm()
765 else {
766 return Err(Error::Operation(Some(
767 "The [[algorithm]] internal slot of key is not an RsaHashedKeyAlgorithm"
768 .to_string(),
769 )));
770 };
771 let hash = algorithm.hash.name();
772
773 match rsa_algorithm {
774 RsaAlgorithm::RsassaPkcs1v1_5 => {
775 // Step 3.4.
776 // If hash is "SHA-1":
777 // Set the alg attribute of jwk to the string "RS1".
778 // If hash is "SHA-256":
779 // Set the alg attribute of jwk to the string "RS256".
780 // If hash is "SHA-384":
781 // Set the alg attribute of jwk to the string "RS384".
782 // If hash is "SHA-512":
783 // Set the alg attribute of jwk to the string "RS512".
784 // Otherwise:
785 // Perform any key export steps defined by other applicable specifications,
786 // passing format and the hash attribute of the [[algorithm]] internal slot
787 // of key and obtaining alg.
788 // Set the alg attribute of jwk to alg.
789 let alg = match hash {
790 CryptoAlgorithm::Sha1 => "RS1",
791 CryptoAlgorithm::Sha256 => "RS256",
792 CryptoAlgorithm::Sha384 => "RS384",
793 CryptoAlgorithm::Sha512 => "RS512",
794 _ => {
795 return Err(Error::NotSupported(Some(format!(
796 "Unsupported \"{}\" hash for RSASSA-PKCS1-v1_5",
797 hash.as_str()
798 ))));
799 },
800 };
801 jwk.alg = Some(DOMString::from(alg));
802 },
803 RsaAlgorithm::RsaPss => {
804 // Step 3.4.
805 // If hash is "SHA-1":
806 // Set the alg attribute of jwk to the string "PS1".
807 // If hash is "SHA-256":
808 // Set the alg attribute of jwk to the string "PS256".
809 // If hash is "SHA-384":
810 // Set the alg attribute of jwk to the string "PS384".
811 // If hash is "SHA-512":
812 // Set the alg attribute of jwk to the string "PS512".
813 // Otherwise:
814 // Perform any key export steps defined by other applicable specifications,
815 // passing format and the hash attribute of the [[algorithm]] internal slot
816 // of key and obtaining alg.
817 // Set the alg attribute of jwk to alg.
818 let alg = match hash {
819 CryptoAlgorithm::Sha1 => "PS1",
820 CryptoAlgorithm::Sha256 => "PS256",
821 CryptoAlgorithm::Sha384 => "PS384",
822 CryptoAlgorithm::Sha512 => "PS512",
823 _ => {
824 return Err(Error::NotSupported(Some(format!(
825 "Unsupported \"{}\" hash for RSA-PSS",
826 hash.as_str()
827 ))));
828 },
829 };
830 jwk.alg = Some(DOMString::from(alg));
831 },
832 RsaAlgorithm::RsaOaep => {
833 // Step 3.4.
834 // If hash is "SHA-1":
835 // Set the alg attribute of jwk to the string "RSA-OAEP".
836 // If hash is "SHA-256":
837 // Set the alg attribute of jwk to the string "RSA-OAEP-256".
838 // If hash is "SHA-384":
839 // Set the alg attribute of jwk to the string "RSA-OAEP-384".
840 // If hash is "SHA-512":
841 // Set the alg attribute of jwk to the string "RSA-OAEP-512".
842 // Otherwise:
843 // Perform any key export steps defined by other applicable specifications,
844 // passing format and the hash attribute of the [[algorithm]] internal slot
845 // of key and obtaining alg.
846 // Set the alg attribute of jwk to alg.
847 let alg = match hash {
848 CryptoAlgorithm::Sha1 => "RSA-OAEP",
849 CryptoAlgorithm::Sha256 => "RSA-OAEP-256",
850 CryptoAlgorithm::Sha384 => "RSA-OAEP-384",
851 CryptoAlgorithm::Sha512 => "RSA-OAEP-512",
852 _ => {
853 return Err(Error::NotSupported(Some(format!(
854 "Unsupported \"{}\" hash for RSA-OAEP",
855 hash.as_str()
856 ))));
857 },
858 };
859 jwk.alg = Some(DOMString::from(alg));
860 },
861 }
862
863 // Step 3.5. Set the attributes n and e of jwk according to the corresponding
864 // definitions in JSON Web Algorithms [JWA], Section 6.3.1.
865 let (n, e) = match key.handle() {
866 Handle::RsaPrivateKey(private_key) => (private_key.n(), private_key.e()),
867 Handle::RsaPublicKey(public_key) => (public_key.n(), public_key.e()),
868 _ => {
869 return Err(Error::Operation(Some(
870 "Failed to extract modulus n and public exponent e from RSA key"
871 .to_string(),
872 )));
873 },
874 };
875 jwk.encode_string_field(JwkStringField::N, &n.to_be_bytes_trimmed_vartime());
876 jwk.encode_string_field(JwkStringField::E, &e.to_be_bytes_trimmed_vartime());
877
878 // Step 3.6. If the [[type]] internal slot of key is "private":
879 if key.Type() == KeyType::Private {
880 // Step 3.6.1. Set the attributes named d, p, q, dp, dq, and qi of jwk according to
881 // the corresponding definitions in JSON Web Algorithms [JWA], Section 6.3.2.
882 let Handle::RsaPrivateKey(private_key) = key.handle() else {
883 return Err(Error::Operation(Some(
884 "The [[handle]] internal slot of key is not an RSA private key".to_string(),
885 )));
886 };
887 let mut private_key = private_key.clone();
888 private_key.precompute().map_err(|_| {
889 Error::Operation(Some("Failed to perform RSA pre-computation".to_string()))
890 })?;
891 let primes = private_key.primes();
892 let d = private_key.d();
893 let p = primes.first().ok_or(Error::Operation(Some(
894 "Failed to extract first prime factor p from RSA private key".to_string(),
895 )))?;
896 let q = primes.get(1).ok_or(Error::Operation(Some(
897 "Failed to extract second prime factor q from RSA private key".to_string(),
898 )))?;
899 let dp = private_key.dp().ok_or(Error::Operation(Some(
900 "Failed to extract first factor CRT exponent dp from RSA private key"
901 .to_string(),
902 )))?;
903 let dq = private_key.dq().ok_or(Error::Operation(Some(
904 "Failed to extract second factor CRT exponent dq from RSA private key"
905 .to_string(),
906 )))?;
907 let qi = private_key.crt_coefficient().ok_or(Error::Operation(Some(
908 "Failed to extract first CRT coefficient qi from RSA private key".into(),
909 )))?;
910 jwk.encode_string_field(JwkStringField::D, &d.to_be_bytes_trimmed_vartime());
911 jwk.encode_string_field(JwkStringField::P, &p.to_be_bytes_trimmed_vartime());
912 jwk.encode_string_field(JwkStringField::Q, &q.to_be_bytes_trimmed_vartime());
913 jwk.encode_string_field(JwkStringField::DP, &dp.to_be_bytes_trimmed_vartime());
914 jwk.encode_string_field(JwkStringField::DQ, &dq.to_be_bytes_trimmed_vartime());
915 jwk.encode_string_field(JwkStringField::QI, &qi.to_be_bytes_trimmed_vartime());
916
917 // Step 3.6.2. If the underlying RSA private key represented by the [[handle]]
918 // internal slot of key is represented by more than two primes, set the attribute
919 // named oth of jwk according to the corresponding definition in JSON Web
920 // Algorithms [JWA], Section 6.3.2.7
921 let mut oth = Vec::new();
922 for (i, p_i) in primes.iter().enumerate().skip(2) {
923 // d_i = d mod (p_i - 1)
924 // t_i = (p_1 * p_2 * ... * p_(i-1)) ^ (-1) mod p_i
925 let d_i = private_key.d().rem(
926 &NonZero::new(p_i.wrapping_sub(BoxedUint::one()))
927 .expect("Prime numbers must be greater than one"),
928 );
929 let non_zero_pi =
930 NonZero::new(p_i.clone()).expect("Prime numbers must be non-zero");
931 let t_i = primes
932 .iter()
933 .take(i - 1)
934 .fold(BoxedUint::one(), |product, p_j| {
935 product.mul_mod(p_j, &non_zero_pi)
936 })
937 .invert_mod(&non_zero_pi)
938 .ok_or(Error::Operation(Some(
939 "Failed to compute factor CRT coefficient of other RSA primes".into(),
940 )))?;
941 oth.push(RsaOtherPrimesInfo {
942 r: Some(
943 Base64UrlUnpadded::encode_string(&p_i.to_be_bytes_trimmed_vartime())
944 .into(),
945 ),
946 d: Some(
947 Base64UrlUnpadded::encode_string(&d_i.to_be_bytes_trimmed_vartime())
948 .into(),
949 ),
950 t: Some(
951 Base64UrlUnpadded::encode_string(&t_i.to_be_bytes_trimmed_vartime())
952 .into(),
953 ),
954 });
955 }
956 if !oth.is_empty() {
957 jwk.oth = Some(oth);
958 }
959 }
960
961 // Step 3.7. Set the key_ops attribute of jwk to the usages attribute of key.
962 jwk.set_key_ops(key.usages());
963
964 // Step 3.8. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
965 jwk.ext = Some(key.Extractable());
966
967 // Step 3.9. Let result be jwk.
968 ExportedKey::new_jwk(jwk)
969 },
970 // Otherwise
971 _ => {
972 // throw a NotSupportedError.
973 return Err(Error::NotSupported(Some(
974 "Unsupported export key format for RSA key".to_string(),
975 )));
976 },
977 };
978
979 // Step 4. Return result.
980 Ok(result)
981}
982
983/// <https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-getPublicKey>
984/// Step 9 - 15, for RSA algorithms
985pub(crate) fn get_public_key(
986 rsa_algorithm: RsaAlgorithm,
987 cx: &mut JSContext,
988 global: &GlobalScope,
989 key: &CryptoKey,
990 algorithm: &KeyAlgorithmAndDerivatives,
991 usages: Vec<KeyUsage>,
992) -> Result<DomRoot<CryptoKey>, Error> {
993 // Step 9. If usages contains an entry which is not supported for a public key by the algorithm
994 // identified by algorithm, then throw a SyntaxError.
995 //
996 // NOTE: See "importKey" operation for supported usages
997 match rsa_algorithm {
998 RsaAlgorithm::RsassaPkcs1v1_5 | RsaAlgorithm::RsaPss => {
999 if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
1000 return Err(Error::Syntax(Some(
1001 "Usages contains an entry which is not \"verify\"".to_string(),
1002 )));
1003 }
1004 },
1005 RsaAlgorithm::RsaOaep => {
1006 if usages
1007 .iter()
1008 .any(|usage| !matches!(usage, KeyUsage::Encrypt | KeyUsage::WrapKey))
1009 {
1010 return Err(Error::Syntax(Some(
1011 "Usages contains an entry which is not \"encrypt\" or \"wrapKey\"".to_string(),
1012 )));
1013 }
1014 },
1015 }
1016
1017 // Step 10. Let publicKey be a new CryptoKey representing the public key corresponding to the
1018 // private key represented by the [[handle]] internal slot of key.
1019 // Step 11. If an error occurred, then throw a OperationError.
1020 // Step 12. Set the [[type]] internal slot of publicKey to "public".
1021 // Step 13. Set the [[algorithm]] internal slot of publicKey to algorithm.
1022 // Step 14. Set the [[extractable]] internal slot of publicKey to true.
1023 // Step 15. Set the [[usages]] internal slot of publicKey to usages.
1024 let Handle::RsaPrivateKey(private_key) = key.handle() else {
1025 return Err(Error::Operation(Some(
1026 "[[handle]] internal slot of key is not an RSA private key".to_string(),
1027 )));
1028 };
1029 let public_key = CryptoKey::new(
1030 cx,
1031 global,
1032 KeyType::Public,
1033 true,
1034 algorithm.clone(),
1035 usages,
1036 Handle::RsaPublicKey(private_key.into()),
1037 );
1038
1039 Ok(public_key)
1040}