Skip to main content

script/dom/webcrypto/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 js::context::JSContext;
6use rsa::oaep::{DecryptingKey, EncryptingKey};
7use rsa::traits::{Decryptor, RandomizedEncryptor};
8use sha1::Sha1;
9use sha2::{Sha256, Sha384, Sha512};
10
11use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
12    CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
13};
14use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::KeyFormat;
15use crate::dom::bindings::error::Error;
16use crate::dom::bindings::root::DomRoot;
17use crate::dom::cryptokey::{CryptoKey, Handle};
18use crate::dom::globalscope::GlobalScope;
19use crate::dom::subtlecrypto::rsa_common::{self, RsaAlgorithm};
20use crate::dom::subtlecrypto::{
21    CryptoAlgorithm, ExportedKey, KeyAlgorithmAndDerivatives, NormalizedAlgorithm,
22    SubtleRsaHashedImportParams, SubtleRsaHashedKeyGenParams, SubtleRsaOaepParams,
23};
24
25/// <https://w3c.github.io/webcrypto/#rsa-oaep-operations-encrypt>
26pub(crate) fn encrypt(
27    normalized_algorithm: &SubtleRsaOaepParams,
28    key: &CryptoKey,
29    plaintext: &[u8],
30) -> Result<Vec<u8>, Error> {
31    // Step 1. If the [[type]] internal slot of key is not "public", then throw an
32    // InvalidAccessError.
33    if key.Type() != KeyType::Public {
34        return Err(Error::InvalidAccess(Some(
35            "[[type]] internal slot of key is not \"public\"".to_string(),
36        )));
37    }
38
39    // Step 2. Let label be the label member of normalizedAlgorithm or the empty byte sequence if
40    // the label member of normalizedAlgorithm is not present.
41    let label = normalized_algorithm.label.as_deref().unwrap_or_default();
42
43    // Step 3. Perform the encryption operation defined in Section 7.1 of [RFC3447] with the key
44    // represented by key as the recipient's RSA public key, plaintext as the message to be
45    // encrypted, M and label as the label, L, and with the hash function specified by the hash
46    // attribute of the [[algorithm]] internal slot of key as the Hash option and MGF1 (defined in
47    // Section B.2.1 of [RFC3447]) as the MGF option.
48    // Step 4. If performing the operation results in an error, then throw an OperationError.
49    // Step 5. Let ciphertext be the value C that results from performing the operation.
50    let Handle::RsaPublicKey(public_key) = key.handle() else {
51        return Err(Error::Operation(Some(
52            "[[handle]] internal slot of key is not an RSA public key".to_string(),
53        )));
54    };
55    let KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm) = key.algorithm() else {
56        return Err(Error::Operation(Some(
57            "[[algorithm]] internal slot of key is not an RsaHashedKeyAlgorithm".to_string(),
58        )));
59    };
60    let mut rng = rand::rng();
61    let ciphertext = match algorithm.hash.name() {
62        CryptoAlgorithm::Sha1 => {
63            let encryption_key = EncryptingKey::<Sha1>::new_with_label(public_key.clone(), label);
64            encryption_key.encrypt_with_rng(&mut rng, plaintext)
65        },
66        CryptoAlgorithm::Sha256 => {
67            let encryption_key = EncryptingKey::<Sha256>::new_with_label(public_key.clone(), label);
68            encryption_key.encrypt_with_rng(&mut rng, plaintext)
69        },
70        CryptoAlgorithm::Sha384 => {
71            let encryption_key = EncryptingKey::<Sha384>::new_with_label(public_key.clone(), label);
72            encryption_key.encrypt_with_rng(&mut rng, plaintext)
73        },
74        CryptoAlgorithm::Sha512 => {
75            let encryption_key = EncryptingKey::<Sha512>::new_with_label(public_key.clone(), label);
76            encryption_key.encrypt_with_rng(&mut rng, plaintext)
77        },
78        algorithm_hash_name => {
79            return Err(Error::Operation(Some(format!(
80                "Unsupported hash for RSASSA-PKCS1-v1_5: {}",
81                algorithm_hash_name.as_str()
82            ))));
83        },
84    }
85    .map_err(|_| Error::Operation(Some("RSA-OAEP failed to encrypt plaintext".into())))?;
86
87    // Step 6. Return ciphertext.
88    Ok(ciphertext)
89}
90
91/// <https://w3c.github.io/webcrypto/#rsa-oaep-operations-decrypt>
92pub(crate) fn decrypt(
93    normalized_algorithm: &SubtleRsaOaepParams,
94    key: &CryptoKey,
95    ciphertext: &[u8],
96) -> Result<Vec<u8>, Error> {
97    // Step 1. If the [[type]] internal slot of key is not "private", then throw an
98    // InvalidAccessError.
99    if key.Type() != KeyType::Private {
100        return Err(Error::InvalidAccess(Some(
101            "[[type]] internal slot of key is not \"private\"".to_string(),
102        )));
103    }
104
105    // Step 2. Let label be the label member of normalizedAlgorithm or the empty byte sequence if
106    // the label member of normalizedAlgorithm is not present.
107    let label = normalized_algorithm.label.as_deref().unwrap_or_default();
108
109    // Step 3. Perform the decryption operation defined in Section 7.1 of [RFC3447] with the key
110    // represented by key as the recipient's RSA private key, ciphertext as the ciphertext to be
111    // decrypted, C, and label as the label, L, and with the hash function specified by the hash
112    // attribute of the [[algorithm]] internal slot of key as the Hash option and MGF1 (defined in
113    // Section B.2.1 of [RFC3447]) as the MGF option.
114    // Step 4. If performing the operation results in an error, then throw an OperationError.
115    // Step 5. Let plaintext the value M that results from performing the operation.
116    let Handle::RsaPrivateKey(private_key) = key.handle() else {
117        return Err(Error::Operation(Some(
118            "[[handle]] internal slot of key is not an RSA private key".to_string(),
119        )));
120    };
121    let KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm) = key.algorithm() else {
122        return Err(Error::Operation(Some(
123            "[[algorithm]] internal slot of key is not an RsaHashedKeyAlgorithm".to_string(),
124        )));
125    };
126    let plaintext = match algorithm.hash.name() {
127        CryptoAlgorithm::Sha1 => {
128            let decrypting_key = DecryptingKey::<Sha1>::new_with_label(private_key.clone(), label);
129            decrypting_key.decrypt(ciphertext)
130        },
131        CryptoAlgorithm::Sha256 => {
132            let decrypting_key =
133                DecryptingKey::<Sha256>::new_with_label(private_key.clone(), label);
134            decrypting_key.decrypt(ciphertext)
135        },
136        CryptoAlgorithm::Sha384 => {
137            let decrypting_key =
138                DecryptingKey::<Sha384>::new_with_label(private_key.clone(), label);
139            decrypting_key.decrypt(ciphertext)
140        },
141        CryptoAlgorithm::Sha512 => {
142            let decrypting_key =
143                DecryptingKey::<Sha512>::new_with_label(private_key.clone(), label);
144            decrypting_key.decrypt(ciphertext)
145        },
146        algorithm_hash_name => {
147            return Err(Error::Operation(Some(format!(
148                "Unsupported hash for RSASSA-PKCS1-v1_5: {}",
149                algorithm_hash_name.as_str()
150            ))));
151        },
152    }
153    .map_err(|_| Error::Operation(Some("RSA-OAEP failed to decrypt ciphertext".into())))?;
154
155    // Step 6. Return plaintext.
156    Ok(plaintext)
157}
158
159/// <https://w3c.github.io/webcrypto/#rsa-oaep-operations-generate-key>
160pub(crate) fn generate_key(
161    cx: &mut JSContext,
162    global: &GlobalScope,
163    normalized_algorithm: &SubtleRsaHashedKeyGenParams,
164    extractable: bool,
165    usages: Vec<KeyUsage>,
166) -> Result<CryptoKeyPair, Error> {
167    rsa_common::generate_key(
168        RsaAlgorithm::RsaOaep,
169        cx,
170        global,
171        normalized_algorithm,
172        extractable,
173        usages,
174    )
175}
176
177/// <https://w3c.github.io/webcrypto/#rsa-oaep-operations-import-key>
178pub(crate) fn import_key(
179    cx: &mut JSContext,
180    global: &GlobalScope,
181    normalized_algorithm: &SubtleRsaHashedImportParams,
182    format: KeyFormat,
183    key_data: &[u8],
184    extractable: bool,
185    usages: Vec<KeyUsage>,
186) -> Result<DomRoot<CryptoKey>, Error> {
187    rsa_common::import_key(
188        RsaAlgorithm::RsaOaep,
189        cx,
190        global,
191        normalized_algorithm,
192        format,
193        key_data,
194        extractable,
195        usages,
196    )
197}
198
199/// <https://w3c.github.io/webcrypto/#rsa-oaep-operations-export-key>
200pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
201    rsa_common::export_key(RsaAlgorithm::RsaOaep, format, key)
202}
203
204/// <https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-getPublicKey>
205/// Step 9 - 15, for RSA-OAEP
206pub(crate) fn get_public_key(
207    cx: &mut JSContext,
208    global: &GlobalScope,
209    key: &CryptoKey,
210    algorithm: &KeyAlgorithmAndDerivatives,
211    usages: Vec<KeyUsage>,
212) -> Result<DomRoot<CryptoKey>, Error> {
213    rsa_common::get_public_key(RsaAlgorithm::RsaOaep, cx, global, key, algorithm, usages)
214}