1use 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#[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 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 pub fn min_modulus_len(&self) -> u32 {
177 *self.2.start()
178 }
179
180 #[must_use]
181 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)]
215pub 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#[allow(clippy::module_name_repetitions)]
248pub trait RsaEncoding: 'static + Sync + Sealed + Debug {
249 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 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}