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