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)]
26pub enum RsaPadding {
27 RSA_PKCS1_PADDING,
28 RSA_PKCS1_PSS_PADDING,
29}
30
31pub 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 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 pub fn min_modulus_len(&self) -> u32 {
172 *self.2.start()
173 }
174
175 #[must_use]
176 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)]
210pub 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#[allow(clippy::module_name_repetitions)]
243pub trait RsaEncoding: 'static + Sync + Sealed + Debug {
244 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 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}