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