aws_lc_rs/rsa/
signature.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0 OR ISC
3
4use std::fmt::{self, Debug, Formatter};
5use std::ops::RangeInclusive;
6
7use crate::aws_lc::{
8    EVP_PKEY_CTX_set_rsa_padding, EVP_PKEY_CTX_set_rsa_pss_saltlen, RSA_bits, EVP_PKEY,
9    EVP_PKEY_CTX, RSA_PKCS1_PSS_PADDING, RSA_PSS_SALTLEN_DIGEST,
10};
11
12use crate::digest::{self};
13use crate::error::Unspecified;
14use crate::ptr::LcPtr;
15use crate::sealed::Sealed;
16use crate::signature::VerificationAlgorithm;
17
18use super::encoding;
19#[cfg(feature = "ring-sig-verify")]
20use untrusted::Input;
21
22#[allow(non_camel_case_types)]
23#[allow(clippy::module_name_repetitions)]
24#[derive(Debug)]
25pub enum RsaPadding {
26    RSA_PKCS1_PADDING,
27    RSA_PKCS1_PSS_PADDING,
28}
29
30/// Parameters for RSA verification.
31pub struct RsaParameters(
32    &'static digest::Algorithm,
33    &'static RsaPadding,
34    RangeInclusive<u32>,
35    &'static RsaVerificationAlgorithmId,
36);
37
38impl RsaParameters {
39    #[inline]
40    pub(crate) fn digest_algorithm(&self) -> &'static digest::Algorithm {
41        self.0
42    }
43
44    #[inline]
45    pub(crate) fn padding(&self) -> &'static RsaPadding {
46        self.1
47    }
48
49    #[inline]
50    pub(crate) fn bit_size_range(&self) -> &RangeInclusive<u32> {
51        &self.2
52    }
53}
54
55impl VerificationAlgorithm for RsaParameters {
56    #[cfg(feature = "ring-sig-verify")]
57    fn verify(
58        &self,
59        public_key: Input<'_>,
60        msg: Input<'_>,
61        signature: Input<'_>,
62    ) -> Result<(), Unspecified> {
63        self.verify_sig(
64            public_key.as_slice_less_safe(),
65            msg.as_slice_less_safe(),
66            signature.as_slice_less_safe(),
67        )
68    }
69
70    fn verify_sig(
71        &self,
72        public_key: &[u8],
73        msg: &[u8],
74        signature: &[u8],
75    ) -> Result<(), Unspecified> {
76        let evp_pkey = encoding::rfc8017::decode_public_key_der(public_key)?;
77        verify_rsa_signature(
78            self.digest_algorithm(),
79            self.padding(),
80            &evp_pkey,
81            msg,
82            signature,
83            self.bit_size_range(),
84        )
85    }
86}
87
88impl Sealed for RsaParameters {}
89
90impl Debug for RsaParameters {
91    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
92        f.write_str(&format!("{{ {:?} }}", self.3))
93    }
94}
95
96impl RsaParameters {
97    pub(crate) const fn new(
98        digest_alg: &'static digest::Algorithm,
99        padding: &'static RsaPadding,
100        range: RangeInclusive<u32>,
101        verification_alg: &'static RsaVerificationAlgorithmId,
102    ) -> Self {
103        Self(digest_alg, padding, range, verification_alg)
104    }
105
106    /// Parses a DER-encoded `RSAPublicKey` structure (RFC 8017) to determine its size in bits.
107    ///
108    /// # Errors
109    /// `error::Unspecified` on parse error.
110    pub fn public_modulus_len(public_key: &[u8]) -> Result<u32, Unspecified> {
111        let rsa = encoding::rfc8017::decode_public_key_der(public_key)?;
112        Ok(unsafe { RSA_bits(*rsa.get_rsa()?) })
113    }
114
115    #[must_use]
116    /// Minimum modulus length in bits.
117    pub fn min_modulus_len(&self) -> u32 {
118        *self.2.start()
119    }
120
121    #[must_use]
122    /// Maximum modulus length in bits.
123    pub fn max_modulus_len(&self) -> u32 {
124        *self.2.end()
125    }
126}
127
128#[derive(Debug)]
129#[allow(non_camel_case_types)]
130pub(crate) enum RsaVerificationAlgorithmId {
131    RSA_PKCS1_1024_8192_SHA1_FOR_LEGACY_USE_ONLY,
132    RSA_PKCS1_1024_8192_SHA256_FOR_LEGACY_USE_ONLY,
133    RSA_PKCS1_1024_8192_SHA512_FOR_LEGACY_USE_ONLY,
134    RSA_PKCS1_2048_8192_SHA1_FOR_LEGACY_USE_ONLY,
135    RSA_PKCS1_2048_8192_SHA256,
136    RSA_PKCS1_2048_8192_SHA384,
137    RSA_PKCS1_2048_8192_SHA512,
138    RSA_PKCS1_3072_8192_SHA384,
139    RSA_PSS_2048_8192_SHA256,
140    RSA_PSS_2048_8192_SHA384,
141    RSA_PSS_2048_8192_SHA512,
142}
143
144#[derive(Debug)]
145#[allow(non_camel_case_types)]
146pub(crate) enum RsaSigningAlgorithmId {
147    RSA_PSS_SHA256,
148    RSA_PSS_SHA384,
149    RSA_PSS_SHA512,
150    RSA_PKCS1_SHA256,
151    RSA_PKCS1_SHA384,
152    RSA_PKCS1_SHA512,
153}
154
155#[allow(clippy::module_name_repetitions)]
156pub struct RsaSignatureEncoding(
157    &'static digest::Algorithm,
158    &'static RsaPadding,
159    &'static RsaSigningAlgorithmId,
160);
161
162impl RsaSignatureEncoding {
163    pub(crate) const fn new(
164        digest_alg: &'static digest::Algorithm,
165        padding: &'static RsaPadding,
166        sig_alg: &'static RsaSigningAlgorithmId,
167    ) -> Self {
168        Self(digest_alg, padding, sig_alg)
169    }
170
171    #[inline]
172    pub(super) fn digest_algorithm(&self) -> &'static digest::Algorithm {
173        self.0
174    }
175
176    #[inline]
177    pub(super) fn padding(&self) -> &'static RsaPadding {
178        self.1
179    }
180}
181
182impl Sealed for RsaSignatureEncoding {}
183
184/// An RSA signature encoding as described in [RFC 3447 Section 8].
185///
186/// [RFC 3447 Section 8]: https://tools.ietf.org/html/rfc3447#section-8
187#[allow(clippy::module_name_repetitions)]
188pub trait RsaEncoding: 'static + Sync + Sealed + Debug {
189    /// The signature encoding.
190    fn encoding(&'static self) -> &'static RsaSignatureEncoding;
191}
192
193impl RsaEncoding for RsaSignatureEncoding {
194    fn encoding(&'static self) -> &'static RsaSignatureEncoding {
195        self
196    }
197}
198
199impl Debug for RsaSignatureEncoding {
200    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
201        f.write_str(&format!("{{ {:?} }}", self.2))
202    }
203}
204
205#[inline]
206pub(crate) fn configure_rsa_pkcs1_pss_padding(pctx: *mut EVP_PKEY_CTX) -> Result<(), ()> {
207    if 1 != unsafe { EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) } {
208        return Err(());
209    }
210    if 1 != unsafe { EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, RSA_PSS_SALTLEN_DIGEST) } {
211        return Err(());
212    }
213    Ok(())
214}
215
216#[inline]
217pub(crate) fn verify_rsa_signature(
218    algorithm: &'static digest::Algorithm,
219    padding: &'static RsaPadding,
220    public_key: &LcPtr<EVP_PKEY>,
221    msg: &[u8],
222    signature: &[u8],
223    allowed_bit_size: &RangeInclusive<u32>,
224) -> Result<(), Unspecified> {
225    if !allowed_bit_size.contains(&public_key.key_size_bits().try_into()?) {
226        return Err(Unspecified);
227    }
228
229    let padding_fn = if let RsaPadding::RSA_PKCS1_PSS_PADDING = padding {
230        Some(configure_rsa_pkcs1_pss_padding)
231    } else {
232        None
233    };
234
235    public_key.verify(msg, Some(algorithm), padding_fn, signature)
236}