Skip to main content

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, EVP_PKEY_set1_RSA, RSA_check_key, RSA_new, RSA_set0_crt_params, RSA_set0_factors,
10    RSA_set0_key, RSA_size, BIGNUM, EVP_PKEY, EVP_PKEY_CTX, EVP_PKEY_RSA, 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#[derive(Clone)]
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
101/// RSA key pair components.
102#[allow(non_snake_case)]
103#[derive(Clone, Copy)]
104pub struct KeyPairComponents<Public, Private = Public> {
105    /// The public key components.
106    pub public_key: PublicKeyComponents<Public>,
107
108    /// The private exponent.
109    pub d: Private,
110
111    /// The first prime factor of `n`.
112    pub p: Private,
113
114    /// The second prime factor of `n`.
115    pub q: Private,
116
117    /// `p`'s CRT exponent: `d mod (p - 1)`.
118    pub dP: Private,
119
120    /// `q`'s CRT exponent: `d mod (q - 1)`.
121    pub dQ: Private,
122
123    /// The CRT coefficient: `q**-1 mod p`.
124    pub qInv: Private,
125}
126
127// Private components are intentionally excluded from the `Debug` output.
128#[allow(clippy::missing_fields_in_debug)]
129impl<Public, Private> Debug for KeyPairComponents<Public, Private>
130where
131    PublicKeyComponents<Public>: Debug,
132{
133    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
134        f.debug_struct("KeyPairComponents")
135            .field("public_key", &self.public_key)
136            .finish()
137    }
138}
139
140impl KeyPair {
141    fn new(evp_pkey: LcPtr<EVP_PKEY>) -> Result<Self, KeyRejected> {
142        KeyPair::validate_private_key(&evp_pkey)?;
143        let serialized_public_key = PublicKey::new(&evp_pkey)?;
144        Ok(KeyPair {
145            evp_pkey,
146            serialized_public_key,
147        })
148    }
149
150    /// Generate a RSA `KeyPair` of the specified key-strength.
151    ///
152    /// Supports the following key sizes:
153    /// * `KeySize::Rsa2048`
154    /// * `KeySize::Rsa3072`
155    /// * `KeySize::Rsa4096`
156    /// * `KeySize::Rsa8192`
157    ///
158    /// # Errors
159    /// * `Unspecified`: Any key generation failure.
160    pub fn generate(size: KeySize) -> Result<Self, Unspecified> {
161        let private_key = generate_rsa_key(size.bits())?;
162        Ok(Self::new(private_key)?)
163    }
164
165    /// Generate a RSA `KeyPair` of the specified key-strength.
166    ///
167    /// ## Deprecated
168    /// This is equivalent to `KeyPair::generate`.
169    ///
170    /// # Errors
171    /// * `Unspecified`: Any key generation failure.
172    #[cfg(feature = "fips")]
173    #[deprecated(
174        since = "1.12.3",
175        note = "Equivalent to `KeyPair::generate`; use that instead."
176    )]
177    pub fn generate_fips(size: KeySize) -> Result<Self, Unspecified> {
178        Self::generate(size)
179    }
180
181    /// Parses an unencrypted PKCS#8 DER encoded RSA private key.
182    ///
183    /// Keys can be generated using [`KeyPair::generate`].
184    ///
185    /// # *ring*-compatibility
186    ///
187    /// *aws-lc-rs* does not impose the same limitations that *ring* does for
188    /// RSA keys. Thus signatures may be generated by keys that are not accepted
189    /// by *ring*. In particular:
190    /// * RSA private keys ranging between 2048-bit keys and 8192-bit keys are supported.
191    /// * The public exponent does not have a required minimum size.
192    ///
193    /// # Errors
194    /// `error::KeyRejected` if bytes do not encode an RSA private key or if the key is otherwise
195    /// not acceptable.
196    pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, KeyRejected> {
197        let key = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(pkcs8, EVP_PKEY_RSA)?;
198        Self::new(key)
199    }
200
201    /// Parses a DER-encoded `RSAPrivateKey` structure (RFC 8017).
202    ///
203    /// # Errors
204    /// `error:KeyRejected` on error.
205    pub fn from_der(input: &[u8]) -> Result<Self, KeyRejected> {
206        let key = encoding::rfc8017::decode_private_key_der(input)?;
207        Self::new(key)
208    }
209
210    /// Returns a boolean indicator if this RSA key is an approved FIPS 140-3 key.
211    #[cfg(feature = "fips")]
212    #[must_use]
213    pub fn is_valid_fips_key(&self) -> bool {
214        is_valid_fips_key(&self.evp_pkey)
215    }
216
217    fn validate_private_key(key: &LcPtr<EVP_PKEY>) -> Result<(), KeyRejected> {
218        validate_rsa_key(key)
219    }
220
221    /// Sign `msg`. `msg` is digested using the digest algorithm from
222    /// `padding_alg` and the digest is then padded using the padding algorithm
223    /// from `padding_alg`. The signature is written into `signature`;
224    /// `signature`'s length must be exactly the length returned by
225    /// `public_modulus_len()`.
226    ///
227    /// This function does *not* take a precomputed digest; instead, `sign`
228    /// calculates the digest itself. See `sign_digest`.
229    ///
230    /// # *ring* Compatibility
231    /// Our implementation ignores the `SecureRandom` parameter.
232    // # FIPS
233    // The following conditions must be met:
234    // * RSA Key Sizes: 2048, 3072, 4096
235    // * Digest Algorithms: SHA256, SHA384, SHA512
236    //
237    /// # Errors
238    /// `error::Unspecified` on error.
239    /// With "fips" feature enabled, errors if digest length is greater than `u32::MAX`.
240    pub fn sign(
241        &self,
242        padding_alg: &'static dyn RsaEncoding,
243        _rng: &dyn rand::SecureRandom,
244        msg: &[u8],
245        signature: &mut [u8],
246    ) -> Result<(), Unspecified> {
247        let encoding = padding_alg.encoding();
248        let padding_fn = if let RsaPadding::RSA_PKCS1_PSS_PADDING = encoding.padding() {
249            Some(|pctx: *mut EVP_PKEY_CTX| {
250                configure_rsa_pkcs1_pss_padding(
251                    pctx,
252                    encoding.pss_salt_length(),
253                    encoding.digest_algorithm(),
254                    &self.evp_pkey,
255                )
256            })
257        } else {
258            None
259        };
260
261        let sig_bytes = self
262            .evp_pkey
263            .sign(msg, Some(encoding.digest_algorithm()), padding_fn)?;
264
265        signature.copy_from_slice(&sig_bytes);
266        Ok(())
267    }
268
269    /// The `digest` is padded using the padding algorithm
270    /// from `padding_alg`. The signature is written into `signature`;
271    /// `signature`'s length must be exactly the length returned by
272    /// `public_modulus_len()`.
273    ///
274    /// # *ring* Compatibility
275    /// Our implementation ignores the `SecureRandom` parameter.
276    //
277    // # FIPS
278    // Not allowed
279    //
280    /// # Errors
281    /// `error::Unspecified` on error.
282    /// With "fips" feature enabled, errors if digest length is greater than `u32::MAX`.
283    pub fn sign_digest(
284        &self,
285        padding_alg: &'static dyn RsaEncoding,
286        digest: &Digest,
287        signature: &mut [u8],
288    ) -> Result<(), Unspecified> {
289        let encoding = padding_alg.encoding();
290        if encoding.digest_algorithm() != digest.algorithm() {
291            return Err(Unspecified);
292        }
293
294        let padding_fn = Some({
295            |pctx: *mut EVP_PKEY_CTX| {
296                let evp_md = match_digest_type(&digest.algorithm().id);
297                if 1 != unsafe { EVP_PKEY_CTX_set_signature_md(pctx, evp_md.as_const_ptr()) } {
298                    return Err(());
299                }
300                if let RsaPadding::RSA_PKCS1_PSS_PADDING = encoding.padding() {
301                    configure_rsa_pkcs1_pss_padding(
302                        pctx,
303                        encoding.pss_salt_length(),
304                        encoding.digest_algorithm(),
305                        &self.evp_pkey,
306                    )
307                } else {
308                    Ok(())
309                }
310            }
311        });
312
313        let sig_bytes = self.evp_pkey.sign_digest(digest, padding_fn)?;
314
315        signature.copy_from_slice(&sig_bytes);
316        Ok(())
317    }
318
319    /// Returns the length in bytes of the key pair's public modulus.
320    ///
321    /// A signature has the same length as the public modulus.
322    #[must_use]
323    pub fn public_modulus_len(&self) -> usize {
324        // This was already validated to be an RSA key so this can't fail
325        match self.evp_pkey.as_const().get_rsa() {
326            Ok(rsa) => {
327                // https://github.com/awslabs/aws-lc/blob/main/include/openssl/rsa.h#L99
328                unsafe { RSA_size(rsa.as_const_ptr()) as usize }
329            }
330            Err(_) => unreachable!(),
331        }
332    }
333
334    /// Constructs an RSA private key from its big-endian-encoded components.
335    ///
336    /// All components, including the CRT parameters (`dP`, `dQ`, `qInv`), are
337    /// required and are validated for consistency with one another: the key is
338    /// rejected unless `n == p * q`, `d * e == 1 (mod p-1)`,
339    /// `d * e == 1 (mod q-1)`, `dP == d (mod p-1)`, `dQ == d (mod q-1)`, and
340    /// `qInv == q**-1 (mod p)`. No primality tests are performed on `p` and
341    /// `q`.
342    ///
343    /// Only two-prime (not multi-prime) keys are supported. The public
344    /// modulus (`n`) must be 2048 to 8192 bits. The public exponent (`e`)
345    /// must be odd, greater than 1, and no longer than 33 bits.
346    ///
347    /// The public components (`n` and `e`) must be encoded without leading
348    /// zero bytes, as documented on [`PublicKeyComponents`]. Leading zero
349    /// bytes are permitted on the private components.
350    ///
351    /// # *ring* compatibility
352    ///
353    /// *aws-lc-rs* does not impose the same limitations that *ring* does, so
354    /// keys rejected by *ring* may be accepted here. In particular:
355    /// * The public modulus may be up to 8192 bits, rather than 4096.
356    /// * The public exponent has no required minimum size, whereas *ring*
357    ///   requires it to be at least 65537.
358    ///
359    /// In two respects *aws-lc-rs* is stricter than *ring*, so a key accepted
360    /// by *ring* may be rejected here:
361    /// * *ring* never uses `d` and so does not fully validate it. We do
362    ///   validate `d`, which means a key carrying a placeholder or otherwise
363    ///   inconsistent `d` is rejected.
364    /// * *ring* defers validation of the CRT parameters until the key is used
365    ///   for signing. We validate them here, so an inconsistent key fails at
366    ///   construction rather than at first use.
367    ///
368    /// # Errors
369    /// `KeyRejected` if the components do not form a valid, supported RSA
370    /// private key.
371    // The bindings use the standard RSA component names.
372    #[allow(clippy::many_single_char_names, clippy::similar_names)]
373    pub fn from_components<Public, Private>(
374        components: &KeyPairComponents<Public, Private>,
375    ) -> Result<Self, KeyRejected>
376    where
377        Public: AsRef<[u8]>,
378        Private: AsRef<[u8]>,
379    {
380        let mut rsa = LcPtr::new(unsafe { RSA_new() })?;
381        let mut p = DetachableLcPtr::try_from(components.p.as_ref())?;
382        let mut q = DetachableLcPtr::try_from(components.q.as_ref())?;
383        if 1 != unsafe { RSA_set0_factors(rsa.as_mut_ptr(), p.as_mut_ptr(), q.as_mut_ptr()) } {
384            return Err(KeyRejected::unspecified());
385        }
386        p.detach();
387        q.detach();
388
389        let mut n = public_component_to_bn(components.public_key.n.as_ref())?;
390        let mut e = public_component_to_bn(components.public_key.e.as_ref())?;
391        let mut d = DetachableLcPtr::try_from(components.d.as_ref())?;
392        if 1 != unsafe {
393            RSA_set0_key(
394                rsa.as_mut_ptr(),
395                n.as_mut_ptr(),
396                e.as_mut_ptr(),
397                d.as_mut_ptr(),
398            )
399        } {
400            return Err(KeyRejected::unspecified());
401        }
402        n.detach();
403        e.detach();
404        d.detach();
405
406        let mut dmp1 = DetachableLcPtr::try_from(components.dP.as_ref())?;
407        let mut dmq1 = DetachableLcPtr::try_from(components.dQ.as_ref())?;
408        let mut iqmp = DetachableLcPtr::try_from(components.qInv.as_ref())?;
409        if 1 != unsafe {
410            RSA_set0_crt_params(
411                rsa.as_mut_ptr(),
412                dmp1.as_mut_ptr(),
413                dmq1.as_mut_ptr(),
414                iqmp.as_mut_ptr(),
415            )
416        } {
417            return Err(KeyRejected::unspecified());
418        }
419        dmp1.detach();
420        dmq1.detach();
421        iqmp.detach();
422
423        if 1 != unsafe { RSA_check_key(rsa.as_mut_ptr()) } {
424            return Err(KeyRejected::inconsistent_components());
425        }
426        let mut evp_pkey = LcPtr::new(unsafe { EVP_PKEY_new() })?;
427        // `EVP_PKEY_set1_RSA` takes a reference on `rsa` rather than ownership
428        // of it, so `rsa` is intentionally left attached and is released when it
429        // goes out of scope. Elsewhere we use `EVP_PKEY_assign_RSA`, which
430        // requires detaching the `RSA` on success.
431        if 1 != unsafe { EVP_PKEY_set1_RSA(evp_pkey.as_mut_ptr(), rsa.as_mut_ptr()) } {
432            return Err(KeyRejected::unspecified());
433        }
434
435        Self::new(evp_pkey)
436    }
437}
438
439impl Debug for KeyPair {
440    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
441        f.write_str(&format!(
442            "RsaKeyPair {{ public_key: {:?} }}",
443            self.serialized_public_key
444        ))
445    }
446}
447
448impl crate::signature::KeyPair for KeyPair {
449    type PublicKey = PublicKey;
450
451    fn public_key(&self) -> &Self::PublicKey {
452        &self.serialized_public_key
453    }
454}
455
456impl AsDer<Pkcs8V1Der<'static>> for KeyPair {
457    fn as_der(&self) -> Result<Pkcs8V1Der<'static>, Unspecified> {
458        Ok(Pkcs8V1Der::new(
459            self.evp_pkey
460                .as_const()
461                .marshal_rfc5208_private_key(Version::V1)?,
462        ))
463    }
464}
465
466/// A serialized RSA public key.
467#[derive(Clone)]
468#[allow(clippy::module_name_repetitions)]
469pub struct PublicKey {
470    key: Box<[u8]>,
471    #[cfg(feature = "ring-io")]
472    modulus: Box<[u8]>,
473    #[cfg(feature = "ring-io")]
474    exponent: Box<[u8]>,
475}
476
477impl Drop for PublicKey {
478    fn drop(&mut self) {
479        self.key.zeroize();
480        #[cfg(feature = "ring-io")]
481        self.modulus.zeroize();
482        #[cfg(feature = "ring-io")]
483        self.exponent.zeroize();
484    }
485}
486
487impl PublicKey {
488    pub(super) fn new(evp_pkey: &LcPtr<EVP_PKEY>) -> Result<Self, KeyRejected> {
489        let key = encoding::rfc8017::encode_public_key_der(evp_pkey)?;
490        #[cfg(feature = "ring-io")]
491        {
492            let evp_pkey = evp_pkey.as_const();
493            let pubkey = evp_pkey.get_rsa()?;
494            let modulus = pubkey
495                .project_const_lifetime(unsafe { |pubkey| RSA_get0_n(pubkey.as_const_ptr()) })?;
496            let modulus = modulus.to_be_bytes().into_boxed_slice();
497            let exponent = pubkey
498                .project_const_lifetime(unsafe { |pubkey| RSA_get0_e(pubkey.as_const_ptr()) })?;
499            let exponent = exponent.to_be_bytes().into_boxed_slice();
500            Ok(PublicKey {
501                key,
502                modulus,
503                exponent,
504            })
505        }
506
507        #[cfg(not(feature = "ring-io"))]
508        Ok(PublicKey { key })
509    }
510
511    /// Parses an RSA public key from either RFC8017 or RFC5280
512    /// # Errors
513    /// `KeyRejected` if the encoding is not for a valid RSA key.
514    pub fn from_der(input: &[u8]) -> Result<Self, KeyRejected> {
515        // These both invoke `RSA_check_key`:
516        // https://github.com/aws/aws-lc/blob/4368aaa6975ba41bd76d3bb12fac54c4680247fb/crypto/rsa_extra/rsa_asn1.c#L105-L109
517        PublicKey::new(
518            &rfc8017::decode_public_key_der(input).or(rfc5280::decode_public_key_der(input))?,
519        )
520    }
521}
522
523pub(crate) fn parse_rsa_public_key(input: &[u8]) -> Result<LcPtr<EVP_PKEY>, KeyRejected> {
524    rfc8017::decode_public_key_der(input).or(rfc5280::decode_public_key_der(input))
525}
526
527impl Debug for PublicKey {
528    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
529        f.write_str(&format!(
530            "RsaPublicKey(\"{}\")",
531            hex::encode(self.key.as_ref())
532        ))
533    }
534}
535
536impl AsRef<[u8]> for PublicKey {
537    /// DER encode a RSA public key to (RFC 8017) `RSAPublicKey` structure.
538    fn as_ref(&self) -> &[u8] {
539        self.key.as_ref()
540    }
541}
542
543impl AsDer<PublicKeyX509Der<'static>> for PublicKey {
544    fn as_der(&self) -> Result<PublicKeyX509Der<'static>, Unspecified> {
545        // TODO: refactor
546        let evp_pkey = rfc8017::decode_public_key_der(self.as_ref())?;
547        rfc5280::encode_public_key_der(&evp_pkey)
548    }
549}
550
551#[cfg(feature = "ring-io")]
552impl PublicKey {
553    /// The public modulus (n).
554    #[must_use]
555    pub fn modulus(&self) -> io::Positive<'_> {
556        io::Positive::new_non_empty_without_leading_zeros(Input::from(self.modulus.as_ref()))
557    }
558
559    /// The public exponent (e).
560    #[must_use]
561    pub fn exponent(&self) -> io::Positive<'_> {
562        io::Positive::new_non_empty_without_leading_zeros(Input::from(self.exponent.as_ref()))
563    }
564
565    /// Returns the length in bytes of the public modulus.
566    #[must_use]
567    pub fn modulus_len(&self) -> usize {
568        self.modulus.len()
569    }
570}
571
572/// Low-level API for RSA public keys.
573///
574/// When the public key is in DER-encoded PKCS#1 ASN.1 format, it is
575/// recommended to use `aws_lc_rs::signature::verify()` with
576/// `aws_lc_rs::signature::RSA_PKCS1_*`, because `aws_lc_rs::signature::verify()`
577/// will handle the parsing in that case. Otherwise, this function can be used
578/// to pass in the raw bytes for the public key components as
579/// `untrusted::Input` arguments.
580#[allow(clippy::module_name_repetitions)]
581#[derive(Clone)]
582pub struct PublicKeyComponents<B> {
583    /// The public modulus, encoded in big-endian bytes without leading zeros.
584    pub n: B,
585    /// The public exponent, encoded in big-endian bytes without leading zeros.
586    pub e: B,
587}
588
589impl<B: Debug> Debug for PublicKeyComponents<B> {
590    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
591        f.debug_struct("RsaPublicKeyComponents")
592            .field("n", &self.n)
593            .field("e", &self.e)
594            .finish()
595    }
596}
597
598impl<B: Copy> Copy for PublicKeyComponents<B> {}
599
600/// Converts one of the public components (`n` or `e`) of a `PublicKeyComponents`
601/// into a `BIGNUM`, enforcing the documented "without leading zeros" encoding.
602///
603/// Every API that consumes a `PublicKeyComponents` must go through this so that
604/// a given value is accepted (or rejected) consistently by all of them.
605fn public_component_to_bn(bytes: &[u8]) -> Result<DetachableLcPtr<BIGNUM>, KeyRejected> {
606    if bytes.is_empty() || bytes[0] == 0u8 {
607        return Err(KeyRejected::invalid_encoding());
608    }
609    Ok(DetachableLcPtr::try_from(bytes)?)
610}
611
612impl<B> PublicKeyComponents<B>
613where
614    B: AsRef<[u8]>,
615{
616    #[inline]
617    fn build_rsa(&self) -> Result<LcPtr<EVP_PKEY>, ()> {
618        let mut n_bn = public_component_to_bn(self.n.as_ref()).map_err(|_| ())?;
619        let mut e_bn = public_component_to_bn(self.e.as_ref()).map_err(|_| ())?;
620
621        let mut rsa = DetachableLcPtr::new(unsafe { RSA_new() })?;
622        if 1 != unsafe {
623            RSA_set0_key(
624                rsa.as_mut_ptr(),
625                n_bn.as_mut_ptr(),
626                e_bn.as_mut_ptr(),
627                null_mut(),
628            )
629        } {
630            return Err(());
631        }
632        n_bn.detach();
633        e_bn.detach();
634
635        let mut pkey = LcPtr::new(unsafe { EVP_PKEY_new() })?;
636        if 1 != unsafe { EVP_PKEY_assign_RSA(pkey.as_mut_ptr(), rsa.as_mut_ptr()) } {
637            return Err(());
638        }
639        rsa.detach();
640
641        Ok(pkey)
642    }
643
644    /// Verifies that `signature` is a valid signature of `message` using `self`
645    /// as the public key. `params` determine what algorithm parameters
646    /// (padding, digest algorithm, key length range, etc.) are used in the
647    /// verification.
648    ///
649    /// # Errors
650    /// `error::Unspecified` if `message` was not verified.
651    pub fn verify(
652        &self,
653        params: &RsaParameters,
654        message: &[u8],
655        signature: &[u8],
656    ) -> Result<(), Unspecified> {
657        let rsa = self.build_rsa()?;
658        super::signature::verify_rsa_signature(
659            params.digest_algorithm(),
660            params.padding(),
661            params.pss_salt_length(),
662            &rsa,
663            message,
664            signature,
665            params.bit_size_range(),
666        )
667    }
668
669    /// Parses these components into a [`crate::signature::ParsedPublicKey`],
670    /// which can then be used to verify multiple signatures while amortizing
671    /// the cost of key parsing.
672    ///
673    /// `params` specifies the RSA verification algorithm, such as
674    /// [`crate::signature::RSA_PKCS1_2048_8192_SHA256`] or
675    /// [`crate::signature::RSA_PSS_2048_8192_SHA256`].
676    ///
677    /// Note that the algorithm's accepted key-size range is *not* enforced at
678    /// this point; that check is deferred to
679    /// [`crate::signature::ParsedPublicKey::verify_sig`], matching the
680    /// behavior of [`crate::signature::ParsedPublicKey::new`].
681    ///
682    /// # Errors
683    /// `KeyRejected` if `self` does not form a valid RSA public key.
684    ///
685    /// # Examples
686    ///
687    /// ```no_run
688    /// use aws_lc_rs::signature::{self, RsaPublicKeyComponents};
689    ///
690    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
691    /// # fn modulus_bytes() -> &'static [u8] { &[] }
692    /// # fn exponent_bytes() -> &'static [u8] { &[] }
693    /// # fn signature_bytes() -> &'static [u8] { &[] }
694    /// // Public key components (big-endian, no leading zeros) received
695    /// // out-of-band from a peer.
696    /// let components = RsaPublicKeyComponents {
697    ///     n: modulus_bytes(),
698    ///     e: exponent_bytes(),
699    /// };
700    /// let parsed =
701    ///     components.to_parsed_public_key(&signature::RSA_PKCS1_2048_8192_SHA256)?;
702    /// parsed.verify_sig(b"hello, world", signature_bytes())?;
703    /// # Ok(()) }
704    /// ```
705    pub fn to_parsed_public_key(
706        &self,
707        params: &'static RsaParameters,
708    ) -> Result<crate::signature::ParsedPublicKey, KeyRejected> {
709        let pkey = self
710            .build_rsa()
711            .map_err(|()| KeyRejected::inconsistent_components())?;
712        Ok(crate::signature::ParsedPublicKey::from_rsa_evp_pkey(
713            params, pkey,
714        )?)
715    }
716}
717
718#[cfg(feature = "ring-io")]
719impl From<&PublicKey> for PublicKeyComponents<Vec<u8>> {
720    fn from(public_key: &PublicKey) -> Self {
721        PublicKeyComponents {
722            n: public_key.modulus.to_vec(),
723            e: public_key.exponent.to_vec(),
724        }
725    }
726}
727
728impl<B> TryInto<PublicEncryptingKey> for PublicKeyComponents<B>
729where
730    B: AsRef<[u8]>,
731{
732    type Error = Unspecified;
733
734    /// Try to build a `PublicEncryptingKey` from the public key components.
735    ///
736    /// # Errors
737    /// `error::Unspecified` if the key failed to verify.
738    fn try_into(self) -> Result<PublicEncryptingKey, Self::Error> {
739        let rsa = self.build_rsa()?;
740        Ok(PublicEncryptingKey::new(rsa)?)
741    }
742}
743
744impl<B> AsDer<PublicKeyX509Der<'static>> for PublicKeyComponents<B>
745where
746    B: AsRef<[u8]>,
747{
748    /// Serializes the RSA public key components into an X.509 `SubjectPublicKeyInfo`
749    /// structure, as specified in [RFC 5280].
750    ///
751    /// [RFC 5280]: https://www.rfc-editor.org/rfc/rfc5280.html
752    ///
753    /// # Errors
754    /// `error::Unspecified` if the components do not form a valid RSA public key.
755    fn as_der(&self) -> Result<PublicKeyX509Der<'static>, Unspecified> {
756        let pkey = self.build_rsa()?;
757        rfc5280::encode_public_key_der(&pkey)
758    }
759}
760
761pub(super) fn generate_rsa_key(size: c_int) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
762    let params_fn = |ctx| {
763        if 1 == unsafe { EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, size) } {
764            Ok(())
765        } else {
766            Err(())
767        }
768    };
769
770    LcPtr::<EVP_PKEY>::generate(EVP_PKEY_RSA, Some(params_fn))
771}
772
773#[cfg(feature = "fips")]
774#[must_use]
775pub(super) fn is_valid_fips_key(key: &LcPtr<EVP_PKEY>) -> bool {
776    // This should always be an RSA key and must-never panic.
777    let evp_pkey = key.as_const();
778    let rsa_key = evp_pkey.get_rsa().expect("RSA EVP_PKEY");
779
780    1 == unsafe { RSA_check_fips((rsa_key.as_const_ptr()).cast_mut()) }
781}
782
783pub(super) fn is_rsa_key(key: &LcPtr<EVP_PKEY>) -> bool {
784    let id = key.as_const().id();
785    id == EVP_PKEY_RSA || id == EVP_PKEY_RSA_PSS
786}
787
788pub(super) fn validate_rsa_key(key: &LcPtr<EVP_PKEY>) -> Result<(), KeyRejected> {
789    if !is_rsa_key(key) {
790        return Err(KeyRejected::unspecified());
791    }
792    match key.as_const().key_size_bits() {
793        2048..=8192 => Ok(()),
794        0..=2047 => Err(KeyRejected::too_small()),
795        _ => Err(KeyRejected::too_large()),
796    }
797}