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};
7use crate::aws_lc::{
8    EVP_PKEY_CTX_set_rsa_keygen_bits, EVP_PKEY_CTX_set_signature_md, EVP_PKEY_assign_RSA,
9    EVP_PKEY_new, RSA_new, RSA_set0_key, RSA_size, EVP_PKEY, EVP_PKEY_CTX, EVP_PKEY_RSA,
10    EVP_PKEY_RSA_PSS,
11};
12#[cfg(feature = "ring-io")]
13use crate::aws_lc::{RSA_get0_e, RSA_get0_n};
14use crate::encoding::{AsDer, Pkcs8V1Der, PublicKeyX509Der};
15use crate::error::{KeyRejected, Unspecified};
16#[cfg(feature = "ring-io")]
17use crate::io;
18use crate::ptr::{DetachableLcPtr, LcPtr};
19use crate::rsa::PublicEncryptingKey;
20use crate::sealed::Sealed;
21use crate::{hex, rand};
22#[cfg(feature = "fips")]
23use aws_lc::RSA_check_fips;
24use core::fmt::{self, Debug, Formatter};
25use core::ptr::null_mut;
26
27// TODO: Uncomment when MSRV >= 1.64
28// use core::ffi::c_int;
29use std::os::raw::c_int;
30
31use crate::digest::{match_digest_type, Digest};
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 is written into `signature`;
187    /// `signature`'s length must be exactly the length returned by
188    /// `public_modulus_len()`.
189    ///
190    /// This function does *not* take a precomputed digest; instead, `sign`
191    /// calculates the digest itself. See `sign_digest`.
192    ///
193    /// # *ring* Compatibility
194    /// Our implementation ignores the `SecureRandom` parameter.
195    // # FIPS
196    // The following conditions must be met:
197    // * RSA Key Sizes: 2048, 3072, 4096
198    // * Digest Algorithms: SHA256, SHA384, SHA512
199    //
200    /// # Errors
201    /// `error::Unspecified` on error.
202    /// With "fips" feature enabled, errors if digest length is greater than `u32::MAX`.
203    pub fn sign(
204        &self,
205        padding_alg: &'static dyn RsaEncoding,
206        _rng: &dyn rand::SecureRandom,
207        msg: &[u8],
208        signature: &mut [u8],
209    ) -> Result<(), Unspecified> {
210        let encoding = padding_alg.encoding();
211        let padding_fn = if let RsaPadding::RSA_PKCS1_PSS_PADDING = encoding.padding() {
212            Some(configure_rsa_pkcs1_pss_padding)
213        } else {
214            None
215        };
216
217        let sig_bytes = self
218            .evp_pkey
219            .sign(msg, Some(encoding.digest_algorithm()), padding_fn)?;
220
221        signature.copy_from_slice(&sig_bytes);
222        Ok(())
223    }
224
225    /// The `digest` is padded using the padding algorithm
226    /// from `padding_alg`. The signature is written into `signature`;
227    /// `signature`'s length must be exactly the length returned by
228    /// `public_modulus_len()`.
229    ///
230    /// # *ring* Compatibility
231    /// Our implementation ignores the `SecureRandom` parameter.
232    //
233    // # FIPS
234    // Not allowed
235    //
236    /// # Errors
237    /// `error::Unspecified` on error.
238    /// With "fips" feature enabled, errors if digest length is greater than `u32::MAX`.
239    pub fn sign_digest(
240        &self,
241        padding_alg: &'static dyn RsaEncoding,
242        digest: &Digest,
243        signature: &mut [u8],
244    ) -> Result<(), Unspecified> {
245        let encoding = padding_alg.encoding();
246        if encoding.digest_algorithm() != digest.algorithm() {
247            return Err(Unspecified);
248        }
249
250        let padding_fn = Some({
251            |pctx: *mut EVP_PKEY_CTX| {
252                let evp_md = match_digest_type(&digest.algorithm().id);
253                if 1 != unsafe { EVP_PKEY_CTX_set_signature_md(pctx, *evp_md) } {
254                    return Err(());
255                }
256                if let RsaPadding::RSA_PKCS1_PSS_PADDING = encoding.padding() {
257                    configure_rsa_pkcs1_pss_padding(pctx)
258                } else {
259                    Ok(())
260                }
261            }
262        });
263
264        let sig_bytes = self.evp_pkey.sign_digest(digest, padding_fn)?;
265
266        signature.copy_from_slice(&sig_bytes);
267        Ok(())
268    }
269
270    /// Returns the length in bytes of the key pair's public modulus.
271    ///
272    /// A signature has the same length as the public modulus.
273    #[must_use]
274    pub fn public_modulus_len(&self) -> usize {
275        // This was already validated to be an RSA key so this can't fail
276        match self.evp_pkey.as_const().get_rsa() {
277            Ok(rsa) => {
278                // https://github.com/awslabs/aws-lc/blob/main/include/openssl/rsa.h#L99
279                unsafe { RSA_size(*rsa) as usize }
280            }
281            Err(_) => unreachable!(),
282        }
283    }
284}
285
286impl Debug for KeyPair {
287    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
288        f.write_str(&format!(
289            "RsaKeyPair {{ public_key: {:?} }}",
290            self.serialized_public_key
291        ))
292    }
293}
294
295impl crate::signature::KeyPair for KeyPair {
296    type PublicKey = PublicKey;
297
298    fn public_key(&self) -> &Self::PublicKey {
299        &self.serialized_public_key
300    }
301}
302
303impl AsDer<Pkcs8V1Der<'static>> for KeyPair {
304    fn as_der(&self) -> Result<Pkcs8V1Der<'static>, Unspecified> {
305        Ok(Pkcs8V1Der::new(
306            self.evp_pkey
307                .as_const()
308                .marshal_rfc5208_private_key(Version::V1)?,
309        ))
310    }
311}
312
313/// A serialized RSA public key.
314#[derive(Clone)]
315#[allow(clippy::module_name_repetitions)]
316pub struct PublicKey {
317    key: Box<[u8]>,
318    #[cfg(feature = "ring-io")]
319    modulus: Box<[u8]>,
320    #[cfg(feature = "ring-io")]
321    exponent: Box<[u8]>,
322}
323
324impl Drop for PublicKey {
325    fn drop(&mut self) {
326        self.key.zeroize();
327        #[cfg(feature = "ring-io")]
328        self.modulus.zeroize();
329        #[cfg(feature = "ring-io")]
330        self.exponent.zeroize();
331    }
332}
333
334impl PublicKey {
335    pub(super) fn new(evp_pkey: &LcPtr<EVP_PKEY>) -> Result<Self, KeyRejected> {
336        let key = encoding::rfc8017::encode_public_key_der(evp_pkey)?;
337        #[cfg(feature = "ring-io")]
338        {
339            let evp_pkey = evp_pkey.as_const();
340            let pubkey = evp_pkey.get_rsa()?;
341            let modulus =
342                pubkey.project_const_lifetime(unsafe { |pubkey| RSA_get0_n(**pubkey) })?;
343            let modulus = modulus.to_be_bytes().into_boxed_slice();
344            let exponent =
345                pubkey.project_const_lifetime(unsafe { |pubkey| RSA_get0_e(**pubkey) })?;
346            let exponent = exponent.to_be_bytes().into_boxed_slice();
347            Ok(PublicKey {
348                key,
349                modulus,
350                exponent,
351            })
352        }
353
354        #[cfg(not(feature = "ring-io"))]
355        Ok(PublicKey { key })
356    }
357
358    /// Parses an RSA public key from either RFC8017 or RFC5280
359    /// # Errors
360    /// `KeyRejected` if the encoding is not for a valid RSA key.
361    pub fn from_der(input: &[u8]) -> Result<Self, KeyRejected> {
362        // These both invoke `RSA_check_key`:
363        // https://github.com/aws/aws-lc/blob/4368aaa6975ba41bd76d3bb12fac54c4680247fb/crypto/rsa_extra/rsa_asn1.c#L105-L109
364        PublicKey::new(
365            &rfc8017::decode_public_key_der(input).or(rfc5280::decode_public_key_der(input))?,
366        )
367    }
368}
369
370pub(crate) fn parse_rsa_public_key(input: &[u8]) -> Result<LcPtr<EVP_PKEY>, KeyRejected> {
371    rfc8017::decode_public_key_der(input).or(rfc5280::decode_public_key_der(input))
372}
373
374impl Debug for PublicKey {
375    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
376        f.write_str(&format!(
377            "RsaPublicKey(\"{}\")",
378            hex::encode(self.key.as_ref())
379        ))
380    }
381}
382
383impl AsRef<[u8]> for PublicKey {
384    /// DER encode a RSA public key to (RFC 8017) `RSAPublicKey` structure.
385    fn as_ref(&self) -> &[u8] {
386        self.key.as_ref()
387    }
388}
389
390impl AsDer<PublicKeyX509Der<'static>> for PublicKey {
391    fn as_der(&self) -> Result<PublicKeyX509Der<'static>, Unspecified> {
392        // TODO: refactor
393        let evp_pkey = rfc8017::decode_public_key_der(self.as_ref())?;
394        rfc5280::encode_public_key_der(&evp_pkey)
395    }
396}
397
398#[cfg(feature = "ring-io")]
399impl PublicKey {
400    /// The public modulus (n).
401    #[must_use]
402    pub fn modulus(&self) -> io::Positive<'_> {
403        io::Positive::new_non_empty_without_leading_zeros(Input::from(self.modulus.as_ref()))
404    }
405
406    /// The public exponent (e).
407    #[must_use]
408    pub fn exponent(&self) -> io::Positive<'_> {
409        io::Positive::new_non_empty_without_leading_zeros(Input::from(self.exponent.as_ref()))
410    }
411
412    /// Returns the length in bytes of the public modulus.
413    #[must_use]
414    pub fn modulus_len(&self) -> usize {
415        self.modulus.len()
416    }
417}
418
419/// Low-level API for RSA public keys.
420///
421/// When the public key is in DER-encoded PKCS#1 ASN.1 format, it is
422/// recommended to use `aws_lc_rs::signature::verify()` with
423/// `aws_lc_rs::signature::RSA_PKCS1_*`, because `aws_lc_rs::signature::verify()`
424/// will handle the parsing in that case. Otherwise, this function can be used
425/// to pass in the raw bytes for the public key components as
426/// `untrusted::Input` arguments.
427#[allow(clippy::module_name_repetitions)]
428#[derive(Clone)]
429pub struct PublicKeyComponents<B>
430where
431    B: AsRef<[u8]> + Debug,
432{
433    /// The public modulus, encoded in big-endian bytes without leading zeros.
434    pub n: B,
435    /// The public exponent, encoded in big-endian bytes without leading zeros.
436    pub e: B,
437}
438
439impl<B: AsRef<[u8]> + Debug> Debug for PublicKeyComponents<B> {
440    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
441        f.debug_struct("RsaPublicKeyComponents")
442            .field("n", &self.n)
443            .field("e", &self.e)
444            .finish()
445    }
446}
447
448impl<B: Copy + AsRef<[u8]> + Debug> Copy for PublicKeyComponents<B> {}
449
450impl<B> PublicKeyComponents<B>
451where
452    B: AsRef<[u8]> + Debug,
453{
454    #[inline]
455    fn build_rsa(&self) -> Result<LcPtr<EVP_PKEY>, ()> {
456        let n_bytes = self.n.as_ref();
457        if n_bytes.is_empty() || n_bytes[0] == 0u8 {
458            return Err(());
459        }
460        let n_bn = DetachableLcPtr::try_from(n_bytes)?;
461
462        let e_bytes = self.e.as_ref();
463        if e_bytes.is_empty() || e_bytes[0] == 0u8 {
464            return Err(());
465        }
466        let e_bn = DetachableLcPtr::try_from(e_bytes)?;
467
468        let rsa = DetachableLcPtr::new(unsafe { RSA_new() })?;
469        if 1 != unsafe { RSA_set0_key(*rsa, *n_bn, *e_bn, null_mut()) } {
470            return Err(());
471        }
472        n_bn.detach();
473        e_bn.detach();
474
475        let mut pkey = LcPtr::new(unsafe { EVP_PKEY_new() })?;
476        if 1 != unsafe { EVP_PKEY_assign_RSA(*pkey.as_mut(), *rsa) } {
477            return Err(());
478        }
479        rsa.detach();
480
481        Ok(pkey)
482    }
483
484    /// Verifies that `signature` is a valid signature of `message` using `self`
485    /// as the public key. `params` determine what algorithm parameters
486    /// (padding, digest algorithm, key length range, etc.) are used in the
487    /// verification.
488    ///
489    /// # Errors
490    /// `error::Unspecified` if `message` was not verified.
491    pub fn verify(
492        &self,
493        params: &RsaParameters,
494        message: &[u8],
495        signature: &[u8],
496    ) -> Result<(), Unspecified> {
497        let rsa = self.build_rsa()?;
498        super::signature::verify_rsa_signature(
499            params.digest_algorithm(),
500            params.padding(),
501            &rsa,
502            message,
503            signature,
504            params.bit_size_range(),
505        )
506    }
507}
508
509#[cfg(feature = "ring-io")]
510impl From<&PublicKey> for PublicKeyComponents<Vec<u8>> {
511    fn from(public_key: &PublicKey) -> Self {
512        PublicKeyComponents {
513            n: public_key.modulus.to_vec(),
514            e: public_key.exponent.to_vec(),
515        }
516    }
517}
518
519impl<B> TryInto<PublicEncryptingKey> for PublicKeyComponents<B>
520where
521    B: AsRef<[u8]> + Debug,
522{
523    type Error = Unspecified;
524
525    /// Try to build a `PublicEncryptingKey` from the public key components.
526    ///
527    /// # Errors
528    /// `error::Unspecified` if the key failed to verify.
529    fn try_into(self) -> Result<PublicEncryptingKey, Self::Error> {
530        let rsa = self.build_rsa()?;
531        PublicEncryptingKey::new(rsa)
532    }
533}
534
535pub(super) fn generate_rsa_key(size: c_int) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
536    let params_fn = |ctx| {
537        if 1 == unsafe { EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, size) } {
538            Ok(())
539        } else {
540            Err(())
541        }
542    };
543
544    LcPtr::<EVP_PKEY>::generate(EVP_PKEY_RSA, Some(params_fn))
545}
546
547#[cfg(feature = "fips")]
548#[must_use]
549pub(super) fn is_valid_fips_key(key: &LcPtr<EVP_PKEY>) -> bool {
550    // This should always be an RSA key and must-never panic.
551    let evp_pkey = key.as_const();
552    let rsa_key = evp_pkey.get_rsa().expect("RSA EVP_PKEY");
553
554    1 == unsafe { RSA_check_fips((*rsa_key).cast_mut()) }
555}
556
557pub(super) fn is_rsa_key(key: &LcPtr<EVP_PKEY>) -> bool {
558    let id = key.as_const().id();
559    id == EVP_PKEY_RSA || id == EVP_PKEY_RSA_PSS
560}