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