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