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