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