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