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