1use signature::Verifier;
4
5use crate::crypto::backend::{VerificationAlgorithm, verification_algorithm};
6use crate::crypto::{CryptoError, SignatureVerifier};
7use crate::x509::{AlgorithmIdentifier, SubjectPublicKeyInfo};
8
9const MIN_RSA_MODULUS_BYTES: usize = 2048 / 8;
11const MAX_RSA_MODULUS_BYTES: usize = 8192 / 8;
12
13#[derive(Debug)]
14pub struct RustCrypto;
15
16impl SignatureVerifier for RustCrypto {
17 fn verify_signature(
18 &self,
19 algorithm: &AlgorithmIdentifier<'_>,
20 public_key: &SubjectPublicKeyInfo<'_>,
21 message: &[u8],
22 signature: &[u8],
23 ) -> Result<(), CryptoError> {
24 let unsupported =
25 || CryptoError::InvalidKey(format!("unsupported algorithm: {}", algorithm.algorithm));
26 let verification_algorithm =
27 verification_algorithm(algorithm, public_key).ok_or_else(unsupported)?;
28
29 let spki_der = &public_key.raw;
30 let key_bytes = public_key.subject_public_key.as_ref();
31
32 match verification_algorithm {
33 VerificationAlgorithm::RsaPkcs1Sha1 => {
34 Self::verify_rsa_pkcs1::<sha1::Sha1>(spki_der, signature, message)
35 }
36 VerificationAlgorithm::RsaPkcs1Sha256 => {
37 Self::verify_rsa_pkcs1::<sha2::Sha256>(spki_der, signature, message)
38 }
39 VerificationAlgorithm::RsaPkcs1Sha384 => {
40 Self::verify_rsa_pkcs1::<sha2::Sha384>(spki_der, signature, message)
41 }
42 VerificationAlgorithm::RsaPkcs1Sha512 => {
43 Self::verify_rsa_pkcs1::<sha2::Sha512>(spki_der, signature, message)
44 }
45 VerificationAlgorithm::RsaPssSha256 => {
46 Self::verify_rsa_pss::<sha2::Sha256>(spki_der, signature, message)
47 }
48 VerificationAlgorithm::RsaPssSha384 => {
49 Self::verify_rsa_pss::<sha2::Sha384>(spki_der, signature, message)
50 }
51 VerificationAlgorithm::RsaPssSha512 => {
52 Self::verify_rsa_pss::<sha2::Sha512>(spki_der, signature, message)
53 }
54 VerificationAlgorithm::EcdsaP256Sha256 => {
55 Self::verify_ecdsa_p256_sha256(key_bytes, signature, message)
56 }
57 VerificationAlgorithm::EcdsaP256Sha384 => {
58 Self::verify_ecdsa_p256_sha384(key_bytes, signature, message)
59 }
60 VerificationAlgorithm::EcdsaP384Sha256 => {
61 Self::verify_ecdsa_p384_sha256(key_bytes, signature, message)
62 }
63 VerificationAlgorithm::EcdsaP384Sha384 => {
64 Self::verify_ecdsa_p384_sha384(key_bytes, signature, message)
65 }
66 VerificationAlgorithm::Ed25519 => Self::verify_ed25519(key_bytes, signature, message),
67 VerificationAlgorithm::EcdsaP256Sha512 | VerificationAlgorithm::EcdsaP384Sha512 => {
68 Err(unsupported())
69 }
70 }
71 }
72}
73
74impl RustCrypto {
75 fn rsa_public_key(spki_der: &[u8]) -> Result<rsa::RsaPublicKey, CryptoError> {
76 use rsa::pkcs8::DecodePublicKey;
77 use rsa::traits::PublicKeyParts;
78
79 let key = rsa::RsaPublicKey::from_public_key_der(spki_der)
80 .map_err(|e| CryptoError::InvalidKey(e.to_string()))?;
81
82 let modulus_bytes = key.size();
83 if !(MIN_RSA_MODULUS_BYTES..=MAX_RSA_MODULUS_BYTES).contains(&modulus_bytes) {
84 return Err(CryptoError::InvalidKey(format!(
85 "RSA modulus of {} bits is outside the supported range of {}-{} bits",
86 modulus_bytes * 8,
87 MIN_RSA_MODULUS_BYTES * 8,
88 MAX_RSA_MODULUS_BYTES * 8
89 )));
90 }
91
92 Ok(key)
93 }
94
95 fn verify_rsa_pkcs1<D>(
96 spki_der: &[u8],
97 signature: &[u8],
98 message: &[u8],
99 ) -> Result<(), CryptoError>
100 where
101 D: sha2::Digest + rsa::pkcs8::AssociatedOid,
102 {
103 let verifying_key = rsa::pkcs1v15::VerifyingKey::<D>::new(Self::rsa_public_key(spki_der)?);
104 let signature = rsa::pkcs1v15::Signature::try_from(signature)
105 .map_err(|_| CryptoError::VerificationFailed)?;
106
107 verifying_key
108 .verify(message, &signature)
109 .map_err(|_| CryptoError::VerificationFailed)
110 }
111
112 fn verify_rsa_pss<D>(
113 spki_der: &[u8],
114 signature: &[u8],
115 message: &[u8],
116 ) -> Result<(), CryptoError>
117 where
118 D: sha2::Digest + sha2::digest::FixedOutputReset,
119 {
120 let verifying_key = rsa::pss::VerifyingKey::<D>::new(Self::rsa_public_key(spki_der)?);
121 let signature = rsa::pss::Signature::try_from(signature)
122 .map_err(|_| CryptoError::VerificationFailed)?;
123
124 verifying_key
125 .verify(message, &signature)
126 .map_err(|_| CryptoError::VerificationFailed)
127 }
128
129 fn verify_ecdsa_p256_sha256(
130 key_bytes: &[u8],
131 signature: &[u8],
132 message: &[u8],
133 ) -> Result<(), CryptoError> {
134 let verifying_key = p256::ecdsa::VerifyingKey::from_sec1_bytes(key_bytes)
135 .map_err(|e| CryptoError::InvalidKey(e.to_string()))?;
136
137 let signature = p256::ecdsa::Signature::from_der(signature)
138 .map_err(|_| CryptoError::VerificationFailed)?;
139
140 verifying_key
141 .verify(message, &signature)
142 .map_err(|_| CryptoError::VerificationFailed)
143 }
144
145 fn verify_ecdsa_p256_sha384(
146 key_bytes: &[u8],
147 signature: &[u8],
148 message: &[u8],
149 ) -> Result<(), CryptoError> {
150 use sha2::Digest as _;
151 use signature::hazmat::PrehashVerifier;
152
153 let verifying_key = p256::ecdsa::VerifyingKey::from_sec1_bytes(key_bytes)
154 .map_err(|e| CryptoError::InvalidKey(e.to_string()))?;
155 let signature = p256::ecdsa::Signature::from_der(signature)
156 .map_err(|_| CryptoError::VerificationFailed)?;
157
158 verifying_key
159 .verify_prehash(&sha2::Sha384::digest(message), &signature)
160 .map_err(|_| CryptoError::VerificationFailed)
161 }
162
163 fn verify_ecdsa_p384_sha256(
164 key_bytes: &[u8],
165 signature: &[u8],
166 message: &[u8],
167 ) -> Result<(), CryptoError> {
168 use sha2::Digest as _;
169 use signature::hazmat::PrehashVerifier;
170
171 let verifying_key = p384::ecdsa::VerifyingKey::from_sec1_bytes(key_bytes)
172 .map_err(|e| CryptoError::InvalidKey(e.to_string()))?;
173 let signature = p384::ecdsa::Signature::from_der(signature)
174 .map_err(|_| CryptoError::VerificationFailed)?;
175
176 verifying_key
177 .verify_prehash(&sha2::Sha256::digest(message), &signature)
178 .map_err(|_| CryptoError::VerificationFailed)
179 }
180
181 fn verify_ecdsa_p384_sha384(
182 key_bytes: &[u8],
183 signature: &[u8],
184 message: &[u8],
185 ) -> Result<(), CryptoError> {
186 let verifying_key = p384::ecdsa::VerifyingKey::from_sec1_bytes(key_bytes)
187 .map_err(|e| CryptoError::InvalidKey(e.to_string()))?;
188 let signature = p384::ecdsa::Signature::from_der(signature)
189 .map_err(|_| CryptoError::VerificationFailed)?;
190
191 verifying_key
192 .verify(message, &signature)
193 .map_err(|_| CryptoError::VerificationFailed)
194 }
195
196 fn verify_ed25519(
197 key_bytes: &[u8],
198 signature: &[u8],
199 message: &[u8],
200 ) -> Result<(), CryptoError> {
201 let verifying_key = ed25519_dalek::VerifyingKey::try_from(key_bytes)
202 .map_err(|e| CryptoError::InvalidKey(e.to_string()))?;
203 let signature = ed25519_dalek::Signature::from_slice(signature)
204 .map_err(|_| CryptoError::VerificationFailed)?;
205
206 verifying_key
207 .verify(message, &signature)
208 .map_err(|_| CryptoError::VerificationFailed)
209 }
210}
211
212pub static DEFAULT_PROVIDER: RustCrypto = RustCrypto;
213
214#[cfg(test)]
215mod tests {
216 use x509_validator_testkit::rcgen::{self, KeyPair};
217 use x509_validator_testkit::self_signed;
218
219 use super::*;
220 use crate::{Certificate, CertificateExt, oid_registry};
221
222 #[test]
223 fn ecdsa_p256_round_trip_verifies() {
224 let key_pair = KeyPair::generate().expect("generate key pair");
225 let der: &'static [u8] = Box::leak(self_signed(&key_pair).into_boxed_slice());
226 let cert = Certificate::parse(der).expect("parse certificate");
227
228 let result = RustCrypto.verify_signature(
229 &cert.signature_algorithm,
230 cert.public_key(),
231 cert.tbs_certificate.as_ref(),
232 cert.signature_value.as_ref(),
233 );
234 assert!(
235 result.is_ok(),
236 "expected valid signature to verify, got {result:?}"
237 );
238 }
239
240 #[test]
241 fn ecdsa_p256_tampered_message_fails() {
242 let key_pair = KeyPair::generate().expect("generate key pair");
243 let der: &'static [u8] = Box::leak(self_signed(&key_pair).into_boxed_slice());
244 let cert = Certificate::parse(der).expect("parse certificate");
245
246 let result = RustCrypto.verify_signature(
247 &cert.signature_algorithm,
248 cert.public_key(),
249 b"tampered message",
250 cert.signature_value.as_ref(),
251 );
252 assert!(matches!(result, Err(CryptoError::VerificationFailed)));
253 }
254
255 #[test]
256 fn unsupported_algorithm_is_rejected() {
257 let algorithm = AlgorithmIdentifier {
258 algorithm: oid_registry::OID_SIG_ED448,
259 parameters: None,
260 };
261 let key_pair = KeyPair::generate().expect("generate key pair");
262 let der: &'static [u8] = Box::leak(self_signed(&key_pair).into_boxed_slice());
263 let cert = Certificate::parse(der).expect("parse certificate");
264
265 let result =
266 RustCrypto.verify_signature(&algorithm, cert.public_key(), b"message", b"signature");
267 assert!(matches!(result, Err(CryptoError::InvalidKey(_))));
268 }
269
270 #[test]
271 fn ecdsa_sha512_is_unsupported() {
272 let algorithm = AlgorithmIdentifier {
273 algorithm: oid_registry::OID_SIG_ECDSA_WITH_SHA512,
274 parameters: None,
275 };
276 let key_pair = KeyPair::generate().expect("generate key pair");
277 let der: &'static [u8] = Box::leak(self_signed(&key_pair).into_boxed_slice());
278 let cert = Certificate::parse(der).expect("parse certificate");
279
280 let result =
281 RustCrypto.verify_signature(&algorithm, cert.public_key(), b"message", b"signature");
282 assert!(matches!(result, Err(CryptoError::InvalidKey(_))));
283 }
284
285 fn assert_round_trip(algorithm: &'static rcgen::SignatureAlgorithm) {
286 let key_pair = KeyPair::generate_for(algorithm).expect("generate key pair");
287 let der: &'static [u8] = Box::leak(self_signed(&key_pair).into_boxed_slice());
288 let cert = Certificate::parse(der).expect("parse certificate");
289
290 let result = RustCrypto.verify_signature(
291 &cert.signature_algorithm,
292 cert.public_key(),
293 cert.tbs_certificate.as_ref(),
294 cert.signature_value.as_ref(),
295 );
296 assert!(
297 result.is_ok(),
298 "expected valid signature to verify, got {result:?}"
299 );
300 }
301
302 fn rsa_key_pair(algorithm: &'static rcgen::SignatureAlgorithm) -> KeyPair {
303 use rsa::pkcs8::EncodePrivateKey;
304
305 static PKCS8_DER: std::sync::OnceLock<Vec<u8>> = std::sync::OnceLock::new();
306
307 let der = PKCS8_DER.get_or_init(|| {
308 let mut rng = rand::rng();
309 let private_key = rsa::RsaPrivateKey::new(&mut rng, 2048).expect("generate RSA key");
310 private_key
311 .to_pkcs8_der()
312 .expect("encode PKCS#8")
313 .as_bytes()
314 .to_vec()
315 });
316
317 KeyPair::from_pkcs8_der_and_sign_algo(&der.as_slice().into(), algorithm)
318 .expect("build RSA key pair")
319 }
320
321 fn assert_rsa_round_trip(algorithm: &'static rcgen::SignatureAlgorithm) {
322 let key_pair = rsa_key_pair(algorithm);
323 let der: &'static [u8] = Box::leak(self_signed(&key_pair).into_boxed_slice());
324 let cert = Certificate::parse(der).expect("parse certificate");
325
326 let result = RustCrypto.verify_signature(
327 &cert.signature_algorithm,
328 cert.public_key(),
329 cert.tbs_certificate.as_ref(),
330 cert.signature_value.as_ref(),
331 );
332 assert!(
333 result.is_ok(),
334 "expected valid signature to verify, got {result:?}"
335 );
336 }
337
338 #[test]
339 fn rsa_pkcs1_sha256_round_trip_verifies() {
340 assert_rsa_round_trip(&rcgen::PKCS_RSA_SHA256);
341 }
342
343 #[test]
344 fn rsa_pkcs1_sha384_round_trip_verifies() {
345 assert_rsa_round_trip(&rcgen::PKCS_RSA_SHA384);
346 }
347
348 #[test]
349 fn rsa_pkcs1_sha512_round_trip_verifies() {
350 assert_rsa_round_trip(&rcgen::PKCS_RSA_SHA512);
351 }
352
353 fn rsa_spki_of_size(bits: usize) -> Vec<u8> {
354 use rsa::pkcs8::EncodePublicKey;
355
356 let mut rng = rand::rng();
357 let private_key =
360 rsa::RsaPrivateKey::new_unchecked(&mut rng, bits).expect("generate RSA key");
361 private_key
362 .to_public_key()
363 .to_public_key_der()
364 .expect("encode SPKI")
365 .as_bytes()
366 .to_vec()
367 }
368
369 #[test]
370 fn rsa_keys_outside_the_supported_size_range_are_refused() {
371 for bits in [512, 1024] {
374 let result = RustCrypto::rsa_public_key(&rsa_spki_of_size(bits));
375 assert!(
376 matches!(result, Err(CryptoError::InvalidKey(_))),
377 "expected {bits}-bit key to be refused, got {result:?}"
378 );
379 }
380
381 assert!(RustCrypto::rsa_public_key(&rsa_spki_of_size(2048)).is_ok());
384 }
385
386 #[test]
387 fn ecdsa_p384_sha384_round_trip_verifies() {
388 assert_round_trip(&rcgen::PKCS_ECDSA_P384_SHA384);
389 }
390
391 #[test]
392 fn ed25519_round_trip_verifies() {
393 assert_round_trip(&rcgen::PKCS_ED25519);
394 }
395}