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