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::ffi::c_int;
5use std::fmt::{self, Debug, Formatter};
6use std::ops::RangeInclusive;
7
8use crate::aws_lc::{
9    EVP_PKEY_CTX_set_rsa_padding, EVP_PKEY_CTX_set_rsa_pss_saltlen, EVP_PKEY_CTX_set_signature_md,
10    RSA_bits, EVP_PKEY, EVP_PKEY_CTX, RSA_PKCS1_PSS_PADDING, RSA_PSS_SALTLEN_DIGEST,
11};
12
13use crate::digest::{self, match_digest_type, Digest};
14use crate::error::Unspecified;
15use crate::ptr::LcPtr;
16use crate::rsa::key::parse_rsa_public_key;
17use crate::sealed::Sealed;
18use crate::signature::{ParsedPublicKey, ParsedVerificationAlgorithm, VerificationAlgorithm};
19
20use super::encoding;
21#[cfg(feature = "ring-sig-verify")]
22use untrusted::Input;
23
24#[allow(non_camel_case_types)]
25#[allow(clippy::module_name_repetitions)]
26#[derive(Debug, Eq, PartialEq)]
27pub enum RsaPadding {
28    RSA_PKCS1_PADDING,
29    RSA_PKCS1_PSS_PADDING,
30}
31
32/// An RSA-PSS padding salt length.
33///
34/// If a non-default value is used for a signature, then a matching verification
35/// should also use the same value, otherwise it will fail.
36///
37/// Using this type with PSS is FIPS-approved only when the salt length equals
38/// the digest length, i.e. either when [`Self::Digest`] or [`Self::Custom`] is
39/// used, and the latter with that digest length given. [`Self::Max`] or any
40/// other value passed into [`Self::Custom`] will not be FIPS-approved.
41#[allow(clippy::module_name_repetitions)]
42#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
43#[non_exhaustive]
44pub enum RsaPssSaltLength {
45    /// The salt length is set to the length of the used digest algorithm.
46    ///
47    /// Corresponds to [`RSA_PSS_SALTLEN_DIGEST`]. This is the default.
48    #[default]
49    Digest,
50    /// The salt length is set to the maximum possible length with respect to
51    /// the used digest algorithm and public key.
52    ///
53    /// This corresponds to `emLen - hLen - 2`, where
54    ///  * `emLen` is `(pub_key_size_bits + 6) / 8`, so depends on the mod size;
55    ///  * `hLen` is the digest algorithm's output length;
56    ///
57    /// A verification of a signature produced using this salt length may only
58    /// work when also using this maximum length, not any length up to it.
59    Max,
60    /// The salt length is set to the given custom value.
61    ///
62    /// The passed value is not particularly checked here, so may ultimately
63    /// result in failures.
64    Custom(u16),
65}
66
67impl RsaPssSaltLength {
68    pub(crate) fn into_value(
69        self,
70        algorithm: &'static digest::Algorithm,
71        pkey: &LcPtr<EVP_PKEY>,
72    ) -> Result<c_int, ()> {
73        match self {
74            RsaPssSaltLength::Digest => Ok(RSA_PSS_SALTLEN_DIGEST),
75            RsaPssSaltLength::Max => {
76                let digest_len = u16::try_from(algorithm.output_len()).map_err(|_| ())?;
77                let pkey_size_bits =
78                    u16::try_from(pkey.as_const().key_size_bits()).map_err(|_| ())?;
79                let em_len = pkey_size_bits.checked_add(6).ok_or(())? / 8;
80                let max_len = em_len
81                    .checked_sub(digest_len)
82                    .ok_or(())?
83                    .checked_sub(2)
84                    .ok_or(())?;
85                Ok(max_len.into())
86            }
87            RsaPssSaltLength::Custom(val) => Ok(val.into()),
88        }
89    }
90}
91
92/// Parameters for RSA verification.
93///
94/// Two values are equal when they denote the same verification algorithm, e.g.
95/// `RSA_PKCS1_2048_8192_SHA256`. Algorithms differing in digest algorithm,
96/// padding scheme, RSA PSS salt length, or modulus size range compare unequal.
97/// In short, it is equality of configuration, not equality of effective behavior.
98#[derive(Eq, PartialEq)]
99pub struct RsaParameters {
100    digest_alg: &'static digest::Algorithm,
101    padding: &'static RsaPadding,
102    pss_salt_length: RsaPssSaltLength,
103    range: RangeInclusive<u32>,
104    verification_alg: &'static RsaVerificationAlgorithmId,
105}
106
107impl RsaParameters {
108    #[inline]
109    pub(crate) fn digest_algorithm(&self) -> &'static digest::Algorithm {
110        self.digest_alg
111    }
112
113    #[inline]
114    pub(crate) fn padding(&self) -> &'static RsaPadding {
115        self.padding
116    }
117
118    #[inline]
119    pub(crate) fn pss_salt_length(&self) -> RsaPssSaltLength {
120        self.pss_salt_length
121    }
122
123    #[inline]
124    pub(crate) fn bit_size_range(&self) -> &RangeInclusive<u32> {
125        &self.range
126    }
127}
128
129impl ParsedVerificationAlgorithm for RsaParameters {
130    fn parsed_verify_sig(
131        &self,
132        public_key: &ParsedPublicKey,
133        msg: &[u8],
134        signature: &[u8],
135    ) -> Result<(), Unspecified> {
136        let evp_pkey = public_key.key();
137        verify_rsa_signature(
138            self.digest_algorithm(),
139            self.padding(),
140            self.pss_salt_length,
141            evp_pkey,
142            msg,
143            signature,
144            self.bit_size_range(),
145        )
146    }
147
148    fn parsed_verify_digest_sig(
149        &self,
150        public_key: &ParsedPublicKey,
151        digest: &Digest,
152        signature: &[u8],
153    ) -> Result<(), Unspecified> {
154        let evp_pkey = public_key.key();
155        verify_rsa_digest_signature(
156            self.digest_algorithm(),
157            self.padding(),
158            self.pss_salt_length,
159            evp_pkey,
160            digest,
161            signature,
162            self.bit_size_range(),
163        )
164    }
165}
166
167impl VerificationAlgorithm for RsaParameters {
168    #[cfg(feature = "ring-sig-verify")]
169    fn verify(
170        &self,
171        public_key: Input<'_>,
172        msg: Input<'_>,
173        signature: Input<'_>,
174    ) -> Result<(), Unspecified> {
175        self.verify_sig(
176            public_key.as_slice_less_safe(),
177            msg.as_slice_less_safe(),
178            signature.as_slice_less_safe(),
179        )
180    }
181
182    fn verify_sig(
183        &self,
184        public_key: &[u8],
185        msg: &[u8],
186        signature: &[u8],
187    ) -> Result<(), Unspecified> {
188        let evp_pkey = parse_rsa_public_key(public_key)?;
189        verify_rsa_signature(
190            self.digest_algorithm(),
191            self.padding(),
192            self.pss_salt_length,
193            &evp_pkey,
194            msg,
195            signature,
196            self.bit_size_range(),
197        )
198    }
199
200    fn verify_digest_sig(
201        &self,
202        public_key: &[u8],
203        digest: &Digest,
204        signature: &[u8],
205    ) -> Result<(), Unspecified> {
206        let evp_pkey = parse_rsa_public_key(public_key)?;
207        verify_rsa_digest_signature(
208            self.digest_algorithm(),
209            self.padding(),
210            self.pss_salt_length,
211            &evp_pkey,
212            digest,
213            signature,
214            self.bit_size_range(),
215        )
216    }
217}
218
219impl Sealed for RsaParameters {}
220
221impl Debug for RsaParameters {
222    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
223        write!(f, "{{ {:?}", self.verification_alg)?;
224
225        if self.pss_salt_length != RsaPssSaltLength::Digest {
226            write!(f, ", RSA-PSS salt length: {:?}", self.pss_salt_length)?;
227        }
228
229        write!(f, " }}")?;
230        Ok(())
231    }
232}
233
234impl RsaParameters {
235    pub(crate) const fn new(
236        digest_alg: &'static digest::Algorithm,
237        padding: &'static RsaPadding,
238        range: RangeInclusive<u32>,
239        verification_alg: &'static RsaVerificationAlgorithmId,
240    ) -> Self {
241        Self {
242            digest_alg,
243            padding,
244            range,
245            verification_alg,
246            pss_salt_length: RsaPssSaltLength::Digest,
247        }
248    }
249
250    /// Sets the RSA-PSS salt length to the given value.
251    ///
252    /// This is ignored for PKCS#1 v1.5 parameters.
253    ///
254    /// Due to the requirement of `'static` references to the current type in
255    /// verification APIs, it is not possible to directly give the result of
256    /// this method to them, nor simply re-borrow it. Instead either the
257    /// inline `&const { rsa_param.with_pss_salt_length(...) }` expression
258    /// syntax should be used, or the following if Rust below 1.79 is used:
259    ///
260    /// ```
261    /// use aws_lc_rs::signature::{self, RsaPssSaltLength};
262    ///
263    /// const VERIFY_ALG: signature::RsaParameters =
264    ///     signature::RSA_PSS_2048_8192_SHA256.with_pss_salt_length(RsaPssSaltLength::Custom(123));
265    /// // Now use `VERIFY_ALG` instead of `RSA_PSS_2048_8192_SHA256`.
266    /// ```
267    #[must_use]
268    pub const fn with_pss_salt_length(mut self, saltlen: RsaPssSaltLength) -> Self {
269        self.pss_salt_length = saltlen;
270        self
271    }
272
273    /// Parses a DER-encoded `RSAPublicKey` structure (RFC 8017) to determine its size in bits.
274    ///
275    /// # Errors
276    /// `error::Unspecified` on parse error.
277    pub fn public_modulus_len(public_key: &[u8]) -> Result<u32, Unspecified> {
278        let rsa = encoding::rfc8017::decode_public_key_der(public_key)?;
279        Ok(unsafe { RSA_bits(rsa.as_const().get_rsa()?.as_const_ptr()) })
280    }
281
282    #[must_use]
283    /// Minimum modulus length in bits.
284    pub fn min_modulus_len(&self) -> u32 {
285        *self.range.start()
286    }
287
288    #[must_use]
289    /// Maximum modulus length in bits.
290    pub fn max_modulus_len(&self) -> u32 {
291        *self.range.end()
292    }
293}
294
295#[derive(Debug, Eq, PartialEq)]
296#[allow(non_camel_case_types)]
297pub(crate) enum RsaVerificationAlgorithmId {
298    RSA_PKCS1_1024_8192_SHA1_FOR_LEGACY_USE_ONLY,
299    RSA_PKCS1_1024_8192_SHA256_FOR_LEGACY_USE_ONLY,
300    RSA_PKCS1_1024_8192_SHA512_FOR_LEGACY_USE_ONLY,
301    RSA_PKCS1_2048_8192_SHA1_FOR_LEGACY_USE_ONLY,
302    RSA_PKCS1_2048_8192_SHA256,
303    RSA_PKCS1_2048_8192_SHA384,
304    RSA_PKCS1_2048_8192_SHA512,
305    RSA_PKCS1_3072_8192_SHA384,
306    RSA_PSS_2048_8192_SHA256,
307    RSA_PSS_2048_8192_SHA384,
308    RSA_PSS_2048_8192_SHA512,
309}
310
311#[derive(Debug)]
312#[allow(non_camel_case_types)]
313pub(crate) enum RsaSigningAlgorithmId {
314    RSA_PSS_SHA256,
315    RSA_PSS_SHA384,
316    RSA_PSS_SHA512,
317    RSA_PKCS1_SHA256,
318    RSA_PKCS1_SHA384,
319    RSA_PKCS1_SHA512,
320}
321
322#[allow(clippy::module_name_repetitions)]
323/// Encoding type for an RSA signature
324pub struct RsaSignatureEncoding {
325    digest_alg: &'static digest::Algorithm,
326    padding: &'static RsaPadding,
327    pss_salt_length: RsaPssSaltLength,
328    sig_alg: &'static RsaSigningAlgorithmId,
329}
330
331impl RsaSignatureEncoding {
332    pub(crate) const fn new(
333        digest_alg: &'static digest::Algorithm,
334        padding: &'static RsaPadding,
335        sig_alg: &'static RsaSigningAlgorithmId,
336    ) -> Self {
337        Self {
338            digest_alg,
339            padding,
340            sig_alg,
341            pss_salt_length: RsaPssSaltLength::Digest,
342        }
343    }
344
345    #[inline]
346    pub(super) fn digest_algorithm(&self) -> &'static digest::Algorithm {
347        self.digest_alg
348    }
349
350    #[inline]
351    pub(super) fn padding(&self) -> &'static RsaPadding {
352        self.padding
353    }
354
355    #[inline]
356    pub(super) fn pss_salt_length(&self) -> RsaPssSaltLength {
357        self.pss_salt_length
358    }
359
360    /// Sets the PSS salt length to the given value.
361    ///
362    /// This is ignored for PKCS#1 v1.5 parameters.
363    ///
364    /// Due to the requirement of `'static` references to the current type in
365    /// signature APIs, it is not possible to directly give the result of this
366    /// method to them, nor simply re-borrow it. Instead either the inline
367    /// `&const { rsa_param.with_pss_salt_length(...) }` expression syntax
368    /// should be used, or the following if Rust below 1.79 is used:
369    ///
370    /// ```
371    /// use aws_lc_rs::signature::{self, RsaPssSaltLength};
372    ///
373    /// const SIGNING_ALG: signature::RsaSignatureEncoding =
374    ///     signature::RSA_PSS_SHA256.with_pss_salt_length(RsaPssSaltLength::Custom(123));
375    /// // Now use `SIGNING_ALG` instead of `RSA_PSS_SHA256`.
376    /// ```
377    #[must_use]
378    pub const fn with_pss_salt_length(mut self, saltlen: RsaPssSaltLength) -> Self {
379        self.pss_salt_length = saltlen;
380        self
381    }
382}
383
384impl Sealed for RsaSignatureEncoding {}
385
386/// An RSA signature encoding as described in [RFC 3447 Section 8].
387///
388/// [RFC 3447 Section 8]: https://tools.ietf.org/html/rfc3447#section-8
389#[allow(clippy::module_name_repetitions)]
390pub trait RsaEncoding: 'static + Sync + Sealed + Debug {
391    /// The signature encoding.
392    fn encoding(&'static self) -> &'static RsaSignatureEncoding;
393}
394
395impl RsaEncoding for RsaSignatureEncoding {
396    fn encoding(&'static self) -> &'static RsaSignatureEncoding {
397        self
398    }
399}
400
401impl Debug for RsaSignatureEncoding {
402    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
403        write!(f, "{{ {:?}", self.sig_alg)?;
404
405        if self.pss_salt_length != RsaPssSaltLength::Digest {
406            write!(f, ", RSA-PSS salt length: {:?}", self.pss_salt_length)?;
407        }
408
409        write!(f, " }}")?;
410        Ok(())
411    }
412}
413
414#[inline]
415pub(crate) fn configure_rsa_pkcs1_pss_padding(
416    pctx: *mut EVP_PKEY_CTX,
417    saltlen: RsaPssSaltLength,
418    algorithm: &'static digest::Algorithm,
419    pkey: &LcPtr<EVP_PKEY>,
420) -> Result<(), ()> {
421    if 1 != unsafe { EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) } {
422        return Err(());
423    }
424
425    let saltlen = saltlen.into_value(algorithm, pkey)?;
426
427    if 1 != unsafe { EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, saltlen) } {
428        return Err(());
429    }
430
431    Ok(())
432}
433
434#[inline]
435pub(crate) fn verify_rsa_signature(
436    algorithm: &'static digest::Algorithm,
437    padding: &'static RsaPadding,
438    pss_salt_length: RsaPssSaltLength,
439    public_key: &LcPtr<EVP_PKEY>,
440    msg: &[u8],
441    signature: &[u8],
442    allowed_bit_size: &RangeInclusive<u32>,
443) -> Result<(), Unspecified> {
444    if !allowed_bit_size.contains(&public_key.as_const().key_size_bits().try_into()?) {
445        return Err(Unspecified);
446    }
447
448    let padding_fn = if let RsaPadding::RSA_PKCS1_PSS_PADDING = padding {
449        Some(|pctx: *mut EVP_PKEY_CTX| {
450            configure_rsa_pkcs1_pss_padding(pctx, pss_salt_length, algorithm, public_key)
451        })
452    } else {
453        None
454    };
455
456    public_key.verify(msg, Some(algorithm), padding_fn, signature)
457}
458
459#[inline]
460pub(crate) fn verify_rsa_digest_signature(
461    algorithm: &'static digest::Algorithm,
462    padding: &'static RsaPadding,
463    pss_salt_length: RsaPssSaltLength,
464    public_key: &LcPtr<EVP_PKEY>,
465    digest: &Digest,
466    signature: &[u8],
467    allowed_bit_size: &RangeInclusive<u32>,
468) -> Result<(), Unspecified> {
469    // Enforced here so no caller can omit it; the ctx below is configured from `digest`.
470    if algorithm != digest.algorithm() {
471        return Err(Unspecified);
472    }
473
474    if !allowed_bit_size.contains(&public_key.as_const().key_size_bits().try_into()?) {
475        return Err(Unspecified);
476    }
477
478    let padding_fn = Some({
479        |pctx: *mut EVP_PKEY_CTX| {
480            let evp_md = match_digest_type(&digest.algorithm().id);
481            if 1 != unsafe { EVP_PKEY_CTX_set_signature_md(pctx, evp_md.as_const_ptr()) } {
482                return Err(());
483            }
484            if let RsaPadding::RSA_PKCS1_PSS_PADDING = padding {
485                configure_rsa_pkcs1_pss_padding(pctx, pss_salt_length, algorithm, public_key)
486            } else {
487                Ok(())
488            }
489        }
490    });
491
492    public_key.verify_digest_sig(digest, padding_fn, signature)
493}