Skip to main content

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, EVP_PKEY_CTX_set_signature_md,
9    RSA_bits, EVP_PKEY, EVP_PKEY_CTX, RSA_PKCS1_PSS_PADDING, RSA_PSS_SALTLEN_DIGEST,
10};
11
12use crate::digest::{self, match_digest_type, Digest};
13use crate::error::Unspecified;
14use crate::ptr::LcPtr;
15use crate::rsa::key::parse_rsa_public_key;
16use crate::sealed::Sealed;
17use crate::signature::{ParsedPublicKey, ParsedVerificationAlgorithm, VerificationAlgorithm};
18
19use super::encoding;
20#[cfg(feature = "ring-sig-verify")]
21use untrusted::Input;
22
23#[allow(non_camel_case_types)]
24#[allow(clippy::module_name_repetitions)]
25#[derive(Debug, Eq, PartialEq)]
26pub enum RsaPadding {
27    RSA_PKCS1_PADDING,
28    RSA_PKCS1_PSS_PADDING,
29}
30
31/// Parameters for RSA verification.
32///
33/// Two values are equal when they denote the same verification algorithm, e.g.
34/// `RSA_PKCS1_2048_8192_SHA256`. Algorithms differing in digest algorithm,
35/// padding scheme or modulus size range compare unequal.
36#[derive(Eq, PartialEq)]
37pub struct RsaParameters(
38    &'static digest::Algorithm,
39    &'static RsaPadding,
40    RangeInclusive<u32>,
41    &'static RsaVerificationAlgorithmId,
42);
43
44impl RsaParameters {
45    #[inline]
46    pub(crate) fn digest_algorithm(&self) -> &'static digest::Algorithm {
47        self.0
48    }
49
50    #[inline]
51    pub(crate) fn padding(&self) -> &'static RsaPadding {
52        self.1
53    }
54
55    #[inline]
56    pub(crate) fn bit_size_range(&self) -> &RangeInclusive<u32> {
57        &self.2
58    }
59}
60
61impl ParsedVerificationAlgorithm for RsaParameters {
62    fn parsed_verify_sig(
63        &self,
64        public_key: &ParsedPublicKey,
65        msg: &[u8],
66        signature: &[u8],
67    ) -> Result<(), Unspecified> {
68        let evp_pkey = public_key.key();
69        verify_rsa_signature(
70            self.digest_algorithm(),
71            self.padding(),
72            evp_pkey,
73            msg,
74            signature,
75            self.bit_size_range(),
76        )
77    }
78
79    fn parsed_verify_digest_sig(
80        &self,
81        public_key: &ParsedPublicKey,
82        digest: &Digest,
83        signature: &[u8],
84    ) -> Result<(), Unspecified> {
85        let evp_pkey = public_key.key();
86        verify_rsa_digest_signature(
87            self.digest_algorithm(),
88            self.padding(),
89            evp_pkey,
90            digest,
91            signature,
92            self.bit_size_range(),
93        )
94    }
95}
96
97impl VerificationAlgorithm for RsaParameters {
98    #[cfg(feature = "ring-sig-verify")]
99    fn verify(
100        &self,
101        public_key: Input<'_>,
102        msg: Input<'_>,
103        signature: Input<'_>,
104    ) -> Result<(), Unspecified> {
105        self.verify_sig(
106            public_key.as_slice_less_safe(),
107            msg.as_slice_less_safe(),
108            signature.as_slice_less_safe(),
109        )
110    }
111
112    fn verify_sig(
113        &self,
114        public_key: &[u8],
115        msg: &[u8],
116        signature: &[u8],
117    ) -> Result<(), Unspecified> {
118        let evp_pkey = parse_rsa_public_key(public_key)?;
119        verify_rsa_signature(
120            self.digest_algorithm(),
121            self.padding(),
122            &evp_pkey,
123            msg,
124            signature,
125            self.bit_size_range(),
126        )
127    }
128
129    fn verify_digest_sig(
130        &self,
131        public_key: &[u8],
132        digest: &Digest,
133        signature: &[u8],
134    ) -> Result<(), Unspecified> {
135        let evp_pkey = parse_rsa_public_key(public_key)?;
136        verify_rsa_digest_signature(
137            self.digest_algorithm(),
138            self.padding(),
139            &evp_pkey,
140            digest,
141            signature,
142            self.bit_size_range(),
143        )
144    }
145}
146
147impl Sealed for RsaParameters {}
148
149impl Debug for RsaParameters {
150    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
151        f.write_str(&format!("{{ {:?} }}", self.3))
152    }
153}
154
155impl RsaParameters {
156    pub(crate) const fn new(
157        digest_alg: &'static digest::Algorithm,
158        padding: &'static RsaPadding,
159        range: RangeInclusive<u32>,
160        verification_alg: &'static RsaVerificationAlgorithmId,
161    ) -> Self {
162        Self(digest_alg, padding, range, verification_alg)
163    }
164
165    /// Parses a DER-encoded `RSAPublicKey` structure (RFC 8017) to determine its size in bits.
166    ///
167    /// # Errors
168    /// `error::Unspecified` on parse error.
169    pub fn public_modulus_len(public_key: &[u8]) -> Result<u32, Unspecified> {
170        let rsa = encoding::rfc8017::decode_public_key_der(public_key)?;
171        Ok(unsafe { RSA_bits(rsa.as_const().get_rsa()?.as_const_ptr()) })
172    }
173
174    #[must_use]
175    /// Minimum modulus length in bits.
176    pub fn min_modulus_len(&self) -> u32 {
177        *self.2.start()
178    }
179
180    #[must_use]
181    /// Maximum modulus length in bits.
182    pub fn max_modulus_len(&self) -> u32 {
183        *self.2.end()
184    }
185}
186
187#[derive(Debug, Eq, PartialEq)]
188#[allow(non_camel_case_types)]
189pub(crate) enum RsaVerificationAlgorithmId {
190    RSA_PKCS1_1024_8192_SHA1_FOR_LEGACY_USE_ONLY,
191    RSA_PKCS1_1024_8192_SHA256_FOR_LEGACY_USE_ONLY,
192    RSA_PKCS1_1024_8192_SHA512_FOR_LEGACY_USE_ONLY,
193    RSA_PKCS1_2048_8192_SHA1_FOR_LEGACY_USE_ONLY,
194    RSA_PKCS1_2048_8192_SHA256,
195    RSA_PKCS1_2048_8192_SHA384,
196    RSA_PKCS1_2048_8192_SHA512,
197    RSA_PKCS1_3072_8192_SHA384,
198    RSA_PSS_2048_8192_SHA256,
199    RSA_PSS_2048_8192_SHA384,
200    RSA_PSS_2048_8192_SHA512,
201}
202
203#[derive(Debug)]
204#[allow(non_camel_case_types)]
205pub(crate) enum RsaSigningAlgorithmId {
206    RSA_PSS_SHA256,
207    RSA_PSS_SHA384,
208    RSA_PSS_SHA512,
209    RSA_PKCS1_SHA256,
210    RSA_PKCS1_SHA384,
211    RSA_PKCS1_SHA512,
212}
213
214#[allow(clippy::module_name_repetitions)]
215/// Encoding type for an RSA signature
216pub struct RsaSignatureEncoding(
217    &'static digest::Algorithm,
218    &'static RsaPadding,
219    &'static RsaSigningAlgorithmId,
220);
221
222impl RsaSignatureEncoding {
223    pub(crate) const fn new(
224        digest_alg: &'static digest::Algorithm,
225        padding: &'static RsaPadding,
226        sig_alg: &'static RsaSigningAlgorithmId,
227    ) -> Self {
228        Self(digest_alg, padding, sig_alg)
229    }
230
231    #[inline]
232    pub(super) fn digest_algorithm(&self) -> &'static digest::Algorithm {
233        self.0
234    }
235
236    #[inline]
237    pub(super) fn padding(&self) -> &'static RsaPadding {
238        self.1
239    }
240}
241
242impl Sealed for RsaSignatureEncoding {}
243
244/// An RSA signature encoding as described in [RFC 3447 Section 8].
245///
246/// [RFC 3447 Section 8]: https://tools.ietf.org/html/rfc3447#section-8
247#[allow(clippy::module_name_repetitions)]
248pub trait RsaEncoding: 'static + Sync + Sealed + Debug {
249    /// The signature encoding.
250    fn encoding(&'static self) -> &'static RsaSignatureEncoding;
251}
252
253impl RsaEncoding for RsaSignatureEncoding {
254    fn encoding(&'static self) -> &'static RsaSignatureEncoding {
255        self
256    }
257}
258
259impl Debug for RsaSignatureEncoding {
260    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
261        f.write_str(&format!("{{ {:?} }}", self.2))
262    }
263}
264
265#[inline]
266pub(crate) fn configure_rsa_pkcs1_pss_padding(pctx: *mut EVP_PKEY_CTX) -> Result<(), ()> {
267    if 1 != unsafe { EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) } {
268        return Err(());
269    }
270    if 1 != unsafe { EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, RSA_PSS_SALTLEN_DIGEST) } {
271        return Err(());
272    }
273    Ok(())
274}
275
276#[inline]
277pub(crate) fn verify_rsa_signature(
278    algorithm: &'static digest::Algorithm,
279    padding: &'static RsaPadding,
280    public_key: &LcPtr<EVP_PKEY>,
281    msg: &[u8],
282    signature: &[u8],
283    allowed_bit_size: &RangeInclusive<u32>,
284) -> Result<(), Unspecified> {
285    if !allowed_bit_size.contains(&public_key.as_const().key_size_bits().try_into()?) {
286        return Err(Unspecified);
287    }
288
289    let padding_fn = if let RsaPadding::RSA_PKCS1_PSS_PADDING = padding {
290        Some(configure_rsa_pkcs1_pss_padding)
291    } else {
292        None
293    };
294
295    public_key.verify(msg, Some(algorithm), padding_fn, signature)
296}
297
298#[inline]
299pub(crate) fn verify_rsa_digest_signature(
300    algorithm: &'static digest::Algorithm,
301    padding: &'static RsaPadding,
302    public_key: &LcPtr<EVP_PKEY>,
303    digest: &Digest,
304    signature: &[u8],
305    allowed_bit_size: &RangeInclusive<u32>,
306) -> Result<(), Unspecified> {
307    // Enforced here so no caller can omit it; the ctx below is configured from `digest`.
308    if algorithm != digest.algorithm() {
309        return Err(Unspecified);
310    }
311
312    if !allowed_bit_size.contains(&public_key.as_const().key_size_bits().try_into()?) {
313        return Err(Unspecified);
314    }
315
316    let padding_fn = Some({
317        |pctx: *mut EVP_PKEY_CTX| {
318            let evp_md = match_digest_type(&digest.algorithm().id);
319            if 1 != unsafe { EVP_PKEY_CTX_set_signature_md(pctx, evp_md.as_const_ptr()) } {
320                return Err(());
321            }
322            if let RsaPadding::RSA_PKCS1_PSS_PADDING = padding {
323                configure_rsa_pkcs1_pss_padding(pctx)
324            } else {
325                Ok(())
326            }
327        }
328    });
329
330    public_key.verify_digest_sig(digest, padding_fn, signature)
331}