aws_lc_rs/rsa/
key.rs

1// Copyright 2015-2016 Brian Smith.
2// SPDX-License-Identifier: ISC
3// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4// SPDX-License-Identifier: Apache-2.0 OR ISC
5use super::signature::{RsaEncoding, RsaPadding};
6use super::{encoding, RsaParameters};
7#[cfg(feature = "fips")]
8use crate::aws_lc::RSA;
9use crate::aws_lc::{
10    EVP_PKEY_CTX_set_rsa_keygen_bits, EVP_PKEY_assign_RSA, EVP_PKEY_new, RSA_new, RSA_set0_key,
11    RSA_size, EVP_PKEY, EVP_PKEY_RSA, EVP_PKEY_RSA_PSS,
12};
13#[cfg(feature = "ring-io")]
14use crate::aws_lc::{RSA_get0_e, RSA_get0_n};
15use crate::encoding::{AsDer, Pkcs8V1Der};
16use crate::error::{KeyRejected, Unspecified};
17#[cfg(feature = "ring-io")]
18use crate::io;
19#[cfg(feature = "ring-io")]
20use crate::ptr::ConstPointer;
21use crate::ptr::{DetachableLcPtr, LcPtr};
22use crate::rsa::PublicEncryptingKey;
23use crate::sealed::Sealed;
24use crate::{hex, rand};
25#[cfg(feature = "fips")]
26use aws_lc::RSA_check_fips;
27use core::fmt::{self, Debug, Formatter};
28use core::ptr::null_mut;
29
30// TODO: Uncomment when MSRV >= 1.64
31// use core::ffi::c_int;
32use std::os::raw::c_int;
33
34use crate::pkcs8::Version;
35use crate::rsa::signature::configure_rsa_pkcs1_pss_padding;
36#[cfg(feature = "ring-io")]
37use untrusted::Input;
38use zeroize::Zeroize;
39
40/// RSA key-size.
41#[allow(clippy::module_name_repetitions)]
42#[non_exhaustive]
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub enum KeySize {
45    /// 2048-bit key
46    Rsa2048,
47
48    /// 3072-bit key
49    Rsa3072,
50
51    /// 4096-bit key
52    Rsa4096,
53
54    /// 8192-bit key
55    Rsa8192,
56}
57
58#[allow(clippy::len_without_is_empty)]
59impl KeySize {
60    /// Returns the size of the key in bytes.
61    #[inline]
62    #[must_use]
63    pub fn len(self) -> usize {
64        match self {
65            Self::Rsa2048 => 256,
66            Self::Rsa3072 => 384,
67            Self::Rsa4096 => 512,
68            Self::Rsa8192 => 1024,
69        }
70    }
71
72    /// Returns the key size in bits.
73    #[inline]
74    pub(super) fn bits(self) -> i32 {
75        match self {
76            Self::Rsa2048 => 2048,
77            Self::Rsa3072 => 3072,
78            Self::Rsa4096 => 4096,
79            Self::Rsa8192 => 8192,
80        }
81    }
82}
83
84/// An RSA key pair, used for signing.
85#[allow(clippy::module_name_repetitions)]
86pub struct KeyPair {
87    // https://github.com/aws/aws-lc/blob/ebaa07a207fee02bd68fe8d65f6b624afbf29394/include/openssl/evp.h#L295
88    // An |EVP_PKEY| object represents a public or private RSA key. A given object may be
89    // used concurrently on multiple threads by non-mutating functions, provided no
90    // other thread is concurrently calling a mutating function. Unless otherwise
91    // documented, functions which take a |const| pointer are non-mutating and
92    // functions which take a non-|const| pointer are mutating.
93    pub(super) evp_pkey: LcPtr<EVP_PKEY>,
94    pub(super) serialized_public_key: PublicKey,
95}
96
97impl Sealed for KeyPair {}
98unsafe impl Send for KeyPair {}
99unsafe impl Sync for KeyPair {}
100
101impl KeyPair {
102    fn new(evp_pkey: LcPtr<EVP_PKEY>) -> Result<Self, KeyRejected> {
103        KeyPair::validate_private_key(&evp_pkey)?;
104        let serialized_public_key = PublicKey::new(&evp_pkey)?;
105        Ok(KeyPair {
106            evp_pkey,
107            serialized_public_key,
108        })
109    }
110
111    /// Generate a RSA `KeyPair` of the specified key-strength.
112    ///
113    /// Supports the following key sizes:
114    /// * `KeySize::Rsa2048`
115    /// * `KeySize::Rsa3072`
116    /// * `KeySize::Rsa4096`
117    /// * `KeySize::Rsa8192`
118    ///
119    /// # Errors
120    /// * `Unspecified`: Any key generation failure.
121    pub fn generate(size: KeySize) -> Result<Self, Unspecified> {
122        let private_key = generate_rsa_key(size.bits())?;
123        Ok(Self::new(private_key)?)
124    }
125
126    /// Generate a RSA `KeyPair` of the specified key-strength.
127    ///
128    /// ## Deprecated
129    /// This is equivalent to `KeyPair::generate`.
130    ///
131    /// # Errors
132    /// * `Unspecified`: Any key generation failure.
133    #[cfg(feature = "fips")]
134    #[deprecated]
135    pub fn generate_fips(size: KeySize) -> Result<Self, Unspecified> {
136        Self::generate(size)
137    }
138
139    /// Parses an unencrypted PKCS#8 DER encoded RSA private key.
140    ///
141    /// Keys can be generated using [`KeyPair::generate`].
142    ///
143    /// # *ring*-compatibility
144    ///
145    /// *aws-lc-rs* does not impose the same limitations that *ring* does for
146    /// RSA keys. Thus signatures may be generated by keys that are not accepted
147    /// by *ring*. In particular:
148    /// * RSA private keys ranging between 2048-bit keys and 8192-bit keys are supported.
149    /// * The public exponent does not have a required minimum size.
150    ///
151    /// # Errors
152    /// `error::KeyRejected` if bytes do not encode an RSA private key or if the key is otherwise
153    /// not acceptable.
154    pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, KeyRejected> {
155        let key = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(pkcs8, EVP_PKEY_RSA)?;
156        Self::new(key)
157    }
158
159    /// Parses a DER-encoded `RSAPrivateKey` structure (RFC 8017).
160    ///
161    /// # Errors
162    /// `error:KeyRejected` on error.
163    pub fn from_der(input: &[u8]) -> Result<Self, KeyRejected> {
164        let key = encoding::rfc8017::decode_private_key_der(input)?;
165        Self::new(key)
166    }
167
168    /// Returns a boolean indicator if this RSA key is an approved FIPS 140-3 key.
169    #[cfg(feature = "fips")]
170    #[must_use]
171    pub fn is_valid_fips_key(&self) -> bool {
172        is_valid_fips_key(&self.evp_pkey)
173    }
174
175    fn validate_private_key(key: &LcPtr<EVP_PKEY>) -> Result<(), KeyRejected> {
176        if !is_rsa_key(key) {
177            return Err(KeyRejected::unspecified());
178        }
179        match key.key_size_bits() {
180            2048..=8192 => Ok(()),
181            _ => Err(KeyRejected::unspecified()),
182        }
183    }
184
185    /// Sign `msg`. `msg` is digested using the digest algorithm from
186    /// `padding_alg` and the digest is then padded using the padding algorithm
187    /// from `padding_alg`. The signature it written into `signature`;
188    /// `signature`'s length must be exactly the length returned by
189    /// `public_modulus_len()`.
190    ///
191    /// Many other crypto libraries have signing functions that takes a
192    /// precomputed digest as input, instead of the message to digest. This
193    /// function does *not* take a precomputed digest; instead, `sign`
194    /// calculates the digest itself.
195    ///
196    /// # *ring* Compatibility
197    /// Our implementation ignores the `SecureRandom` parameter.
198    // # FIPS
199    // The following conditions must be met:
200    // * RSA Key Sizes: 2048, 3072, 4096
201    // * Digest Algorithms: SHA256, SHA384, SHA512
202    //
203    /// # Errors
204    /// `error::Unspecified` on error.
205    /// With "fips" feature enabled, errors if digest length is greater than `u32::MAX`.
206    pub fn sign(
207        &self,
208        padding_alg: &'static dyn RsaEncoding,
209        _rng: &dyn rand::SecureRandom,
210        msg: &[u8],
211        signature: &mut [u8],
212    ) -> Result<(), Unspecified> {
213        let encoding = padding_alg.encoding();
214        let padding_fn = if let RsaPadding::RSA_PKCS1_PSS_PADDING = encoding.padding() {
215            Some(configure_rsa_pkcs1_pss_padding)
216        } else {
217            None
218        };
219
220        let sig_bytes = self
221            .evp_pkey
222            .sign(msg, Some(encoding.digest_algorithm()), padding_fn)?;
223
224        signature.copy_from_slice(&sig_bytes);
225        Ok(())
226    }
227
228    /// Returns the length in bytes of the key pair's public modulus.
229    ///
230    /// A signature has the same length as the public modulus.
231    #[must_use]
232    pub fn public_modulus_len(&self) -> usize {
233        // This was already validated to be an RSA key so this can't fail
234        match self.evp_pkey.get_rsa() {
235            Ok(rsa) => {
236                // https://github.com/awslabs/aws-lc/blob/main/include/openssl/rsa.h#L99
237                unsafe { RSA_size(*rsa) as usize }
238            }
239            Err(_) => unreachable!(),
240        }
241    }
242}
243
244impl Debug for KeyPair {
245    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
246        f.write_str(&format!(
247            "RsaKeyPair {{ public_key: {:?} }}",
248            self.serialized_public_key
249        ))
250    }
251}
252
253impl crate::signature::KeyPair for KeyPair {
254    type PublicKey = PublicKey;
255
256    fn public_key(&self) -> &Self::PublicKey {
257        &self.serialized_public_key
258    }
259}
260
261impl AsDer<Pkcs8V1Der<'static>> for KeyPair {
262    fn as_der(&self) -> Result<Pkcs8V1Der<'static>, Unspecified> {
263        Ok(Pkcs8V1Der::new(
264            self.evp_pkey.marshal_rfc5208_private_key(Version::V1)?,
265        ))
266    }
267}
268
269/// A serialized RSA public key.
270#[derive(Clone)]
271#[allow(clippy::module_name_repetitions)]
272pub struct PublicKey {
273    key: Box<[u8]>,
274    #[cfg(feature = "ring-io")]
275    modulus: Box<[u8]>,
276    #[cfg(feature = "ring-io")]
277    exponent: Box<[u8]>,
278}
279
280impl Drop for PublicKey {
281    fn drop(&mut self) {
282        self.key.zeroize();
283        #[cfg(feature = "ring-io")]
284        self.modulus.zeroize();
285        #[cfg(feature = "ring-io")]
286        self.exponent.zeroize();
287    }
288}
289
290impl PublicKey {
291    pub(super) fn new(evp_pkey: &LcPtr<EVP_PKEY>) -> Result<Self, Unspecified> {
292        let key = encoding::rfc8017::encode_public_key_der(evp_pkey)?;
293        #[cfg(feature = "ring-io")]
294        {
295            let pubkey = evp_pkey.get_rsa()?;
296            let modulus = ConstPointer::new(unsafe { RSA_get0_n(*pubkey) })?;
297            let modulus = modulus.to_be_bytes().into_boxed_slice();
298            let exponent = ConstPointer::new(unsafe { RSA_get0_e(*pubkey) })?;
299            let exponent = exponent.to_be_bytes().into_boxed_slice();
300            Ok(PublicKey {
301                key,
302                modulus,
303                exponent,
304            })
305        }
306
307        #[cfg(not(feature = "ring-io"))]
308        Ok(PublicKey { key })
309    }
310}
311
312impl Debug for PublicKey {
313    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
314        f.write_str(&format!(
315            "RsaPublicKey(\"{}\")",
316            hex::encode(self.key.as_ref())
317        ))
318    }
319}
320
321impl AsRef<[u8]> for PublicKey {
322    /// DER encode a RSA public key to (RFC 8017) `RSAPublicKey` structure.
323    fn as_ref(&self) -> &[u8] {
324        self.key.as_ref()
325    }
326}
327
328#[cfg(feature = "ring-io")]
329impl PublicKey {
330    /// The public modulus (n).
331    #[must_use]
332    pub fn modulus(&self) -> io::Positive<'_> {
333        io::Positive::new_non_empty_without_leading_zeros(Input::from(self.modulus.as_ref()))
334    }
335
336    /// The public exponent (e).
337    #[must_use]
338    pub fn exponent(&self) -> io::Positive<'_> {
339        io::Positive::new_non_empty_without_leading_zeros(Input::from(self.exponent.as_ref()))
340    }
341
342    /// Returns the length in bytes of the public modulus.
343    #[must_use]
344    pub fn modulus_len(&self) -> usize {
345        self.modulus.len()
346    }
347}
348
349/// Low-level API for RSA public keys.
350///
351/// When the public key is in DER-encoded PKCS#1 ASN.1 format, it is
352/// recommended to use `aws_lc_rs::signature::verify()` with
353/// `aws_lc_rs::signature::RSA_PKCS1_*`, because `aws_lc_rs::signature::verify()`
354/// will handle the parsing in that case. Otherwise, this function can be used
355/// to pass in the raw bytes for the public key components as
356/// `untrusted::Input` arguments.
357#[allow(clippy::module_name_repetitions)]
358#[derive(Clone)]
359pub struct PublicKeyComponents<B>
360where
361    B: AsRef<[u8]> + Debug,
362{
363    /// The public modulus, encoded in big-endian bytes without leading zeros.
364    pub n: B,
365    /// The public exponent, encoded in big-endian bytes without leading zeros.
366    pub e: B,
367}
368
369impl<B: AsRef<[u8]> + Debug> Debug for PublicKeyComponents<B> {
370    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
371        f.debug_struct("RsaPublicKeyComponents")
372            .field("n", &self.n)
373            .field("e", &self.e)
374            .finish()
375    }
376}
377
378impl<B: Copy + AsRef<[u8]> + Debug> Copy for PublicKeyComponents<B> {}
379
380impl<B> PublicKeyComponents<B>
381where
382    B: AsRef<[u8]> + Debug,
383{
384    #[inline]
385    fn build_rsa(&self) -> Result<LcPtr<EVP_PKEY>, ()> {
386        let n_bytes = self.n.as_ref();
387        if n_bytes.is_empty() || n_bytes[0] == 0u8 {
388            return Err(());
389        }
390        let n_bn = DetachableLcPtr::try_from(n_bytes)?;
391
392        let e_bytes = self.e.as_ref();
393        if e_bytes.is_empty() || e_bytes[0] == 0u8 {
394            return Err(());
395        }
396        let e_bn = DetachableLcPtr::try_from(e_bytes)?;
397
398        let rsa = DetachableLcPtr::new(unsafe { RSA_new() })?;
399        if 1 != unsafe { RSA_set0_key(*rsa, *n_bn, *e_bn, null_mut()) } {
400            return Err(());
401        }
402        n_bn.detach();
403        e_bn.detach();
404
405        let mut pkey = LcPtr::new(unsafe { EVP_PKEY_new() })?;
406        if 1 != unsafe { EVP_PKEY_assign_RSA(*pkey.as_mut(), *rsa) } {
407            return Err(());
408        }
409        rsa.detach();
410
411        Ok(pkey)
412    }
413
414    /// Verifies that `signature` is a valid signature of `message` using `self`
415    /// as the public key. `params` determine what algorithm parameters
416    /// (padding, digest algorithm, key length range, etc.) are used in the
417    /// verification.
418    ///
419    /// # Errors
420    /// `error::Unspecified` if `message` was not verified.
421    pub fn verify(
422        &self,
423        params: &RsaParameters,
424        message: &[u8],
425        signature: &[u8],
426    ) -> Result<(), Unspecified> {
427        let rsa = self.build_rsa()?;
428        super::signature::verify_rsa_signature(
429            params.digest_algorithm(),
430            params.padding(),
431            &rsa,
432            message,
433            signature,
434            params.bit_size_range(),
435        )
436    }
437}
438
439#[cfg(feature = "ring-io")]
440impl From<&PublicKey> for PublicKeyComponents<Vec<u8>> {
441    fn from(public_key: &PublicKey) -> Self {
442        PublicKeyComponents {
443            n: public_key.modulus.to_vec(),
444            e: public_key.exponent.to_vec(),
445        }
446    }
447}
448
449impl<B> TryInto<PublicEncryptingKey> for PublicKeyComponents<B>
450where
451    B: AsRef<[u8]> + Debug,
452{
453    type Error = Unspecified;
454
455    /// Try to build a `PublicEncryptingKey` from the public key components.
456    ///
457    /// # Errors
458    /// `error::Unspecified` if the key failed to verify.
459    fn try_into(self) -> Result<PublicEncryptingKey, Self::Error> {
460        let rsa = self.build_rsa()?;
461        PublicEncryptingKey::new(rsa)
462    }
463}
464
465pub(super) fn generate_rsa_key(size: c_int) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
466    let params_fn = |ctx| {
467        if 1 == unsafe { EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, size) } {
468            Ok(())
469        } else {
470            Err(())
471        }
472    };
473
474    LcPtr::<EVP_PKEY>::generate(EVP_PKEY_RSA, Some(params_fn))
475}
476
477#[cfg(feature = "fips")]
478#[must_use]
479pub(super) fn is_valid_fips_key(key: &LcPtr<EVP_PKEY>) -> bool {
480    // This should always be an RSA key and must-never panic.
481    let rsa_key = key.get_rsa().expect("RSA EVP_PKEY");
482
483    1 == unsafe { RSA_check_fips(*rsa_key as *mut RSA) }
484}
485
486pub(super) fn is_rsa_key(key: &LcPtr<EVP_PKEY>) -> bool {
487    let id = key.id();
488    id == EVP_PKEY_RSA || id == EVP_PKEY_RSA_PSS
489}