script/dom/subtlecrypto/
rsa_pss_operation.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 pkcs8::der::asn1::BitString;
6use pkcs8::der::{AnyRef, Decode};
7use pkcs8::rand_core::OsRng;
8use pkcs8::{PrivateKeyInfo, SubjectPublicKeyInfo};
9use rsa::pkcs1::{self, DecodeRsaPrivateKey};
10use rsa::pss::{Signature, SigningKey, VerifyingKey};
11use rsa::signature::{RandomizedSigner, SignatureEncoding, Verifier};
12use rsa::traits::PublicKeyParts;
13use rsa::{BigUint, RsaPrivateKey, RsaPublicKey};
14use sha1::Sha1;
15use sha2::{Sha256, Sha384, Sha512};
16
17use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
18    CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
19};
20use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{
21    AlgorithmIdentifier, JsonWebKey, KeyFormat,
22};
23use crate::dom::bindings::error::Error;
24use crate::dom::bindings::root::DomRoot;
25use crate::dom::bindings::str::DOMString;
26use crate::dom::cryptokey::{CryptoKey, Handle};
27use crate::dom::globalscope::GlobalScope;
28use crate::dom::subtlecrypto::rsa_common::{self, RsaAlgorithm};
29use crate::dom::subtlecrypto::{
30    ALG_RSA_PSS, ALG_SHA1, ALG_SHA256, ALG_SHA384, ALG_SHA512, ExportedKey, JsonWebKeyExt,
31    JwkStringField, KeyAlgorithmAndDerivatives, Operation, SubtleRsaHashedImportParams,
32    SubtleRsaHashedKeyAlgorithm, SubtleRsaHashedKeyGenParams, SubtleRsaPssParams,
33    normalize_algorithm,
34};
35use crate::script_runtime::CanGc;
36
37/// <https://w3c.github.io/webcrypto/#rsa-pss-operations-sign>
38pub(crate) fn sign(
39    normalized_algorithm: &SubtleRsaPssParams,
40    key: &CryptoKey,
41    message: &[u8],
42) -> Result<Vec<u8>, Error> {
43    // Step 1. If the [[type]] internal slot of key is not "private", then throw an
44    // InvalidAccessError.
45    if key.Type() != KeyType::Private {
46        return Err(Error::InvalidAccess(Some(
47            "[[type]] internal slot of key is not \"private\"".to_string(),
48        )));
49    }
50
51    // Step 2. Perform the signature generation operation defined in Section 8.1 of [RFC3447] with
52    // the key represented by the [[handle]] internal slot of key as the signer's private key, K,
53    // and message as the message to be signed, M, and using the hash function specified by the
54    // hash attribute of the [[algorithm]] internal slot of key as the Hash option, MGF1 (defined
55    // in Section B.2.1 of [RFC3447]) as the MGF option and the saltLength member of
56    // normalizedAlgorithm as the salt length option for the EMSA-PSS-ENCODE operation.
57    // Step 3. If performing the operation results in an error, then throw an OperationError.
58    // Step 4. Let signature be the signature, S, that results from performing the operation.
59    let Handle::RsaPrivateKey(private_key) = key.handle() else {
60        return Err(Error::Operation(Some(
61            "[[handle]] internal slot of key is not an RSA private key".to_string(),
62        )));
63    };
64    let KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm) = key.algorithm() else {
65        return Err(Error::Operation(Some(
66            "[[algorithm]] internal slot of key is not an RsaHashedKeyAlgorithm".to_string(),
67        )));
68    };
69    let mut rng = OsRng;
70    let signature = match algorithm.hash.name() {
71        ALG_SHA1 => {
72            let signing_key = SigningKey::<Sha1>::new_with_salt_len(
73                private_key.clone(),
74                normalized_algorithm.salt_length as usize,
75            );
76            signing_key.try_sign_with_rng(&mut rng, message)
77        },
78        ALG_SHA256 => {
79            let signing_key = SigningKey::<Sha256>::new_with_salt_len(
80                private_key.clone(),
81                normalized_algorithm.salt_length as usize,
82            );
83            signing_key.try_sign_with_rng(&mut rng, message)
84        },
85        ALG_SHA384 => {
86            let signing_key = SigningKey::<Sha384>::new_with_salt_len(
87                private_key.clone(),
88                normalized_algorithm.salt_length as usize,
89            );
90            signing_key.try_sign_with_rng(&mut rng, message)
91        },
92        ALG_SHA512 => {
93            let signing_key = SigningKey::<Sha512>::new_with_salt_len(
94                private_key.clone(),
95                normalized_algorithm.salt_length as usize,
96            );
97            signing_key.try_sign_with_rng(&mut rng, message)
98        },
99        _ => {
100            return Err(Error::Operation(Some(format!(
101                "Unsupported \"{}\" hash for RSA-PSS",
102                algorithm.hash.name()
103            ))));
104        },
105    }
106    .map_err(|_| Error::Operation(Some("RSA-PSS failed to sign message".to_string())))?;
107
108    // Step 5. Return signature.
109    Ok(signature.to_vec())
110}
111
112/// <https://w3c.github.io/webcrypto/#rsa-pss-operations-verify>
113pub(crate) fn verify(
114    normalized_algorithm: &SubtleRsaPssParams,
115    key: &CryptoKey,
116    message: &[u8],
117    signature: &[u8],
118) -> Result<bool, Error> {
119    // Step 1. If the [[type]] internal slot of key is not "public", then throw an
120    // InvalidAccessError.
121    if key.Type() != KeyType::Public {
122        return Err(Error::InvalidAccess(Some(
123            "[[type]] internal slot of key is not \"public\"".to_string(),
124        )));
125    }
126
127    // Step 2. Perform the signature verification operation defined in Section 8.1 of [RFC3447]
128    // with the key represented by the [[handle]] internal slot of key as the signer's RSA public
129    // key and message as M and signature as S and using the hash function specified by the hash
130    // attribute of the [[algorithm]] internal slot of key as the Hash option, MGF1 (defined in
131    // Section B.2.1 of [RFC3447]) as the MGF option and the saltLength member of
132    // normalizedAlgorithm as the salt length option for the EMSA-PSS-VERIFY operation.
133    // Step 3. Let result be a boolean with the value true if the result of the operation was
134    // "valid signature" and the value false otherwise.
135    let Handle::RsaPublicKey(public_key) = key.handle() else {
136        return Err(Error::Operation(Some(
137            "[[handle]] internal slot of key is not an RSA public key".to_string(),
138        )));
139    };
140    let KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm) = key.algorithm() else {
141        return Err(Error::Operation(Some(
142            "[[algorithm]] internal slot of key is not an RsaHashedKeyAlgorithm".to_string(),
143        )));
144    };
145    let signature = Signature::try_from(signature)
146        .map_err(|_| Error::Operation(Some("Failed to parse RSA signature".to_string())))?;
147    let result = match algorithm.hash.name() {
148        ALG_SHA1 => {
149            let verifying_key = VerifyingKey::<Sha1>::new_with_salt_len(
150                public_key.clone(),
151                normalized_algorithm.salt_length as usize,
152            );
153            verifying_key.verify(message, &signature)
154        },
155        ALG_SHA256 => {
156            let verifying_key = VerifyingKey::<Sha256>::new_with_salt_len(
157                public_key.clone(),
158                normalized_algorithm.salt_length as usize,
159            );
160            verifying_key.verify(message, &signature)
161        },
162        ALG_SHA384 => {
163            let verifying_key = VerifyingKey::<Sha384>::new_with_salt_len(
164                public_key.clone(),
165                normalized_algorithm.salt_length as usize,
166            );
167            verifying_key.verify(message, &signature)
168        },
169        ALG_SHA512 => {
170            let verifying_key = VerifyingKey::<Sha512>::new_with_salt_len(
171                public_key.clone(),
172                normalized_algorithm.salt_length as usize,
173            );
174            verifying_key.verify(message, &signature)
175        },
176        _ => {
177            return Err(Error::Operation(Some(format!(
178                "Unsupported \"{}\" hash for RSASSA-PKCS1-v1_5",
179                algorithm.hash.name()
180            ))));
181        },
182    }
183    .is_ok();
184
185    // Step 4. Return result.
186    Ok(result)
187}
188
189/// <https://w3c.github.io/webcrypto/#rsa-pss-operations-generate-key>
190pub(crate) fn generate_key(
191    global: &GlobalScope,
192    normalized_algorithm: &SubtleRsaHashedKeyGenParams,
193    extractable: bool,
194    usages: Vec<KeyUsage>,
195    can_gc: CanGc,
196) -> Result<CryptoKeyPair, Error> {
197    rsa_common::generate_key(
198        RsaAlgorithm::RsaPss,
199        global,
200        normalized_algorithm,
201        extractable,
202        usages,
203        can_gc,
204    )
205}
206
207/// <https://w3c.github.io/webcrypto/#rsa-pss-operations-import-key>
208pub(crate) fn import_key(
209    global: &GlobalScope,
210    normalized_algorithm: &SubtleRsaHashedImportParams,
211    format: KeyFormat,
212    key_data: &[u8],
213    extractable: bool,
214    usages: Vec<KeyUsage>,
215    can_gc: CanGc,
216) -> Result<DomRoot<CryptoKey>, Error> {
217    // Step 1. Let keyData be the key data to be imported.
218
219    // Step 2.
220    let (key_handle, key_type) = match format {
221        // If format is "spki":
222        KeyFormat::Spki => {
223            // Step 2.1. If usages contains an entry which is not "verify" then throw a
224            // SyntaxError.
225            if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
226                return Err(Error::Syntax(Some(
227                    "Usages contains an entry which is not \"verify\"".to_string(),
228                )));
229            }
230
231            // Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
232            // algorithm over keyData.
233            // Step 2.3. If an error occurred while parsing, then throw a DataError.
234            let spki =
235                SubjectPublicKeyInfo::<AnyRef, BitString>::from_der(key_data).map_err(|_| {
236                    Error::Data(Some(
237                        "Fail to parse SubjectPublicKeyInfo over keyData".to_string(),
238                    ))
239                })?;
240
241            // Step 2.4. If the algorithm object identifier field of the algorithm
242            // AlgorithmIdentifier field of spki is not equal to the rsaEncryption object
243            // identifier defined in [RFC3447], then throw a DataError.
244            if spki.algorithm.oid != pkcs1::ALGORITHM_OID {
245                return Err(Error::Data(Some(
246                    "Algorithm object identifier of spki is not an rsaEncryption".to_string(),
247                )));
248            }
249
250            // Step 2.5. Let publicKey be the result of performing the parse an ASN.1 structure
251            // algorithm, with data as the subjectPublicKeyInfo field of spki, structure as the
252            // RSAPublicKey structure specified in Section A.1.1 of [RFC3447], and exactData set to
253            // true.
254            // Step 2.6. If an error occurred while parsing, or it can be determined that publicKey
255            // is not a valid public key according to [RFC3447], then throw a DataError.
256            let pkcs1_bytes = spki.subject_public_key.as_bytes().ok_or(Error::Data(Some(
257                "Fail to parse byte sequence over SubjectPublicKey field of spki".to_string(),
258            )))?;
259            let rsa_public_key_structure =
260                pkcs1::RsaPublicKey::try_from(pkcs1_bytes).map_err(|_| {
261                    Error::Data(Some(
262                        "SubjectPublicKey field of spki is not an RSAPublicKey structure"
263                            .to_string(),
264                    ))
265                })?;
266            let n = BigUint::from_bytes_be(rsa_public_key_structure.modulus.as_bytes());
267            let e = BigUint::from_bytes_be(rsa_public_key_structure.public_exponent.as_bytes());
268            let public_key = RsaPublicKey::new(n, e).map_err(|_| {
269                Error::Data(Some(
270                    "Fail to construct RSA public key from modulus and public exponent".to_string(),
271                ))
272            })?;
273
274            // Step 2.7. Let key be a new CryptoKey that represents the RSA public key identified
275            // by publicKey.
276            // Step 2.8. Set the [[type]] internal slot of key to "public"
277            // NOTE: Done in Step 3-8.
278            let key_handle = Handle::RsaPublicKey(public_key);
279            let key_type = KeyType::Public;
280            (key_handle, key_type)
281        },
282        // If format is "pkcs8":
283        KeyFormat::Pkcs8 => {
284            // Step 2.1. If usages contains an entry which is not "sign" then throw a SyntaxError.
285            if usages.iter().any(|usage| *usage != KeyUsage::Sign) {
286                return Err(Error::Syntax(Some(
287                    "Usages contains an entry which is not \"sign\"".to_string(),
288                )));
289            }
290
291            // Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
292            // algorithm over keyData.
293            // Step 2.3. If an error occurred while parsing, then throw a DataError.
294            let private_key_info = PrivateKeyInfo::from_der(key_data).map_err(|_| {
295                Error::Data(Some(
296                    "Fail to parse PrivateKeyInfo over keyData".to_string(),
297                ))
298            })?;
299
300            // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
301            // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the rsaEncryption object
302            // identifier defined in [RFC3447], then throw a DataError.
303            if private_key_info.algorithm.oid != pkcs1::ALGORITHM_OID {
304                return Err(Error::Data(Some(
305                    "Algorithm object identifier of PrivateKeyInfo is not an rsaEncryption"
306                        .to_string(),
307                )));
308            }
309
310            // Step 2.5. Let rsaPrivateKey be the result of performing the parse an ASN.1 structure
311            // algorithm, with data as the privateKey field of privateKeyInfo, structure as the
312            // RSAPrivateKey structure specified in Section A.1.2 of [RFC3447], and exactData set
313            // to true.
314            // Step 2.6. If an error occurred while parsing, or if rsaPrivateKey is not a valid RSA
315            // private key according to [RFC3447], then throw a DataError.
316            let rsa_private_key = RsaPrivateKey::from_pkcs1_der(private_key_info.private_key)
317                .map_err(|_| {
318                    Error::Data(Some(
319                        "PrivateKey field of PrivateKeyInfo is not an RSAPrivateKey structure"
320                            .to_string(),
321                    ))
322                })?;
323
324            // Step 2.7. Let key be a new CryptoKey that represents the RSA private key identified
325            // by rsaPrivateKey.
326            // Step 2.8. Set the [[type]] internal slot of key to "private"
327            // NOTE: Done in Step 3-8.
328            let key_handle = Handle::RsaPrivateKey(rsa_private_key);
329            let key_type = KeyType::Private;
330            (key_handle, key_type)
331        },
332        // If format is "jwk":
333        KeyFormat::Jwk => {
334            // Step 2.1.
335            // If keyData is a JsonWebKey dictionary:
336            //     Let jwk equal keyData.
337            // Otherwise:
338            //     Throw a DataError.
339            let cx = GlobalScope::get_cx();
340            let jwk = JsonWebKey::parse(cx, key_data)?;
341
342            // Step 2.2. If the d field of jwk is present and usages contains an entry which is not
343            // "sign", or, if the d field of jwk is not present and usages contains an entry which
344            // is not "verify" then throw a SyntaxError.
345            if jwk.d.is_some() && usages.iter().any(|usage| *usage != KeyUsage::Sign) {
346                return Err(Error::Syntax(Some(
347                    "The d field of jwk is present and usages contains an entry which is not \"sign\""
348                        .to_string()
349                )));
350            }
351            if jwk.d.is_none() && usages.iter().any(|usage| *usage != KeyUsage::Verify) {
352                return Err(Error::Syntax(Some(
353                    "The d field of jwk is not present and usages contains an entry which is not \"verify\""
354                        .to_string()
355                )));
356            }
357
358            // Step 2.3. If the kty field of jwk is not a case-sensitive string match to "RSA",
359            // then throw a DataError.
360            if jwk.kty.as_ref().is_none_or(|kty| kty != "RSA") {
361                return Err(Error::Data(Some(
362                    "The kty field of jwk is not a case-sensitive string match to \"RSA\""
363                        .to_string(),
364                )));
365            }
366
367            // Step 2.4. If usages is non-empty and the use field of jwk is present and is not a
368            // case-sensitive string match to "sig", then throw a DataError.
369            if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "sig") {
370                return Err(Error::Data(Some(
371                    "Usages is non-empty and the use field of jwk is present and \
372                    is not a case-sensitive string match to \"sig\""
373                        .to_string(),
374                )));
375            }
376
377            // Step 2.5. If the key_ops field of jwk is present, and is invalid according to the
378            // requirements of JSON Web Key [JWK] or does not contain all of the specified usages
379            // values, then throw a DataError.
380            jwk.check_key_ops(&usages)?;
381
382            // Step 2.6. If the ext field of jwk is present and has the value false and extractable
383            // is true, then throw a DataError.
384            if jwk.ext.is_some_and(|ext| !ext) && extractable {
385                return Err(Error::Data(Some(
386                    "The ext field of jwk is present and \
387                    has the value false and extractable is true"
388                        .to_string(),
389                )));
390            }
391
392            // Step 2.7.
393            // If the alg field of jwk is not present:
394            //     Let hash be undefined.
395            // If the alg field is equal to the string "PS1":
396            //     Let hash be the string "SHA-1".
397            // If the alg field is equal to the string "PS256":
398            //     Let hash be the string "SHA-256".
399            // If the alg field is equal to the string "PS384":
400            //     Let hash be the string "SHA-384".
401            // If the alg field is equal to the string "PS512":
402            //     Let hash be the string "SHA-512".
403            // Otherwise:
404            //     Perform any key import steps defined by other applicable specifications, passing
405            //     format, jwk and obtaining hash.
406            //     If an error occurred or there are no applicable specifications, throw a DataError.
407            let hash = match &jwk.alg {
408                None => None,
409                Some(alg) => match &*alg.str() {
410                    "PS1" => Some(ALG_SHA1),
411                    "PS256" => Some(ALG_SHA256),
412                    "PS384" => Some(ALG_SHA384),
413                    "PS512" => Some(ALG_SHA512),
414                    _ => None,
415                },
416            };
417
418            // Step 2.8. If hash is not undefined:
419            if let Some(hash) = hash {
420                // Step 2.8.1. Let normalizedHash be the result of normalize an algorithm with alg
421                // set to hash and op set to digest.
422                let normalized_hash = normalize_algorithm(
423                    cx,
424                    Operation::Digest,
425                    &AlgorithmIdentifier::String(DOMString::from(hash)),
426                    can_gc,
427                )?;
428
429                // Step 2.8.2. If normalizedHash is not equal to the hash member of
430                // normalizedAlgorithm, throw a DataError.
431                if normalized_hash.name() != normalized_algorithm.hash.name() {
432                    return Err(Error::Data(Some(
433                        "The normalizedHash is not equal to the hash member of normalizedAlgorithm"
434                            .to_string(),
435                    )));
436                }
437            }
438
439            // Step 2.9.
440            // If the d field of jwk is present:
441            if jwk.d.is_some() {
442                // Step 2.9.1. If jwk does not meet the requirements of Section 6.3.2 of JSON Web
443                // Algorithms [JWA], then throw a DataError.
444                let n = jwk.decode_required_string_field(JwkStringField::N)?;
445                let e = jwk.decode_required_string_field(JwkStringField::E)?;
446                let d = jwk.decode_required_string_field(JwkStringField::D)?;
447                let p = jwk.decode_optional_string_field(JwkStringField::P)?;
448                let q = jwk.decode_optional_string_field(JwkStringField::Q)?;
449                let dp = jwk.decode_optional_string_field(JwkStringField::DP)?;
450                let dq = jwk.decode_optional_string_field(JwkStringField::DQ)?;
451                let qi = jwk.decode_optional_string_field(JwkStringField::QI)?;
452                let mut primes = match (p, q, dp, dq, qi) {
453                    (Some(p), Some(q), Some(_dp), Some(_dq), Some(_qi)) => vec![p, q],
454                    (None, None, None, None, None) => Vec::new(),
455                    _ => return Err(Error::Data(Some(
456                        "The p, q, dp, dq, qi fields of jwk must be either all-present or all-absent"
457                            .to_string()
458                    ))),
459                };
460                jwk.decode_primes_from_oth_field(&mut primes)?;
461
462                // Step 2.9.2. Let privateKey represents the RSA private key identified by
463                // interpreting jwk according to Section 6.3.2 of JSON Web Algorithms [JWA].
464                // Step 2.9.3. If privateKey is not a valid RSA private key according to [RFC3447],
465                // then throw a DataError.
466                let private_key = RsaPrivateKey::from_components(
467                    BigUint::from_bytes_be(&n),
468                    BigUint::from_bytes_be(&e),
469                    BigUint::from_bytes_be(&d),
470                    primes
471                        .into_iter()
472                        .map(|prime| BigUint::from_bytes_be(&prime))
473                        .collect(),
474                )
475                .map_err(|_| {
476                    Error::Data(Some(
477                        "Failed to construct RSA private key from values in jwk".to_string(),
478                    ))
479                })?;
480
481                // Step 2.9.4. Let key be a new CryptoKey object that represents privateKey.
482                // Step 2.9.5. Set the [[type]] internal slot of key to "private"
483                // NOTE: Done in Step 3-8.
484                let key_handle = Handle::RsaPrivateKey(private_key);
485                let key_type = KeyType::Private;
486                (key_handle, key_type)
487            }
488            // Otherwise:
489            else {
490                // Step 2.9.1. If jwk does not meet the requirements of Section 6.3.1 of JSON Web
491                // Algorithms [JWA], then throw a DataError.
492                let n = jwk.decode_required_string_field(JwkStringField::N)?;
493                let e = jwk.decode_required_string_field(JwkStringField::E)?;
494
495                // Step 2.9.2. Let publicKey represent the RSA public key identified by
496                // interpreting jwk according to Section 6.3.1 of JSON Web Algorithms [JWA].
497                // Step 2.9.3. If publicKey can be determined to not be a valid RSA public key
498                // according to [RFC3447], then throw a DataError.
499                let public_key =
500                    RsaPublicKey::new(BigUint::from_bytes_be(&n), BigUint::from_bytes_be(&e))
501                        .map_err(|_| {
502                            Error::Data(Some(
503                                "Failed to construct RSA public key from values in jwk".to_string(),
504                            ))
505                        })?;
506
507                // Step 2.9.4. Let key be a new CryptoKey representing publicKey.
508                // Step 2.9.5. Set the [[type]] internal slot of key to "public"
509                // NOTE: Done in Step 3-8.
510                let key_handle = Handle::RsaPublicKey(public_key);
511                let key_type = KeyType::Public;
512                (key_handle, key_type)
513            }
514        },
515        // Otherwise:
516        _ => {
517            // throw a NotSupportedError.
518            return Err(Error::NotSupported(Some(
519                "Unsupported import key format for RSA key".to_string(),
520            )));
521        },
522    };
523
524    // Step 3. Let algorithm be a new RsaHashedKeyAlgorithm dictionary.
525    // Step 4. Set the name attribute of algorithm to "RSA-PSS"
526    // Step 5. Set the modulusLength attribute of algorithm to the length, in bits, of the RSA
527    // public modulus.
528    // Step 6. Set the publicExponent attribute of algorithm to the BigInteger representation of
529    // the RSA public exponent.
530    // Step 7. Set the hash attribute of algorithm to the hash member of normalizedAlgorithm.
531    // Step 8. Set the [[algorithm]] internal slot of key to algorithm
532    let (modulus_length, public_exponent) = match &key_handle {
533        Handle::RsaPrivateKey(private_key) => {
534            (private_key.size() as u32 * 8, private_key.e().to_bytes_be())
535        },
536        Handle::RsaPublicKey(public_key) => {
537            (public_key.size() as u32 * 8, public_key.e().to_bytes_be())
538        },
539        _ => unreachable!(),
540    };
541    let algorithm = SubtleRsaHashedKeyAlgorithm {
542        name: ALG_RSA_PSS.to_string(),
543        modulus_length,
544        public_exponent,
545        hash: normalized_algorithm.hash.clone(),
546    };
547    let key = CryptoKey::new(
548        global,
549        key_type,
550        extractable,
551        KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm),
552        usages,
553        key_handle,
554        can_gc,
555    );
556
557    // Step 9. Return key.
558    Ok(key)
559}
560
561/// <https://w3c.github.io/webcrypto/#rsa-pss-operations-export-key>
562pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
563    rsa_common::export_key(RsaAlgorithm::RsaPss, format, key)
564}