Skip to main content

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