Skip to main content

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}