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