Skip to main content

embedded_tls/
webpki.rs

1use crate::TlsError;
2use crate::config::{Certificate, TlsCipherSuite, TlsClock, TlsVerifier};
3use crate::extensions::extension_data::signature_algorithms::SignatureScheme;
4use crate::handshake::{
5    certificate::{
6        Certificate as OwnedCertificate, CertificateEntryRef, CertificateRef as ServerCertificate,
7    },
8    certificate_verify::CertificateVerifyRef,
9};
10use core::marker::PhantomData;
11use digest::Digest;
12use heapless::Vec;
13#[cfg(all(not(feature = "alloc"), feature = "webpki"))]
14impl TryInto<&'static webpki::SignatureAlgorithm> for SignatureScheme {
15    type Error = TlsError;
16    fn try_into(self) -> Result<&'static webpki::SignatureAlgorithm, Self::Error> {
17        // TODO: support other schemes via 'alloc' feature
18        #[allow(clippy::match_same_arms)] // Style
19        match self {
20            SignatureScheme::RsaPkcs1Sha256
21            | SignatureScheme::RsaPkcs1Sha384
22            | SignatureScheme::RsaPkcs1Sha512 => Err(TlsError::InvalidSignatureScheme),
23
24            /* ECDSA algorithms */
25            SignatureScheme::EcdsaSecp256r1Sha256 => Ok(&webpki::ECDSA_P256_SHA256),
26            SignatureScheme::EcdsaSecp384r1Sha384 => Ok(&webpki::ECDSA_P384_SHA384),
27            SignatureScheme::EcdsaSecp521r1Sha512 => Err(TlsError::InvalidSignatureScheme),
28
29            /* RSASSA-PSS algorithms with public key OID rsaEncryption */
30            SignatureScheme::RsaPssRsaeSha256
31            | SignatureScheme::RsaPssRsaeSha384
32            | SignatureScheme::RsaPssRsaeSha512 => Err(TlsError::InvalidSignatureScheme),
33
34            /* EdDSA algorithms */
35            SignatureScheme::Ed25519 => Ok(&webpki::ED25519),
36            SignatureScheme::Ed448
37            | SignatureScheme::Sha224Ecdsa
38            | SignatureScheme::Sha224Rsa
39            | SignatureScheme::Sha224Dsa => Err(TlsError::InvalidSignatureScheme),
40
41            /* RSASSA-PSS algorithms with public key OID RSASSA-PSS */
42            SignatureScheme::RsaPssPssSha256
43            | SignatureScheme::RsaPssPssSha384
44            | SignatureScheme::RsaPssPssSha512 => Err(TlsError::InvalidSignatureScheme),
45
46            /* Legacy algorithms */
47            SignatureScheme::RsaPkcs1Sha1 | SignatureScheme::EcdsaSha1 => {
48                Err(TlsError::InvalidSignatureScheme)
49            }
50
51            /* Ml-DSA */
52            SignatureScheme::MlDsa44 | SignatureScheme::MlDsa65 | SignatureScheme::MlDsa87 => {
53                Err(TlsError::InvalidSignatureScheme)
54            }
55
56            /* Brainpool */
57            SignatureScheme::Sha256BrainpoolP256r1
58            | SignatureScheme::Sha384BrainpoolP384r1
59            | SignatureScheme::Sha512BrainpoolP512r1 => Err(TlsError::InvalidSignatureScheme),
60        }
61    }
62}
63
64#[cfg(all(feature = "alloc", feature = "webpki"))]
65impl TryInto<&'static webpki::SignatureAlgorithm> for SignatureScheme {
66    type Error = TlsError;
67    fn try_into(self) -> Result<&'static webpki::SignatureAlgorithm, Self::Error> {
68        match self {
69            SignatureScheme::RsaPkcs1Sha256 => Ok(&webpki::RSA_PKCS1_2048_8192_SHA256),
70            SignatureScheme::RsaPkcs1Sha384 => Ok(&webpki::RSA_PKCS1_2048_8192_SHA384),
71            SignatureScheme::RsaPkcs1Sha512 => Ok(&webpki::RSA_PKCS1_2048_8192_SHA512),
72
73            /* ECDSA algorithms */
74            SignatureScheme::EcdsaSecp256r1Sha256 => Ok(&webpki::ECDSA_P256_SHA256),
75            SignatureScheme::EcdsaSecp384r1Sha384 => Ok(&webpki::ECDSA_P384_SHA384),
76            SignatureScheme::EcdsaSecp521r1Sha512 => Err(TlsError::InvalidSignatureScheme),
77
78            /* RSASSA-PSS algorithms with public key OID rsaEncryption */
79            SignatureScheme::RsaPssRsaeSha256 => Ok(&webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY),
80            SignatureScheme::RsaPssRsaeSha384 => Ok(&webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY),
81            SignatureScheme::RsaPssRsaeSha512 => Ok(&webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY),
82
83            /* EdDSA algorithms */
84            SignatureScheme::Ed25519 => Ok(&webpki::ED25519),
85            SignatureScheme::Ed448 => Err(TlsError::InvalidSignatureScheme),
86
87            SignatureScheme::Sha224Ecdsa => Err(TlsError::InvalidSignatureScheme),
88            SignatureScheme::Sha224Rsa => Err(TlsError::InvalidSignatureScheme),
89            SignatureScheme::Sha224Dsa => Err(TlsError::InvalidSignatureScheme),
90
91            /* RSASSA-PSS algorithms with public key OID RSASSA-PSS */
92            SignatureScheme::RsaPssPssSha256 => Err(TlsError::InvalidSignatureScheme),
93            SignatureScheme::RsaPssPssSha384 => Err(TlsError::InvalidSignatureScheme),
94            SignatureScheme::RsaPssPssSha512 => Err(TlsError::InvalidSignatureScheme),
95
96            /* Legacy algorithms */
97            SignatureScheme::RsaPkcs1Sha1 => Err(TlsError::InvalidSignatureScheme),
98            SignatureScheme::EcdsaSha1 => Err(TlsError::InvalidSignatureScheme),
99
100            /* MlDsa */
101            SignatureScheme::MlDsa44 => Err(TlsError::InvalidSignatureScheme),
102            SignatureScheme::MlDsa65 => Err(TlsError::InvalidSignatureScheme),
103            SignatureScheme::MlDsa87 => Err(TlsError::InvalidSignatureScheme),
104
105            /* Brainpool */
106            SignatureScheme::Sha256BrainpoolP256r1 => Err(TlsError::InvalidSignatureScheme),
107            SignatureScheme::Sha384BrainpoolP384r1 => Err(TlsError::InvalidSignatureScheme),
108            SignatureScheme::Sha512BrainpoolP512r1 => Err(TlsError::InvalidSignatureScheme),
109        }
110    }
111}
112
113static ALL_SIGALGS: &[&webpki::SignatureAlgorithm] = &[
114    &webpki::ECDSA_P256_SHA256,
115    &webpki::ECDSA_P256_SHA384,
116    &webpki::ECDSA_P384_SHA256,
117    &webpki::ECDSA_P384_SHA384,
118    &webpki::ED25519,
119];
120
121pub struct CertVerifier<'a, CipherSuite, Clock, const CERT_SIZE: usize>
122where
123    Clock: TlsClock,
124    CipherSuite: TlsCipherSuite,
125{
126    ca: Certificate<&'a [u8]>,
127    host: Option<heapless::String<64>>,
128    certificate_transcript: Option<CipherSuite::Hash>,
129    certificate: Option<OwnedCertificate<CERT_SIZE>>,
130    _clock: PhantomData<Clock>,
131}
132
133impl<'a, CipherSuite, Clock, const CERT_SIZE: usize> CertVerifier<'a, CipherSuite, Clock, CERT_SIZE>
134where
135    Clock: TlsClock,
136    CipherSuite: TlsCipherSuite,
137{
138    #[must_use]
139    pub fn new(ca: Certificate<&'a [u8]>) -> Self {
140        Self {
141            ca,
142            host: None,
143            certificate_transcript: None,
144            certificate: None,
145            _clock: PhantomData,
146        }
147    }
148}
149
150impl<CipherSuite, Clock, const CERT_SIZE: usize> TlsVerifier<CipherSuite>
151    for CertVerifier<'_, CipherSuite, Clock, CERT_SIZE>
152where
153    CipherSuite: TlsCipherSuite,
154    Clock: TlsClock,
155{
156    fn set_hostname_verification(&mut self, hostname: &str) -> Result<(), TlsError> {
157        self.host.replace(
158            heapless::String::try_from(hostname).map_err(|_| TlsError::InsufficientSpace)?,
159        );
160        Ok(())
161    }
162
163    fn verify_certificate(
164        &mut self,
165        transcript: &CipherSuite::Hash,
166        cert: ServerCertificate,
167    ) -> Result<(), TlsError> {
168        verify_certificate(self.host.as_deref(), &self.ca, &cert, Clock::now())?;
169        self.certificate.replace(cert.try_into()?);
170        self.certificate_transcript.replace(transcript.clone());
171        Ok(())
172    }
173
174    fn verify_signature(&mut self, verify: CertificateVerifyRef) -> Result<(), TlsError> {
175        let handshake_hash = unwrap!(self.certificate_transcript.take());
176        let ctx_str = b"TLS 1.3, server CertificateVerify\x00";
177        let mut msg: Vec<u8, 130> = Vec::new();
178        msg.resize(64, 0x20).map_err(|_| TlsError::EncodeError)?;
179        msg.extend_from_slice(ctx_str)
180            .map_err(|_| TlsError::EncodeError)?;
181        msg.extend_from_slice(&handshake_hash.finalize())
182            .map_err(|_| TlsError::EncodeError)?;
183
184        let certificate = unwrap!(self.certificate.as_ref()).try_into()?;
185        verify_signature(&msg[..], &certificate, &verify)?;
186        Ok(())
187    }
188}
189
190fn verify_signature(
191    message: &[u8],
192    certificate: &ServerCertificate,
193    verify: &CertificateVerifyRef,
194) -> Result<(), TlsError> {
195    let mut verified = false;
196    if !certificate.entries.is_empty() {
197        // TODO: Support intermediates...
198        if let CertificateEntryRef::X509(certificate) = certificate.entries[0] {
199            let cert = webpki::EndEntityCert::try_from(certificate).map_err(|e| {
200                warn!("Error loading cert: {:?}", e);
201                TlsError::DecodeError
202            })?;
203
204            trace!(
205                "Verifying with signature scheme {:?}",
206                verify.signature_scheme
207            );
208            info!("Signature: {:x?}", verify.signature);
209            let pkisig = verify.signature_scheme.try_into()?;
210            match cert.verify_signature(pkisig, message, verify.signature) {
211                Ok(()) => {
212                    verified = true;
213                }
214                Err(e) => {
215                    info!("Error verifying signature: {:?}", e);
216                }
217            }
218        }
219    }
220    if !verified {
221        return Err(TlsError::InvalidSignature);
222    }
223    Ok(())
224}
225
226fn verify_certificate(
227    verify_host: Option<&str>,
228    ca: &Certificate<&[u8]>,
229    certificate: &ServerCertificate,
230    now: Option<u64>,
231) -> Result<(), TlsError> {
232    let mut verified = false;
233    let mut host_verified = false;
234    if let Certificate::X509(ca) = ca {
235        let trust = webpki::TrustAnchor::try_from_cert_der(ca).map_err(|e| {
236            warn!("Error loading CA: {:?}", e);
237            TlsError::DecodeError
238        })?;
239
240        trace!("We got {} certificate entries", certificate.entries.len());
241
242        if !certificate.entries.is_empty() {
243            // TODO: Support intermediates...
244            if let CertificateEntryRef::X509(certificate) = certificate.entries[0] {
245                let cert = webpki::EndEntityCert::try_from(certificate).map_err(|e| {
246                    warn!("Error loading cert: {:?}", e);
247                    TlsError::DecodeError
248                })?;
249
250                let time = if let Some(now) = now {
251                    webpki::Time::from_seconds_since_unix_epoch(now)
252                } else {
253                    // If no clock is provided, the validity check will fail
254                    webpki::Time::from_seconds_since_unix_epoch(0)
255                };
256                info!("Certificate is loaded!");
257                match cert.verify_for_usage(
258                    ALL_SIGALGS,
259                    &[trust],
260                    &[],
261                    time,
262                    webpki::KeyUsage::server_auth(),
263                    &[],
264                ) {
265                    Ok(()) => verified = true,
266                    Err(e) => {
267                        warn!("Error verifying certificate: {:?}", e);
268                    }
269                }
270
271                if let Some(server_name) = verify_host {
272                    match webpki::SubjectNameRef::try_from_ascii(server_name.as_bytes()) {
273                        Ok(subject) => match cert.verify_is_valid_for_subject_name(subject) {
274                            Ok(()) => host_verified = true,
275                            Err(e) => {
276                                warn!("Error verifying host: {:?}", e);
277                            }
278                        },
279                        Err(e) => {
280                            warn!("Error verifying host: {:?}", e);
281                        }
282                    }
283                }
284            }
285        }
286    }
287
288    if !verified {
289        return Err(TlsError::InvalidCertificate);
290    }
291
292    if !host_verified && verify_host.is_some() {
293        return Err(TlsError::InvalidCertificate);
294    }
295    Ok(())
296}