Skip to main content

x509_validator/crypto/
mod.rs

1#[cfg(any(feature = "aws_lc", feature = "ring", feature = "rust_crypto"))]
2#[macro_use]
3mod backend;
4
5#[cfg(feature = "aws_lc")]
6pub mod aws_lc;
7#[cfg(feature = "ring")]
8pub mod ring;
9#[cfg(feature = "rust_crypto")]
10pub mod rust_crypto;
11
12use core::fmt::Debug;
13
14use crate::x509::{AlgorithmIdentifier, SubjectPublicKeyInfo};
15use crate::{Any, RsaSsaPssParams, oid_registry};
16
17#[derive(thiserror::Error, Debug)]
18pub enum CryptoError {
19    #[error("invalid key encoding: {0}")]
20    InvalidKey(String),
21    #[error("signature verification failed")]
22    VerificationFailed,
23}
24
25/// Checks one signature over one message, given the signer's SPKI and the
26/// algorithm the signature was made with.
27///
28/// This is the whole contract between the chain-building core and a crypto
29/// library: implement it and any backend drops in, whether or not its keys can
30/// be usefully prepared ahead of the message. Backends that do have a
31/// reusable key type build one inside `verify_signature`; those that carry
32/// per-algorithm state (an OpenSSL digest, a PSS flag) simply keep it in local
33/// variables rather than in a struct that outlives the call.
34pub trait SignatureVerifier: Send + Sync + Debug {
35    fn verify_signature(
36        &self,
37        algorithm: &AlgorithmIdentifier<'_>,
38        public_key: &SubjectPublicKeyInfo<'_>,
39        message: &[u8],
40        signature: &[u8],
41    ) -> Result<(), CryptoError>;
42}
43
44/// A `SignatureVerifier` whose every operation panics, reporting that
45/// no single backend could be determined from the crate's features.
46#[derive(Debug)]
47struct UndeterminedCryptoBackend;
48
49const NO_BACKEND_ERROR: &str = "
50Could not automatically determine the crypto backend from x509-validator crate features.
51Make sure exactly one of the 'aws_lc', 'ring' and 'rust_crypto' features is enabled, or pass a
52provider explicitly to Validator::with_policy_and_backend instead of Validator::with_policy.
53";
54
55impl SignatureVerifier for UndeterminedCryptoBackend {
56    fn verify_signature(
57        &self,
58        _algorithm: &AlgorithmIdentifier<'_>,
59        _public_key: &SubjectPublicKeyInfo<'_>,
60        _message: &[u8],
61        _signature: &[u8],
62    ) -> Result<(), CryptoError> {
63        panic!("{NO_BACKEND_ERROR}")
64    }
65}
66
67/// The crypto backend determined by this crate's feature flags.
68pub fn default_provider() -> &'static dyn SignatureVerifier {
69    #[cfg(all(
70        feature = "aws_lc",
71        not(feature = "ring"),
72        not(feature = "rust_crypto")
73    ))]
74    {
75        return &aws_lc::DEFAULT_PROVIDER;
76    }
77
78    #[cfg(all(
79        feature = "ring",
80        not(feature = "aws_lc"),
81        not(feature = "rust_crypto")
82    ))]
83    {
84        return &ring::DEFAULT_PROVIDER;
85    }
86
87    #[cfg(all(
88        feature = "rust_crypto",
89        not(feature = "aws_lc"),
90        not(feature = "ring")
91    ))]
92    {
93        return &rust_crypto::DEFAULT_PROVIDER;
94    }
95
96    // Reached when zero backends are enabled
97    #[allow(unreachable_code)]
98    {
99        static UNDETERMINED_BACKEND: UndeterminedCryptoBackend = UndeterminedCryptoBackend;
100
101        &UNDETERMINED_BACKEND
102    }
103}
104
105pub fn rsa_pss_digest_bits(params: Option<&Any<'_>>) -> Option<usize> {
106    let params = params?;
107    let params = RsaSsaPssParams::try_from(params).ok()?;
108    let hash_algorithm = params.hash_algorithm_oid();
109
110    if *hash_algorithm == oid_registry::OID_NIST_HASH_SHA256 {
111        Some(256)
112    } else if *hash_algorithm == oid_registry::OID_NIST_HASH_SHA384 {
113        Some(384)
114    } else if *hash_algorithm == oid_registry::OID_NIST_HASH_SHA512 {
115        Some(512)
116    } else {
117        None
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use x509_validator_testkit::rcgen::{CertificateParams, KeyPair};
124
125    use super::*;
126    use crate::{Certificate, CertificateExt, FromDer};
127
128    /// A real self-signed certificate's `AlgorithmIdentifier` and
129    /// `SubjectPublicKeyInfo`, for tests that only need *some* valid values
130    /// of these types rather than to exercise a specific algorithm.
131    fn algorithm_and_spki() -> (AlgorithmIdentifier<'static>, SubjectPublicKeyInfo<'static>) {
132        let key_pair = KeyPair::generate().expect("generate key pair");
133        let der = CertificateParams::default()
134            .self_signed(&key_pair)
135            .expect("self-sign")
136            .der()
137            .to_vec();
138        let der: &'static [u8] = Box::leak(der.into_boxed_slice());
139        let cert = Certificate::parse(der).expect("parse certificate");
140        (cert.signature_algorithm, cert.tbs_certificate.subject_pki)
141    }
142
143    /// Tagged verifier reporting, through the error it returns, which
144    /// algorithm it was handed.
145    #[derive(Debug)]
146    struct TaggedVerifier;
147
148    impl SignatureVerifier for TaggedVerifier {
149        fn verify_signature(
150            &self,
151            algorithm: &AlgorithmIdentifier<'_>,
152            _public_key: &SubjectPublicKeyInfo<'_>,
153            _message: &[u8],
154            _signature: &[u8],
155        ) -> Result<(), CryptoError> {
156            Err(CryptoError::InvalidKey(format!(
157                "{}-was-called",
158                algorithm.algorithm
159            )))
160        }
161    }
162
163    /// Fake verifier that always reports the signature as bad.
164    #[derive(Debug)]
165    struct FailureVerifier;
166
167    impl SignatureVerifier for FailureVerifier {
168        fn verify_signature(
169            &self,
170            _algorithm: &AlgorithmIdentifier<'_>,
171            _public_key: &SubjectPublicKeyInfo<'_>,
172            _message: &[u8],
173            _signature: &[u8],
174        ) -> Result<(), CryptoError> {
175            Err(CryptoError::VerificationFailed)
176        }
177    }
178
179    #[test]
180    fn verify_signature_receives_the_signature_algorithm() {
181        let (algorithm, spki) = algorithm_and_spki();
182
183        let result = TaggedVerifier.verify_signature(&algorithm, &spki, b"message", b"signature");
184
185        match result {
186            Err(CryptoError::InvalidKey(msg)) => {
187                assert_eq!(msg, format!("{}-was-called", algorithm.algorithm));
188            }
189            _ => panic!("Expected InvalidKey error naming the algorithm, got {result:?}"),
190        }
191    }
192
193    #[test]
194    fn verify_signature_propagates_verification_failure() {
195        let (algorithm, spki) = algorithm_and_spki();
196
197        let result = FailureVerifier.verify_signature(&algorithm, &spki, b"message", b"signature");
198
199        assert!(matches!(result, Err(CryptoError::VerificationFailed)));
200    }
201
202    #[test]
203    fn absent_parameters_yield_no_digest() {
204        assert_eq!(rsa_pss_digest_bits(None), None);
205    }
206
207    #[test]
208    fn undecodable_parameters_yield_no_digest() {
209        // A NULL where `RSASSA-PSS-params` (a SEQUENCE) is expected.
210        let params = Any::from_der(&[0x05, 0x00])
211            .expect("parse NULL")
212            .1;
213
214        assert_eq!(rsa_pss_digest_bits(Some(&params)), None);
215    }
216
217    /// DER for `RSASSA-PSS-params` carrying only a `hashAlgorithm` of `oid`.
218    fn pss_params_der(oid_der: &[u8]) -> Vec<u8> {
219        // AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER }
220        let mut algorithm_identifier = vec![0x30, oid_der.len() as u8];
221        algorithm_identifier.extend_from_slice(oid_der);
222
223        // hashAlgorithm is context tag [0], explicit.
224        let mut tagged = vec![0xa0, algorithm_identifier.len() as u8];
225        tagged.extend_from_slice(&algorithm_identifier);
226
227        // RSASSA-PSS-params ::= SEQUENCE { [0] hashAlgorithm ... }
228        let mut params = vec![0x30, tagged.len() as u8];
229        params.extend_from_slice(&tagged);
230        params
231    }
232
233    #[test]
234    fn sha2_hash_algorithms_yield_their_digest_size() {
235        // OIDs 2.16.840.1.101.3.4.2.{1,2,3} = SHA-256 / SHA-384 / SHA-512.
236        for (last_octet, expected) in [(0x01, 256), (0x02, 384), (0x03, 512)] {
237            let oid_der = [
238                0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, last_octet,
239            ];
240            let der = pss_params_der(&oid_der);
241            let params = Any::from_der(&der)
242                .expect("parse PSS params")
243                .1;
244
245            assert_eq!(rsa_pss_digest_bits(Some(&params)), Some(expected));
246        }
247    }
248
249    #[test]
250    fn non_sha2_hash_algorithm_yields_no_digest() {
251        // OID 1.3.14.3.2.26 = SHA-1, which no backend supports for RSA-PSS.
252        let oid_der = [0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a];
253        let der = pss_params_der(&oid_der);
254        let params = Any::from_der(&der)
255            .expect("parse PSS params")
256            .1;
257
258        assert_eq!(rsa_pss_digest_bits(Some(&params)), None);
259    }
260}