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