aws_lc_rs/rsa/encryption/
oaep.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0 OR ISC
3
4#![allow(clippy::module_name_repetitions)]
5
6use super::{EncryptionAlgorithmId, PrivateDecryptingKey, PublicEncryptingKey};
7use crate::aws_lc::{
8    EVP_PKEY_CTX_set0_rsa_oaep_label, EVP_PKEY_CTX_set_rsa_mgf1_md, EVP_PKEY_CTX_set_rsa_oaep_md,
9    EVP_PKEY_CTX_set_rsa_padding, EVP_PKEY_decrypt, EVP_PKEY_decrypt_init, EVP_PKEY_encrypt,
10    EVP_PKEY_encrypt_init, EVP_sha1, EVP_sha256, EVP_sha384, EVP_sha512, OPENSSL_malloc, EVP_MD,
11    EVP_PKEY_CTX, RSA_PKCS1_OAEP_PADDING,
12};
13use crate::error::Unspecified;
14use crate::fips::indicator_check;
15use crate::ptr::{DetachableLcPtr, LcPtr};
16use core::fmt::Debug;
17use core::mem::size_of_val;
18use core::ptr::null_mut;
19
20/// RSA-OAEP with SHA1 Hash and SHA1 MGF1
21pub const OAEP_SHA1_MGF1SHA1: OaepAlgorithm = OaepAlgorithm {
22    id: EncryptionAlgorithmId::OaepSha1Mgf1sha1,
23    oaep_hash_fn: EVP_sha1,
24    mgf1_hash_fn: EVP_sha1,
25};
26
27/// RSA-OAEP with SHA256 Hash and SHA256 MGF1
28pub const OAEP_SHA256_MGF1SHA256: OaepAlgorithm = OaepAlgorithm {
29    id: EncryptionAlgorithmId::OaepSha256Mgf1sha256,
30    oaep_hash_fn: EVP_sha256,
31    mgf1_hash_fn: EVP_sha256,
32};
33
34/// RSA-OAEP with SHA384 Hash and SHA384  MGF1
35pub const OAEP_SHA384_MGF1SHA384: OaepAlgorithm = OaepAlgorithm {
36    id: EncryptionAlgorithmId::OaepSha384Mgf1sha384,
37    oaep_hash_fn: EVP_sha384,
38    mgf1_hash_fn: EVP_sha384,
39};
40
41/// RSA-OAEP with SHA512 Hash and SHA512 MGF1
42pub const OAEP_SHA512_MGF1SHA512: OaepAlgorithm = OaepAlgorithm {
43    id: EncryptionAlgorithmId::OaepSha512Mgf1sha512,
44    oaep_hash_fn: EVP_sha512,
45    mgf1_hash_fn: EVP_sha512,
46};
47
48type OaepHashFn = unsafe extern "C" fn() -> *const EVP_MD;
49type Mgf1HashFn = unsafe extern "C" fn() -> *const EVP_MD;
50
51/// An RSA-OAEP algorithm.
52pub struct OaepAlgorithm {
53    id: EncryptionAlgorithmId,
54    oaep_hash_fn: OaepHashFn,
55    mgf1_hash_fn: Mgf1HashFn,
56}
57
58impl OaepAlgorithm {
59    /// Returns the `EncryptionAlgorithmId`.
60    #[must_use]
61    pub fn id(&self) -> EncryptionAlgorithmId {
62        self.id
63    }
64
65    #[inline]
66    fn oaep_hash_fn(&self) -> OaepHashFn {
67        self.oaep_hash_fn
68    }
69
70    #[inline]
71    fn mgf1_hash_fn(&self) -> Mgf1HashFn {
72        self.mgf1_hash_fn
73    }
74}
75
76impl Debug for OaepAlgorithm {
77    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
78        Debug::fmt(&self.id, f)
79    }
80}
81
82/// An RSA-OAEP public key for encryption.
83pub struct OaepPublicEncryptingKey {
84    public_key: PublicEncryptingKey,
85}
86
87impl OaepPublicEncryptingKey {
88    /// Constructs an `OaepPublicEncryptingKey` from a `PublicEncryptingKey`.
89    /// # Errors
90    /// * `Unspecified`: Any error that occurs while attempting to construct an RSA-OAEP public key.
91    pub fn new(public_key: PublicEncryptingKey) -> Result<Self, Unspecified> {
92        Ok(Self { public_key })
93    }
94
95    /// Encrypts the contents in `plaintext` and writes the corresponding ciphertext to `ciphertext`.
96    /// Returns the subslice of `ciphertext` containing the ciphertext output.
97    ///
98    /// # Max Plaintext Length
99    /// The provided length of `plaintext` must be at most [`Self::max_plaintext_size`].
100    ///
101    /// # Sizing `output`
102    /// For `OAEP_SHA1_MGF1SHA1`, `OAEP_SHA256_MGF1SHA256`, `OAEP_SHA384_MGF1SHA384`, `OAEP_SHA512_MGF1SHA512` The
103    /// length of `output` must be greater then or equal to [`Self::ciphertext_size`].
104    ///
105    /// # Errors
106    /// * `Unspecified` for any error that occurs while encrypting `plaintext`.
107    pub fn encrypt<'ciphertext>(
108        &self,
109        algorithm: &'static OaepAlgorithm,
110        plaintext: &[u8],
111        ciphertext: &'ciphertext mut [u8],
112        label: Option<&[u8]>,
113    ) -> Result<&'ciphertext mut [u8], Unspecified> {
114        let mut pkey_ctx = self.public_key.0.create_EVP_PKEY_CTX()?;
115
116        if 1 != unsafe { EVP_PKEY_encrypt_init(*pkey_ctx.as_mut()) } {
117            return Err(Unspecified);
118        }
119
120        configure_oaep_crypto_operation(
121            &mut pkey_ctx,
122            algorithm.oaep_hash_fn(),
123            algorithm.mgf1_hash_fn(),
124            label,
125        )?;
126
127        let mut out_len = ciphertext.len();
128
129        if 1 != indicator_check!(unsafe {
130            EVP_PKEY_encrypt(
131                *pkey_ctx.as_mut(),
132                ciphertext.as_mut_ptr(),
133                &mut out_len,
134                plaintext.as_ptr(),
135                plaintext.len(),
136            )
137        }) {
138            return Err(Unspecified);
139        }
140
141        Ok(&mut ciphertext[..out_len])
142    }
143
144    /// Returns the RSA key size in bytes.
145    #[must_use]
146    pub fn key_size_bytes(&self) -> usize {
147        self.public_key.key_size_bytes()
148    }
149
150    /// Returns the RSA key size in bits.
151    #[must_use]
152    pub fn key_size_bits(&self) -> usize {
153        self.public_key.key_size_bits()
154    }
155
156    /// Returns the max plaintext that could be decrypted using this key and with the provided algorithm.
157    #[must_use]
158    pub fn max_plaintext_size(&self, algorithm: &'static OaepAlgorithm) -> usize {
159        #[allow(unreachable_patterns)]
160        let hash_len: usize = match algorithm.id() {
161            EncryptionAlgorithmId::OaepSha1Mgf1sha1 => 20,
162            EncryptionAlgorithmId::OaepSha256Mgf1sha256 => 32,
163            EncryptionAlgorithmId::OaepSha384Mgf1sha384 => 48,
164            EncryptionAlgorithmId::OaepSha512Mgf1sha512 => 64,
165            _ => unreachable!(),
166        };
167
168        // The RSA-OAEP algorithms we support use the hashing algorithm for the hash and mgf1 functions.
169        self.key_size_bytes() - 2 * hash_len - 2
170    }
171
172    /// Returns the max ciphertext size that will be output by `Self::encrypt`.
173    #[must_use]
174    pub fn ciphertext_size(&self) -> usize {
175        self.key_size_bytes()
176    }
177}
178
179impl Debug for OaepPublicEncryptingKey {
180    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
181        f.debug_struct("OaepPublicEncryptingKey")
182            .finish_non_exhaustive()
183    }
184}
185
186/// An RSA-OAEP private key for decryption.
187pub struct OaepPrivateDecryptingKey {
188    private_key: PrivateDecryptingKey,
189}
190
191impl OaepPrivateDecryptingKey {
192    /// Constructs an `OaepPrivateDecryptingKey` from a `PrivateDecryptingKey`.
193    /// # Errors
194    /// * `Unspecified`: Any error that occurs while attempting to construct an RSA-OAEP public key.
195    pub fn new(private_key: PrivateDecryptingKey) -> Result<Self, Unspecified> {
196        Ok(Self { private_key })
197    }
198
199    /// Decrypts the contents in `ciphertext` and writes the corresponding plaintext to `plaintext`.
200    /// Returns the subslice of `plaintext` containing the plaintext output.
201    ///
202    /// # Max Ciphertext Length
203    /// The provided length of `ciphertext` must be [`Self::key_size_bytes`].
204    ///
205    /// # Sizing `output`
206    /// For `OAEP_SHA1_MGF1SHA1`, `OAEP_SHA256_MGF1SHA256`, `OAEP_SHA384_MGF1SHA384`, `OAEP_SHA512_MGF1SHA512`. The
207    /// length of `output` must be greater then or equal to [`Self::min_output_size`].
208    ///
209    /// # Errors
210    /// * `Unspecified` for any error that occurs while decrypting `ciphertext`.
211    pub fn decrypt<'plaintext>(
212        &self,
213        algorithm: &'static OaepAlgorithm,
214        ciphertext: &[u8],
215        plaintext: &'plaintext mut [u8],
216        label: Option<&[u8]>,
217    ) -> Result<&'plaintext mut [u8], Unspecified> {
218        let mut pkey_ctx = self.private_key.0.create_EVP_PKEY_CTX()?;
219
220        if 1 != unsafe { EVP_PKEY_decrypt_init(*pkey_ctx.as_mut()) } {
221            return Err(Unspecified);
222        }
223
224        configure_oaep_crypto_operation(
225            &mut pkey_ctx,
226            algorithm.oaep_hash_fn(),
227            algorithm.mgf1_hash_fn(),
228            label,
229        )?;
230
231        let mut out_len = plaintext.len();
232
233        if 1 != indicator_check!(unsafe {
234            EVP_PKEY_decrypt(
235                *pkey_ctx.as_mut(),
236                plaintext.as_mut_ptr(),
237                &mut out_len,
238                ciphertext.as_ptr(),
239                ciphertext.len(),
240            )
241        }) {
242            return Err(Unspecified);
243        }
244
245        Ok(&mut plaintext[..out_len])
246    }
247
248    /// Returns the RSA key size in bytes.
249    #[must_use]
250    pub fn key_size_bytes(&self) -> usize {
251        self.private_key.key_size_bytes()
252    }
253
254    /// Returns the RSA key size in bits.
255    #[must_use]
256    pub fn key_size_bits(&self) -> usize {
257        self.private_key.key_size_bits()
258    }
259
260    /// Returns the minimum plaintext buffer size required for `Self::decrypt`.
261    #[must_use]
262    pub fn min_output_size(&self) -> usize {
263        self.key_size_bytes()
264    }
265}
266
267impl Debug for OaepPrivateDecryptingKey {
268    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
269        f.debug_struct("OaepPrivateDecryptingKey")
270            .finish_non_exhaustive()
271    }
272}
273
274fn configure_oaep_crypto_operation(
275    evp_pkey_ctx: &mut LcPtr<EVP_PKEY_CTX>,
276    oaep_hash_fn: OaepHashFn,
277    mgf1_hash_fn: Mgf1HashFn,
278    label: Option<&[u8]>,
279) -> Result<(), Unspecified> {
280    if 1 != unsafe { EVP_PKEY_CTX_set_rsa_padding(*evp_pkey_ctx.as_mut(), RSA_PKCS1_OAEP_PADDING) }
281    {
282        return Err(Unspecified);
283    }
284
285    if 1 != unsafe { EVP_PKEY_CTX_set_rsa_oaep_md(*evp_pkey_ctx.as_mut(), oaep_hash_fn()) } {
286        return Err(Unspecified);
287    }
288
289    if 1 != unsafe { EVP_PKEY_CTX_set_rsa_mgf1_md(*evp_pkey_ctx.as_mut(), mgf1_hash_fn()) } {
290        return Err(Unspecified);
291    }
292
293    let label = label.unwrap_or(&[0u8; 0]);
294
295    if label.is_empty() {
296        // Safety: Don't pass zero-length slice pointers to C code :)
297        if 1 != unsafe { EVP_PKEY_CTX_set0_rsa_oaep_label(*evp_pkey_ctx.as_mut(), null_mut(), 0) } {
298            return Err(Unspecified);
299        }
300        return Ok(());
301    }
302
303    // AWS-LC takes ownership of the label memory, and will call OPENSSL_free, so we are forced to copy it for now.
304    let mut label_ptr =
305        DetachableLcPtr::<u8>::new(unsafe { OPENSSL_malloc(size_of_val(label)) }.cast())?;
306
307    {
308        // memcpy the label data into the AWS-LC allocation
309        let label_ptr =
310            unsafe { core::slice::from_raw_parts_mut(*label_ptr.as_mut(), label.len()) };
311        label_ptr.copy_from_slice(label);
312    }
313
314    if 1 != unsafe {
315        EVP_PKEY_CTX_set0_rsa_oaep_label(*evp_pkey_ctx.as_mut(), *label_ptr, label.len())
316    } {
317        return Err(Unspecified);
318    }
319
320    // AWS-LC owns the allocation now, so we detach it to avoid freeing it here when label_ptr goes out of scope.
321    label_ptr.detach();
322
323    Ok(())
324}