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