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