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