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}