Skip to main content

x509_validator/crypto/
rust_crypto.rs

1//! RustCrypto backed crypto backend.
2
3use signature::Verifier;
4
5use crate::crypto::backend::{VerificationAlgorithm, verification_algorithm};
6use crate::crypto::{CryptoError, SignatureVerifier};
7use crate::x509::{AlgorithmIdentifier, SubjectPublicKeyInfo};
8
9/// The RSA modulus sizes, in bytes, this backend will verify against.
10const 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        // `new` refuses to generate below 1024 bits, but the undersized keys are exactly what the
358        // size bound needs to be handed, so bypass the generator's own guard here.
359        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        // An undersized modulus is refused before any signature is considered, so this backend
372        // cannot trust a chain that ring and aws-lc reject on key size alone.
373        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        // Guard against a vacuous test: the smallest supported size must still load, so the
382        // rejections above are the bound talking and not a broken SPKI encoding.
383        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}