tlsn_core/
connection.rs

1//! TLS connection types.
2
3use std::fmt;
4
5use serde::{Deserialize, Serialize};
6use tls_core::{
7    msgs::{
8        codec::Codec,
9        enums::NamedGroup,
10        handshake::{DigitallySignedStruct, ServerECDHParams},
11    },
12    verify::{ServerCertVerifier as _, WebPkiVerifier},
13};
14use web_time::{Duration, UNIX_EPOCH};
15
16/// TLS version.
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
18#[serde(rename_all = "snake_case")]
19pub enum TlsVersion {
20    /// TLS 1.2.
21    V1_2,
22    /// TLS 1.3.
23    V1_3,
24}
25
26impl TryFrom<tls_core::msgs::enums::ProtocolVersion> for TlsVersion {
27    type Error = &'static str;
28
29    fn try_from(value: tls_core::msgs::enums::ProtocolVersion) -> Result<Self, Self::Error> {
30        Ok(match value {
31            tls_core::msgs::enums::ProtocolVersion::TLSv1_2 => TlsVersion::V1_2,
32            tls_core::msgs::enums::ProtocolVersion::TLSv1_3 => TlsVersion::V1_3,
33            _ => return Err("unsupported TLS version"),
34        })
35    }
36}
37
38/// Server's name, a.k.a. the DNS name.
39#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
40pub struct ServerName(String);
41
42impl ServerName {
43    /// Creates a new server name.
44    pub fn new(name: String) -> Self {
45        Self(name)
46    }
47
48    /// Returns the name as a string.
49    pub fn as_str(&self) -> &str {
50        &self.0
51    }
52}
53
54impl From<&str> for ServerName {
55    fn from(name: &str) -> Self {
56        Self(name.to_string())
57    }
58}
59
60impl AsRef<str> for ServerName {
61    fn as_ref(&self) -> &str {
62        &self.0
63    }
64}
65
66impl fmt::Display for ServerName {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        write!(f, "{}", self.0)
69    }
70}
71
72/// Type of a public key.
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
74#[serde(rename_all = "lowercase")]
75#[non_exhaustive]
76#[allow(non_camel_case_types)]
77pub enum KeyType {
78    /// secp256r1.
79    SECP256R1 = 0x0017,
80}
81
82/// Signature scheme on the key exchange parameters.
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
84#[serde(rename_all = "lowercase")]
85#[allow(non_camel_case_types, missing_docs)]
86pub enum SignatureScheme {
87    RSA_PKCS1_SHA1 = 0x0201,
88    ECDSA_SHA1_Legacy = 0x0203,
89    RSA_PKCS1_SHA256 = 0x0401,
90    ECDSA_NISTP256_SHA256 = 0x0403,
91    RSA_PKCS1_SHA384 = 0x0501,
92    ECDSA_NISTP384_SHA384 = 0x0503,
93    RSA_PKCS1_SHA512 = 0x0601,
94    ECDSA_NISTP521_SHA512 = 0x0603,
95    RSA_PSS_SHA256 = 0x0804,
96    RSA_PSS_SHA384 = 0x0805,
97    RSA_PSS_SHA512 = 0x0806,
98    ED25519 = 0x0807,
99}
100
101impl TryFrom<tls_core::msgs::enums::SignatureScheme> for SignatureScheme {
102    type Error = &'static str;
103
104    fn try_from(value: tls_core::msgs::enums::SignatureScheme) -> Result<Self, Self::Error> {
105        use tls_core::msgs::enums::SignatureScheme as Core;
106        use SignatureScheme::*;
107        Ok(match value {
108            Core::RSA_PKCS1_SHA1 => RSA_PKCS1_SHA1,
109            Core::ECDSA_SHA1_Legacy => ECDSA_SHA1_Legacy,
110            Core::RSA_PKCS1_SHA256 => RSA_PKCS1_SHA256,
111            Core::ECDSA_NISTP256_SHA256 => ECDSA_NISTP256_SHA256,
112            Core::RSA_PKCS1_SHA384 => RSA_PKCS1_SHA384,
113            Core::ECDSA_NISTP384_SHA384 => ECDSA_NISTP384_SHA384,
114            Core::RSA_PKCS1_SHA512 => RSA_PKCS1_SHA512,
115            Core::ECDSA_NISTP521_SHA512 => ECDSA_NISTP521_SHA512,
116            Core::RSA_PSS_SHA256 => RSA_PSS_SHA256,
117            Core::RSA_PSS_SHA384 => RSA_PSS_SHA384,
118            Core::RSA_PSS_SHA512 => RSA_PSS_SHA512,
119            Core::ED25519 => ED25519,
120            _ => return Err("unsupported signature scheme"),
121        })
122    }
123}
124
125impl From<SignatureScheme> for tls_core::msgs::enums::SignatureScheme {
126    fn from(value: SignatureScheme) -> Self {
127        use tls_core::msgs::enums::SignatureScheme::*;
128        match value {
129            SignatureScheme::RSA_PKCS1_SHA1 => RSA_PKCS1_SHA1,
130            SignatureScheme::ECDSA_SHA1_Legacy => ECDSA_SHA1_Legacy,
131            SignatureScheme::RSA_PKCS1_SHA256 => RSA_PKCS1_SHA256,
132            SignatureScheme::ECDSA_NISTP256_SHA256 => ECDSA_NISTP256_SHA256,
133            SignatureScheme::RSA_PKCS1_SHA384 => RSA_PKCS1_SHA384,
134            SignatureScheme::ECDSA_NISTP384_SHA384 => ECDSA_NISTP384_SHA384,
135            SignatureScheme::RSA_PKCS1_SHA512 => RSA_PKCS1_SHA512,
136            SignatureScheme::ECDSA_NISTP521_SHA512 => ECDSA_NISTP521_SHA512,
137            SignatureScheme::RSA_PSS_SHA256 => RSA_PSS_SHA256,
138            SignatureScheme::RSA_PSS_SHA384 => RSA_PSS_SHA384,
139            SignatureScheme::RSA_PSS_SHA512 => RSA_PSS_SHA512,
140            SignatureScheme::ED25519 => ED25519,
141        }
142    }
143}
144
145/// X.509 certificate, DER encoded.
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct Certificate(pub Vec<u8>);
148
149impl From<tls_core::key::Certificate> for Certificate {
150    fn from(cert: tls_core::key::Certificate) -> Self {
151        Self(cert.0)
152    }
153}
154
155/// Server's signature of the key exchange parameters.
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct ServerSignature {
158    /// Signature scheme.
159    pub scheme: SignatureScheme,
160    /// Signature data.
161    pub sig: Vec<u8>,
162}
163
164/// Server's ephemeral public key.
165#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
166pub struct ServerEphemKey {
167    /// Type of the public key.
168    #[serde(rename = "type")]
169    pub typ: KeyType,
170    /// Public key data.
171    pub key: Vec<u8>,
172}
173
174impl ServerEphemKey {
175    /// Encodes the key exchange parameters as in TLS.
176    pub(crate) fn kx_params(&self) -> Vec<u8> {
177        let group = match self.typ {
178            KeyType::SECP256R1 => NamedGroup::secp256r1,
179        };
180
181        let mut kx_params = Vec::new();
182        ServerECDHParams::new(group, &self.key).encode(&mut kx_params);
183
184        kx_params
185    }
186}
187
188impl TryFrom<tls_core::key::PublicKey> for ServerEphemKey {
189    type Error = &'static str;
190
191    fn try_from(value: tls_core::key::PublicKey) -> Result<Self, Self::Error> {
192        let tls_core::msgs::enums::NamedGroup::secp256r1 = value.group else {
193            return Err("unsupported key type");
194        };
195
196        Ok(ServerEphemKey {
197            typ: KeyType::SECP256R1,
198            key: value.key,
199        })
200    }
201}
202
203/// TLS session information.
204#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
205pub struct ConnectionInfo {
206    /// UNIX time when the TLS connection started.
207    pub time: u64,
208    /// TLS version used in the connection.
209    pub version: TlsVersion,
210    /// Transcript length.
211    pub transcript_length: TranscriptLength,
212}
213
214/// Transcript length information.
215#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
216pub struct TranscriptLength {
217    /// Number of bytes sent by the Prover to the Server.
218    pub sent: u32,
219    /// Number of bytes received by the Prover from the Server.
220    pub received: u32,
221}
222
223/// TLS 1.2 handshake data.
224#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct HandshakeDataV1_2 {
226    /// Client random.
227    pub client_random: [u8; 32],
228    /// Server random.
229    pub server_random: [u8; 32],
230    /// Server's ephemeral public key.
231    pub server_ephemeral_key: ServerEphemKey,
232}
233
234/// TLS handshake data.
235#[derive(Debug, Clone, Serialize, Deserialize)]
236#[serde(rename_all = "snake_case")]
237#[non_exhaustive]
238pub enum HandshakeData {
239    /// TLS 1.2 handshake data.
240    V1_2(HandshakeDataV1_2),
241}
242
243/// Verify data from the TLS handshake finished messages.
244#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct VerifyData {
246    /// Client finished verify data.
247    pub client_finished: Vec<u8>,
248    /// Server finished verify data.
249    pub server_finished: Vec<u8>,
250}
251
252/// Server certificate and handshake data.
253#[derive(Debug, Clone, Serialize, Deserialize)]
254pub struct ServerCertData {
255    /// Certificate chain.
256    pub certs: Vec<Certificate>,
257    /// Server signature of the key exchange parameters.
258    pub sig: ServerSignature,
259    /// TLS handshake data.
260    pub handshake: HandshakeData,
261}
262
263impl ServerCertData {
264    /// Verifies the server certificate data.
265    ///
266    /// # Arguments
267    ///
268    /// * `verifier` - Cerificate verifier.
269    /// * `time` - The time of the connection.
270    /// * `server_ephemeral_key` - The server's ephemeral key.
271    /// * `server_name` - The server name.
272    pub fn verify(
273        &self,
274        verifier: &WebPkiVerifier,
275        time: u64,
276        server_ephemeral_key: &ServerEphemKey,
277        server_name: &ServerName,
278    ) -> Result<(), CertificateVerificationError> {
279        #[allow(irrefutable_let_patterns)]
280        let HandshakeData::V1_2(HandshakeDataV1_2 {
281            client_random,
282            server_random,
283            server_ephemeral_key: expected_server_ephemeral_key,
284        }) = &self.handshake
285        else {
286            unreachable!("only TLS 1.2 is implemented")
287        };
288
289        if server_ephemeral_key != expected_server_ephemeral_key {
290            return Err(CertificateVerificationError::InvalidServerEphemeralKey);
291        }
292
293        // Verify server name.
294        let server_name = tls_core::dns::ServerName::try_from(server_name.as_ref())
295            .map_err(|_| CertificateVerificationError::InvalidIdentity(server_name.clone()))?;
296
297        // Verify server certificate.
298        let cert_chain = self
299            .certs
300            .clone()
301            .into_iter()
302            .map(|cert| tls_core::key::Certificate(cert.0))
303            .collect::<Vec<_>>();
304
305        let (end_entity, intermediates) = cert_chain
306            .split_first()
307            .ok_or(CertificateVerificationError::MissingCerts)?;
308
309        // Verify the end entity cert is valid for the provided server name
310        // and that it chains to at least one of the roots we trust.
311        verifier
312            .verify_server_cert(
313                end_entity,
314                intermediates,
315                &server_name,
316                &mut [].into_iter(),
317                &[],
318                UNIX_EPOCH + Duration::from_secs(time),
319            )
320            .map_err(|_| CertificateVerificationError::InvalidCert)?;
321
322        // Verify the signature matches the certificate and key exchange parameters.
323        let mut message = Vec::new();
324        message.extend_from_slice(client_random);
325        message.extend_from_slice(server_random);
326        message.extend_from_slice(&server_ephemeral_key.kx_params());
327
328        let dss = DigitallySignedStruct::new(self.sig.scheme.into(), self.sig.sig.clone());
329
330        verifier
331            .verify_tls12_signature(&message, end_entity, &dss)
332            .map_err(|_| CertificateVerificationError::InvalidServerSignature)?;
333
334        Ok(())
335    }
336}
337
338/// Errors that can occur when verifying a certificate chain or signature.
339#[derive(Debug, thiserror::Error)]
340#[allow(missing_docs)]
341pub enum CertificateVerificationError {
342    #[error("invalid server identity: {0}")]
343    InvalidIdentity(ServerName),
344    #[error("missing server certificates")]
345    MissingCerts,
346    #[error("invalid server certificate")]
347    InvalidCert,
348    #[error("invalid server signature")]
349    InvalidServerSignature,
350    #[error("invalid server ephemeral key")]
351    InvalidServerEphemeralKey,
352}
353
354#[cfg(test)]
355mod tests {
356    use super::*;
357    use crate::{fixtures::ConnectionFixture, transcript::Transcript};
358
359    use hex::FromHex;
360    use rstest::*;
361    use tls_core::{
362        anchors::{OwnedTrustAnchor, RootCertStore},
363        verify::WebPkiVerifier,
364    };
365    use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON};
366
367    #[fixture]
368    #[once]
369    fn verifier() -> WebPkiVerifier {
370        let mut root_store = RootCertStore::empty();
371        root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| {
372            OwnedTrustAnchor::from_subject_spki_name_constraints(
373                ta.subject.as_ref(),
374                ta.subject_public_key_info.as_ref(),
375                ta.name_constraints.as_ref().map(|nc| nc.as_ref()),
376            )
377        }));
378
379        // Add a cert which is no longer included in the Mozilla root store.
380        let cert = tls_core::key::Certificate(
381            appliedzkp()
382                .server_cert_data
383                .certs
384                .last()
385                .expect("chain is valid")
386                .0
387                .clone(),
388        );
389
390        root_store.add(&cert).unwrap();
391
392        WebPkiVerifier::new(root_store, None)
393    }
394
395    fn tlsnotary() -> ConnectionFixture {
396        ConnectionFixture::tlsnotary(Transcript::new(GET_WITH_HEADER, OK_JSON).length())
397    }
398
399    fn appliedzkp() -> ConnectionFixture {
400        ConnectionFixture::appliedzkp(Transcript::new(GET_WITH_HEADER, OK_JSON).length())
401    }
402
403    /// Expect chain verification to succeed.
404    #[rstest]
405    #[case::tlsnotary(tlsnotary())]
406    #[case::appliedzkp(appliedzkp())]
407    fn test_verify_cert_chain_sucess_ca_implicit(
408        verifier: &WebPkiVerifier,
409        #[case] mut data: ConnectionFixture,
410    ) {
411        // Remove the CA cert
412        data.server_cert_data.certs.pop();
413
414        assert!(data
415            .server_cert_data
416            .verify(
417                verifier,
418                data.connection_info.time,
419                data.server_ephemeral_key(),
420                &ServerName::from(data.server_name.as_ref()),
421            )
422            .is_ok());
423    }
424
425    /// Expect chain verification to succeed even when a trusted CA is provided
426    /// among the intermediate certs. webpki handles such cases properly.
427    #[rstest]
428    #[case::tlsnotary(tlsnotary())]
429    #[case::appliedzkp(appliedzkp())]
430    fn test_verify_cert_chain_success_ca_explicit(
431        verifier: &WebPkiVerifier,
432        #[case] data: ConnectionFixture,
433    ) {
434        assert!(data
435            .server_cert_data
436            .verify(
437                verifier,
438                data.connection_info.time,
439                data.server_ephemeral_key(),
440                &ServerName::from(data.server_name.as_ref()),
441            )
442            .is_ok());
443    }
444
445    /// Expect to fail since the end entity cert was not valid at the time.
446    #[rstest]
447    #[case::tlsnotary(tlsnotary())]
448    #[case::appliedzkp(appliedzkp())]
449    fn test_verify_cert_chain_fail_bad_time(
450        verifier: &WebPkiVerifier,
451        #[case] data: ConnectionFixture,
452    ) {
453        // unix time when the cert chain was NOT valid
454        let bad_time: u64 = 1571465711;
455
456        let err = data.server_cert_data.verify(
457            verifier,
458            bad_time,
459            data.server_ephemeral_key(),
460            &ServerName::from(data.server_name.as_ref()),
461        );
462
463        assert!(matches!(
464            err.unwrap_err(),
465            CertificateVerificationError::InvalidCert
466        ));
467    }
468
469    /// Expect to fail when no intermediate cert provided.
470    #[rstest]
471    #[case::tlsnotary(tlsnotary())]
472    #[case::appliedzkp(appliedzkp())]
473    fn test_verify_cert_chain_fail_no_interm_cert(
474        verifier: &WebPkiVerifier,
475        #[case] mut data: ConnectionFixture,
476    ) {
477        // Remove the CA cert
478        data.server_cert_data.certs.pop();
479        // Remove the intermediate cert
480        data.server_cert_data.certs.pop();
481
482        let err = data.server_cert_data.verify(
483            verifier,
484            data.connection_info.time,
485            data.server_ephemeral_key(),
486            &ServerName::from(data.server_name.as_ref()),
487        );
488
489        assert!(matches!(
490            err.unwrap_err(),
491            CertificateVerificationError::InvalidCert
492        ));
493    }
494
495    /// Expect to fail when no intermediate cert provided even if a trusted CA
496    /// cert is provided.
497    #[rstest]
498    #[case::tlsnotary(tlsnotary())]
499    #[case::appliedzkp(appliedzkp())]
500    fn test_verify_cert_chain_fail_no_interm_cert_with_ca_cert(
501        verifier: &WebPkiVerifier,
502        #[case] mut data: ConnectionFixture,
503    ) {
504        // Remove the intermediate cert
505        data.server_cert_data.certs.remove(1);
506
507        let err = data.server_cert_data.verify(
508            verifier,
509            data.connection_info.time,
510            data.server_ephemeral_key(),
511            &ServerName::from(data.server_name.as_ref()),
512        );
513
514        assert!(matches!(
515            err.unwrap_err(),
516            CertificateVerificationError::InvalidCert
517        ));
518    }
519
520    /// Expect to fail because end-entity cert is wrong.
521    #[rstest]
522    #[case::tlsnotary(tlsnotary())]
523    #[case::appliedzkp(appliedzkp())]
524    fn test_verify_cert_chain_fail_bad_ee_cert(
525        verifier: &WebPkiVerifier,
526        #[case] mut data: ConnectionFixture,
527    ) {
528        let ee: &[u8] = include_bytes!("./fixtures/data/unknown/ee.der");
529
530        // Change the end entity cert
531        data.server_cert_data.certs[0] = Certificate(ee.to_vec());
532
533        let err = data.server_cert_data.verify(
534            verifier,
535            data.connection_info.time,
536            data.server_ephemeral_key(),
537            &ServerName::from(data.server_name.as_ref()),
538        );
539
540        assert!(matches!(
541            err.unwrap_err(),
542            CertificateVerificationError::InvalidCert
543        ));
544    }
545
546    /// Expect sig verification to fail because client_random is wrong.
547    #[rstest]
548    #[case::tlsnotary(tlsnotary())]
549    #[case::appliedzkp(appliedzkp())]
550    fn test_verify_sig_ke_params_fail_bad_client_random(
551        verifier: &WebPkiVerifier,
552        #[case] mut data: ConnectionFixture,
553    ) {
554        let HandshakeData::V1_2(HandshakeDataV1_2 { client_random, .. }) =
555            &mut data.server_cert_data.handshake;
556        client_random[31] = client_random[31].wrapping_add(1);
557
558        let err = data.server_cert_data.verify(
559            verifier,
560            data.connection_info.time,
561            data.server_ephemeral_key(),
562            &ServerName::from(data.server_name.as_ref()),
563        );
564
565        assert!(matches!(
566            err.unwrap_err(),
567            CertificateVerificationError::InvalidServerSignature
568        ));
569    }
570
571    /// Expect sig verification to fail because the sig is wrong.
572    #[rstest]
573    #[case::tlsnotary(tlsnotary())]
574    #[case::appliedzkp(appliedzkp())]
575    fn test_verify_sig_ke_params_fail_bad_sig(
576        verifier: &WebPkiVerifier,
577        #[case] mut data: ConnectionFixture,
578    ) {
579        data.server_cert_data.sig.sig[31] = data.server_cert_data.sig.sig[31].wrapping_add(1);
580
581        let err = data.server_cert_data.verify(
582            verifier,
583            data.connection_info.time,
584            data.server_ephemeral_key(),
585            &ServerName::from(data.server_name.as_ref()),
586        );
587
588        assert!(matches!(
589            err.unwrap_err(),
590            CertificateVerificationError::InvalidServerSignature
591        ));
592    }
593
594    /// Expect to fail because the dns name is not in the cert.
595    #[rstest]
596    #[case::tlsnotary(tlsnotary())]
597    #[case::appliedzkp(appliedzkp())]
598    fn test_check_dns_name_present_in_cert_fail_bad_host(
599        verifier: &WebPkiVerifier,
600        #[case] data: ConnectionFixture,
601    ) {
602        let bad_name = ServerName::from("badhost.com");
603
604        let err = data.server_cert_data.verify(
605            verifier,
606            data.connection_info.time,
607            data.server_ephemeral_key(),
608            &bad_name,
609        );
610
611        assert!(matches!(
612            err.unwrap_err(),
613            CertificateVerificationError::InvalidCert
614        ));
615    }
616
617    /// Expect to fail because the ephemeral key provided is wrong.
618    #[rstest]
619    #[case::tlsnotary(tlsnotary())]
620    #[case::appliedzkp(appliedzkp())]
621    fn test_invalid_ephemeral_key(verifier: &WebPkiVerifier, #[case] data: ConnectionFixture) {
622        let wrong_ephemeral_key = ServerEphemKey {
623            typ: KeyType::SECP256R1,
624            key: Vec::<u8>::from_hex(include_bytes!("./fixtures/data/unknown/pubkey")).unwrap(),
625        };
626
627        let err = data.server_cert_data.verify(
628            verifier,
629            data.connection_info.time,
630            &wrong_ephemeral_key,
631            &ServerName::from(data.server_name.as_ref()),
632        );
633
634        assert!(matches!(
635            err.unwrap_err(),
636            CertificateVerificationError::InvalidServerEphemeralKey
637        ));
638    }
639
640    /// Expect to fail when no cert provided.
641    #[rstest]
642    #[case::tlsnotary(tlsnotary())]
643    #[case::appliedzkp(appliedzkp())]
644    fn test_verify_cert_chain_fail_no_cert(
645        verifier: &WebPkiVerifier,
646        #[case] mut data: ConnectionFixture,
647    ) {
648        // Empty certs
649        data.server_cert_data.certs = Vec::new();
650
651        let err = data.server_cert_data.verify(
652            verifier,
653            data.connection_info.time,
654            data.server_ephemeral_key(),
655            &ServerName::from(data.server_name.as_ref()),
656        );
657
658        assert!(matches!(
659            err.unwrap_err(),
660            CertificateVerificationError::MissingCerts
661        ));
662    }
663}