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