Coverage Report

Created: 2026-05-23 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aws-lc-rs-1.16.2/src/rsa/key.rs
Line
Count
Source
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
5
use super::signature::{RsaEncoding, RsaPadding};
6
use super::{encoding, RsaParameters};
7
use 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")]
13
use crate::aws_lc::{RSA_get0_e, RSA_get0_n};
14
use crate::encoding::{AsDer, Pkcs8V1Der, PublicKeyX509Der};
15
use crate::error::{KeyRejected, Unspecified};
16
#[cfg(feature = "ring-io")]
17
use crate::io;
18
use crate::ptr::{DetachableLcPtr, LcPtr};
19
use crate::rsa::PublicEncryptingKey;
20
use crate::sealed::Sealed;
21
use crate::{hex, rand};
22
#[cfg(feature = "fips")]
23
use aws_lc::RSA_check_fips;
24
use core::fmt::{self, Debug, Formatter};
25
use core::ptr::null_mut;
26
27
// TODO: Uncomment when MSRV >= 1.64
28
// use core::ffi::c_int;
29
use std::os::raw::c_int;
30
31
use crate::digest::{match_digest_type, Digest};
32
use crate::pkcs8::Version;
33
use crate::rsa::encoding::{rfc5280, rfc8017};
34
use crate::rsa::signature::configure_rsa_pkcs1_pss_padding;
35
#[cfg(feature = "ring-io")]
36
use untrusted::Input;
37
use zeroize::Zeroize;
38
39
/// RSA key-size.
40
#[allow(clippy::module_name_repetitions)]
41
#[non_exhaustive]
42
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43
pub 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)]
58
impl KeySize {
59
    /// Returns the size of the key in bytes.
60
    #[inline]
61
    #[must_use]
62
0
    pub fn len(self) -> usize {
63
0
        match self {
64
0
            Self::Rsa2048 => 256,
65
0
            Self::Rsa3072 => 384,
66
0
            Self::Rsa4096 => 512,
67
0
            Self::Rsa8192 => 1024,
68
        }
69
0
    }
70
71
    /// Returns the key size in bits.
72
    #[inline]
73
0
    pub(super) fn bits(self) -> i32 {
74
0
        match self {
75
0
            Self::Rsa2048 => 2048,
76
0
            Self::Rsa3072 => 3072,
77
0
            Self::Rsa4096 => 4096,
78
0
            Self::Rsa8192 => 8192,
79
        }
80
0
    }
81
}
82
83
/// An RSA key pair, used for signing.
84
#[allow(clippy::module_name_repetitions)]
85
pub 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
96
impl Sealed for KeyPair {}
97
unsafe impl Send for KeyPair {}
98
unsafe impl Sync for KeyPair {}
99
100
impl KeyPair {
101
0
    fn new(evp_pkey: LcPtr<EVP_PKEY>) -> Result<Self, KeyRejected> {
102
0
        KeyPair::validate_private_key(&evp_pkey)?;
103
0
        let serialized_public_key = PublicKey::new(&evp_pkey)?;
104
0
        Ok(KeyPair {
105
0
            evp_pkey,
106
0
            serialized_public_key,
107
0
        })
108
0
    }
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
0
    pub fn generate(size: KeySize) -> Result<Self, Unspecified> {
121
0
        let private_key = generate_rsa_key(size.bits())?;
122
0
        Ok(Self::new(private_key)?)
123
0
    }
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
0
    pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, KeyRejected> {
154
0
        let key = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(pkcs8, EVP_PKEY_RSA)?;
155
0
        Self::new(key)
156
0
    }
157
158
    /// Parses a DER-encoded `RSAPrivateKey` structure (RFC 8017).
159
    ///
160
    /// # Errors
161
    /// `error:KeyRejected` on error.
162
0
    pub fn from_der(input: &[u8]) -> Result<Self, KeyRejected> {
163
0
        let key = encoding::rfc8017::decode_private_key_der(input)?;
164
0
        Self::new(key)
165
0
    }
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
0
    fn validate_private_key(key: &LcPtr<EVP_PKEY>) -> Result<(), KeyRejected> {
175
0
        if !is_rsa_key(key) {
176
0
            return Err(KeyRejected::unspecified());
177
0
        }
178
0
        match key.as_const().key_size_bits() {
179
0
            2048..=8192 => Ok(()),
180
0
            _ => Err(KeyRejected::unspecified()),
181
        }
182
0
    }
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
0
    pub fn sign(
204
0
        &self,
205
0
        padding_alg: &'static dyn RsaEncoding,
206
0
        _rng: &dyn rand::SecureRandom,
207
0
        msg: &[u8],
208
0
        signature: &mut [u8],
209
0
    ) -> Result<(), Unspecified> {
210
0
        let encoding = padding_alg.encoding();
211
0
        let padding_fn = if let RsaPadding::RSA_PKCS1_PSS_PADDING = encoding.padding() {
212
0
            Some(configure_rsa_pkcs1_pss_padding)
213
        } else {
214
0
            None
215
        };
216
217
0
        let sig_bytes = self
218
0
            .evp_pkey
219
0
            .sign(msg, Some(encoding.digest_algorithm()), padding_fn)?;
220
221
0
        signature.copy_from_slice(&sig_bytes);
222
0
        Ok(())
223
0
    }
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
0
    pub fn sign_digest(
240
0
        &self,
241
0
        padding_alg: &'static dyn RsaEncoding,
242
0
        digest: &Digest,
243
0
        signature: &mut [u8],
244
0
    ) -> Result<(), Unspecified> {
245
0
        let encoding = padding_alg.encoding();
246
0
        if encoding.digest_algorithm() != digest.algorithm() {
247
0
            return Err(Unspecified);
248
0
        }
249
250
0
        let padding_fn = Some({
251
0
            |pctx: *mut EVP_PKEY_CTX| {
252
0
                let evp_md = match_digest_type(&digest.algorithm().id);
253
0
                if 1 != unsafe { EVP_PKEY_CTX_set_signature_md(pctx, evp_md.as_const_ptr()) } {
254
0
                    return Err(());
255
0
                }
256
0
                if let RsaPadding::RSA_PKCS1_PSS_PADDING = encoding.padding() {
257
0
                    configure_rsa_pkcs1_pss_padding(pctx)
258
                } else {
259
0
                    Ok(())
260
                }
261
0
            }
262
        });
263
264
0
        let sig_bytes = self.evp_pkey.sign_digest(digest, padding_fn)?;
265
266
0
        signature.copy_from_slice(&sig_bytes);
267
0
        Ok(())
268
0
    }
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
0
    pub fn public_modulus_len(&self) -> usize {
275
        // This was already validated to be an RSA key so this can't fail
276
0
        match self.evp_pkey.as_const().get_rsa() {
277
0
            Ok(rsa) => {
278
                // https://github.com/awslabs/aws-lc/blob/main/include/openssl/rsa.h#L99
279
0
                unsafe { RSA_size(rsa.as_const_ptr()) as usize }
280
            }
281
0
            Err(_) => unreachable!(),
282
        }
283
0
    }
284
}
285
286
impl Debug for KeyPair {
287
0
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
288
0
        f.write_str(&format!(
289
0
            "RsaKeyPair {{ public_key: {:?} }}",
290
0
            self.serialized_public_key
291
0
        ))
292
0
    }
293
}
294
295
impl crate::signature::KeyPair for KeyPair {
296
    type PublicKey = PublicKey;
297
298
0
    fn public_key(&self) -> &Self::PublicKey {
299
0
        &self.serialized_public_key
300
0
    }
301
}
302
303
impl AsDer<Pkcs8V1Der<'static>> for KeyPair {
304
0
    fn as_der(&self) -> Result<Pkcs8V1Der<'static>, Unspecified> {
305
0
        Ok(Pkcs8V1Der::new(
306
0
            self.evp_pkey
307
0
                .as_const()
308
0
                .marshal_rfc5208_private_key(Version::V1)?,
309
        ))
310
0
    }
311
}
312
313
/// A serialized RSA public key.
314
#[derive(Clone)]
315
#[allow(clippy::module_name_repetitions)]
316
pub 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
324
impl Drop for PublicKey {
325
0
    fn drop(&mut self) {
326
0
        self.key.zeroize();
327
        #[cfg(feature = "ring-io")]
328
0
        self.modulus.zeroize();
329
        #[cfg(feature = "ring-io")]
330
0
        self.exponent.zeroize();
331
0
    }
332
}
333
334
impl PublicKey {
335
0
    pub(super) fn new(evp_pkey: &LcPtr<EVP_PKEY>) -> Result<Self, KeyRejected> {
336
0
        let key = encoding::rfc8017::encode_public_key_der(evp_pkey)?;
337
        #[cfg(feature = "ring-io")]
338
        {
339
0
            let evp_pkey = evp_pkey.as_const();
340
0
            let pubkey = evp_pkey.get_rsa()?;
341
0
            let modulus = pubkey
342
0
                .project_const_lifetime(unsafe { |pubkey| RSA_get0_n(pubkey.as_const_ptr()) })?;
343
0
            let modulus = modulus.to_be_bytes().into_boxed_slice();
344
0
            let exponent = pubkey
345
0
                .project_const_lifetime(unsafe { |pubkey| RSA_get0_e(pubkey.as_const_ptr()) })?;
346
0
            let exponent = exponent.to_be_bytes().into_boxed_slice();
347
0
            Ok(PublicKey {
348
0
                key,
349
0
                modulus,
350
0
                exponent,
351
0
            })
352
        }
353
354
        #[cfg(not(feature = "ring-io"))]
355
        Ok(PublicKey { key })
356
0
    }
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
0
    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
0
        PublicKey::new(
365
0
            &rfc8017::decode_public_key_der(input).or(rfc5280::decode_public_key_der(input))?,
366
        )
367
0
    }
368
}
369
370
0
pub(crate) fn parse_rsa_public_key(input: &[u8]) -> Result<LcPtr<EVP_PKEY>, KeyRejected> {
371
0
    rfc8017::decode_public_key_der(input).or(rfc5280::decode_public_key_der(input))
372
0
}
373
374
impl Debug for PublicKey {
375
0
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
376
0
        f.write_str(&format!(
377
0
            "RsaPublicKey(\"{}\")",
378
0
            hex::encode(self.key.as_ref())
379
0
        ))
380
0
    }
381
}
382
383
impl AsRef<[u8]> for PublicKey {
384
    /// DER encode a RSA public key to (RFC 8017) `RSAPublicKey` structure.
385
0
    fn as_ref(&self) -> &[u8] {
386
0
        self.key.as_ref()
387
0
    }
388
}
389
390
impl AsDer<PublicKeyX509Der<'static>> for PublicKey {
391
0
    fn as_der(&self) -> Result<PublicKeyX509Der<'static>, Unspecified> {
392
        // TODO: refactor
393
0
        let evp_pkey = rfc8017::decode_public_key_der(self.as_ref())?;
394
0
        rfc5280::encode_public_key_der(&evp_pkey)
395
0
    }
396
}
397
398
#[cfg(feature = "ring-io")]
399
impl PublicKey {
400
    /// The public modulus (n).
401
    #[must_use]
402
0
    pub fn modulus(&self) -> io::Positive<'_> {
403
0
        io::Positive::new_non_empty_without_leading_zeros(Input::from(self.modulus.as_ref()))
404
0
    }
405
406
    /// The public exponent (e).
407
    #[must_use]
408
0
    pub fn exponent(&self) -> io::Positive<'_> {
409
0
        io::Positive::new_non_empty_without_leading_zeros(Input::from(self.exponent.as_ref()))
410
0
    }
411
412
    /// Returns the length in bytes of the public modulus.
413
    #[must_use]
414
0
    pub fn modulus_len(&self) -> usize {
415
0
        self.modulus.len()
416
0
    }
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)]
429
pub struct PublicKeyComponents<B>
430
where
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
439
impl<B: AsRef<[u8]> + Debug> Debug for PublicKeyComponents<B> {
440
0
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
441
0
        f.debug_struct("RsaPublicKeyComponents")
442
0
            .field("n", &self.n)
443
0
            .field("e", &self.e)
444
0
            .finish()
445
0
    }
446
}
447
448
impl<B: Copy + AsRef<[u8]> + Debug> Copy for PublicKeyComponents<B> {}
449
450
impl<B> PublicKeyComponents<B>
451
where
452
    B: AsRef<[u8]> + Debug,
453
{
454
    #[inline]
455
0
    fn build_rsa(&self) -> Result<LcPtr<EVP_PKEY>, ()> {
456
0
        let n_bytes = self.n.as_ref();
457
0
        if n_bytes.is_empty() || n_bytes[0] == 0u8 {
458
0
            return Err(());
459
0
        }
460
0
        let mut n_bn = DetachableLcPtr::try_from(n_bytes)?;
461
462
0
        let e_bytes = self.e.as_ref();
463
0
        if e_bytes.is_empty() || e_bytes[0] == 0u8 {
464
0
            return Err(());
465
0
        }
466
0
        let mut e_bn = DetachableLcPtr::try_from(e_bytes)?;
467
468
0
        let mut rsa = DetachableLcPtr::new(unsafe { RSA_new() })?;
469
0
        if 1 != unsafe {
470
0
            RSA_set0_key(
471
0
                rsa.as_mut_ptr(),
472
0
                n_bn.as_mut_ptr(),
473
0
                e_bn.as_mut_ptr(),
474
0
                null_mut(),
475
0
            )
476
0
        } {
477
0
            return Err(());
478
0
        }
479
0
        n_bn.detach();
480
0
        e_bn.detach();
481
482
0
        let mut pkey = LcPtr::new(unsafe { EVP_PKEY_new() })?;
483
0
        if 1 != unsafe { EVP_PKEY_assign_RSA(pkey.as_mut_ptr(), rsa.as_mut_ptr()) } {
484
0
            return Err(());
485
0
        }
486
0
        rsa.detach();
487
488
0
        Ok(pkey)
489
0
    }
Unexecuted instantiation: <aws_lc_rs::rsa::key::PublicKeyComponents<&[u8]>>::build_rsa
Unexecuted instantiation: <aws_lc_rs::rsa::key::PublicKeyComponents<_>>::build_rsa
490
491
    /// Verifies that `signature` is a valid signature of `message` using `self`
492
    /// as the public key. `params` determine what algorithm parameters
493
    /// (padding, digest algorithm, key length range, etc.) are used in the
494
    /// verification.
495
    ///
496
    /// # Errors
497
    /// `error::Unspecified` if `message` was not verified.
498
0
    pub fn verify(
499
0
        &self,
500
0
        params: &RsaParameters,
501
0
        message: &[u8],
502
0
        signature: &[u8],
503
0
    ) -> Result<(), Unspecified> {
504
0
        let rsa = self.build_rsa()?;
505
0
        super::signature::verify_rsa_signature(
506
0
            params.digest_algorithm(),
507
0
            params.padding(),
508
0
            &rsa,
509
0
            message,
510
0
            signature,
511
0
            params.bit_size_range(),
512
        )
513
0
    }
Unexecuted instantiation: <aws_lc_rs::rsa::key::PublicKeyComponents<&[u8]>>::verify
Unexecuted instantiation: <aws_lc_rs::rsa::key::PublicKeyComponents<_>>::verify
514
}
515
516
#[cfg(feature = "ring-io")]
517
impl From<&PublicKey> for PublicKeyComponents<Vec<u8>> {
518
0
    fn from(public_key: &PublicKey) -> Self {
519
0
        PublicKeyComponents {
520
0
            n: public_key.modulus.to_vec(),
521
0
            e: public_key.exponent.to_vec(),
522
0
        }
523
0
    }
524
}
525
526
impl<B> TryInto<PublicEncryptingKey> for PublicKeyComponents<B>
527
where
528
    B: AsRef<[u8]> + Debug,
529
{
530
    type Error = Unspecified;
531
532
    /// Try to build a `PublicEncryptingKey` from the public key components.
533
    ///
534
    /// # Errors
535
    /// `error::Unspecified` if the key failed to verify.
536
0
    fn try_into(self) -> Result<PublicEncryptingKey, Self::Error> {
537
0
        let rsa = self.build_rsa()?;
538
0
        PublicEncryptingKey::new(rsa)
539
0
    }
540
}
541
542
0
pub(super) fn generate_rsa_key(size: c_int) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
543
0
    let params_fn = |ctx| {
544
0
        if 1 == unsafe { EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, size) } {
545
0
            Ok(())
546
        } else {
547
0
            Err(())
548
        }
549
0
    };
550
551
0
    LcPtr::<EVP_PKEY>::generate(EVP_PKEY_RSA, Some(params_fn))
552
0
}
553
554
#[cfg(feature = "fips")]
555
#[must_use]
556
pub(super) fn is_valid_fips_key(key: &LcPtr<EVP_PKEY>) -> bool {
557
    // This should always be an RSA key and must-never panic.
558
    let evp_pkey = key.as_const();
559
    let rsa_key = evp_pkey.get_rsa().expect("RSA EVP_PKEY");
560
561
    1 == unsafe { RSA_check_fips((rsa_key.as_const_ptr()).cast_mut()) }
562
}
563
564
0
pub(super) fn is_rsa_key(key: &LcPtr<EVP_PKEY>) -> bool {
565
0
    let id = key.as_const().id();
566
0
    id == EVP_PKEY_RSA || id == EVP_PKEY_RSA_PSS
567
0
}