Skip to main content

tlsn_core/fixtures/
transcript.rs

1//! Transcript fixtures for testing.
2
3use aead::Payload as AeadPayload;
4use aes_gcm::{Aes128Gcm, NewAead, aead::Aead};
5#[allow(deprecated)]
6use generic_array::GenericArray;
7use rand::{Rng, SeedableRng, rngs::StdRng};
8use tls_core::msgs::{
9    base::Payload,
10    codec::Codec,
11    enums::{HandshakeType, ProtocolVersion},
12    handshake::{HandshakeMessagePayload, HandshakePayload},
13    message::{OpaqueMessage, PlainMessage},
14};
15
16use crate::{
17    connection::TranscriptLength,
18    fixtures::ConnectionFixture,
19    transcript::{ContentType, Record, TlsTranscript},
20};
21
22/// The key used for encryption of the sent and received transcript.
23pub const KEY: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
24
25/// The iv used for encryption of the sent and received transcript.
26pub const IV: [u8; 4] = [1, 3, 3, 7];
27
28/// The record size in bytes.
29pub const RECORD_SIZE: usize = 512;
30
31/// Creates a transript fixture for testing.
32pub fn transcript_fixture(sent: &[u8], recv: &[u8]) -> TlsTranscript {
33    TranscriptGenerator::new(KEY, IV).generate(sent, recv)
34}
35
36struct TranscriptGenerator {
37    key: [u8; 16],
38    iv: [u8; 4],
39}
40
41impl TranscriptGenerator {
42    fn new(key: [u8; 16], iv: [u8; 4]) -> Self {
43        Self { key, iv }
44    }
45
46    fn generate(&self, sent: &[u8], recv: &[u8]) -> TlsTranscript {
47        let mut rng = StdRng::from_seed([1; 32]);
48
49        let transcript_len = TranscriptLength {
50            sent: sent.len() as u32,
51            received: recv.len() as u32,
52        };
53        let tlsn = ConnectionFixture::tlsnotary(transcript_len);
54
55        let time = tlsn.connection_info.time;
56        let version = tlsn.connection_info.version;
57
58        let cf_vd: [u8; 12] = rng.random();
59        let sf_vd: [u8; 12] = rng.random();
60
61        let client_finished = self.gen_finished_record(cf_vd);
62        let server_finished = self.gen_finished_record(sf_vd);
63
64        let mut sent = self.gen_app_records(sent);
65        let mut recv = self.gen_app_records(recv);
66
67        sent.insert(0, client_finished);
68        recv.insert(0, server_finished);
69
70        TlsTranscript {
71            time,
72            version,
73            server_signature: Some(tlsn.server_cert_data.sig),
74            server_cert_chain: Some(tlsn.server_cert_data.certs),
75            certificate_binding: tlsn.server_cert_data.binding,
76            cf_hash: None,
77            session_hash: None,
78            sf_hash: None,
79            sent,
80            recv,
81        }
82    }
83
84    fn gen_app_records(&self, plaintext: &[u8]) -> Vec<Record> {
85        (1_u64..)
86            .zip(plaintext.chunks(RECORD_SIZE))
87            .map(|(seq, msg)| self.gen_app_data(seq, msg))
88            .collect()
89    }
90
91    fn gen_app_data(&self, seq: u64, plaintext: &[u8]) -> Record {
92        assert!(
93            plaintext.len() <= 1 << 14,
94            "plaintext len per record must be smaller than 2^14 bytes"
95        );
96
97        let explicit_nonce: [u8; 8] = seq.to_be_bytes();
98        let msg = PlainMessage {
99            typ: ContentType::ApplicationData.into(),
100            version: ProtocolVersion::TLSv1_2,
101            payload: Payload::new(plaintext),
102        };
103        let opaque = aes_gcm_encrypt(self.key, self.iv, seq, explicit_nonce, &msg);
104
105        let mut payload = opaque.payload.0;
106        let mut ciphertext = payload.split_off(8);
107        let tag = ciphertext.split_off(ciphertext.len() - 16);
108
109        Record {
110            seq,
111            typ: ContentType::ApplicationData,
112            plaintext: Some(plaintext.to_vec()),
113            explicit_nonce: explicit_nonce.to_vec(),
114            ciphertext,
115            tag: Some(tag),
116        }
117    }
118
119    fn gen_finished_record(&self, vd: [u8; 12]) -> Record {
120        let seq = 0_u64;
121        let explicit_nonce = seq.to_be_bytes();
122
123        let mut plaintext = Vec::new();
124
125        let payload = Payload(vd.to_vec());
126        let hs_payload = HandshakePayload::Finished(payload);
127        let handshake_message = HandshakeMessagePayload {
128            typ: HandshakeType::Finished,
129            payload: hs_payload,
130        };
131        handshake_message.encode(&mut plaintext);
132
133        let msg = PlainMessage {
134            typ: ContentType::Handshake.into(),
135            version: ProtocolVersion::TLSv1_2,
136            payload: Payload::new(plaintext.clone()),
137        };
138
139        let opaque = aes_gcm_encrypt(self.key, self.iv, seq, explicit_nonce, &msg);
140        let mut payload = opaque.payload.0;
141        let mut ciphertext = payload.split_off(8);
142        let tag = ciphertext.split_off(ciphertext.len() - 16);
143
144        Record {
145            seq: 0,
146            typ: ContentType::Handshake,
147            plaintext: Some(plaintext),
148            explicit_nonce: explicit_nonce.to_vec(),
149            ciphertext,
150            tag: Some(tag),
151        }
152    }
153}
154
155fn aes_gcm_encrypt(
156    key: [u8; 16],
157    iv: [u8; 4],
158    seq: u64,
159    explicit_nonce: [u8; 8],
160    msg: &PlainMessage,
161) -> OpaqueMessage {
162    let mut aad = [0u8; 13];
163
164    aad[..8].copy_from_slice(&seq.to_be_bytes());
165    aad[8] = msg.typ.get_u8();
166    aad[9..11].copy_from_slice(&msg.version.get_u16().to_be_bytes());
167    aad[11..13].copy_from_slice(&(msg.payload.0.len() as u16).to_be_bytes());
168    let payload = AeadPayload {
169        msg: &msg.payload.0,
170        aad: &aad,
171    };
172
173    let mut nonce = [0u8; 12];
174    nonce[..4].copy_from_slice(&iv);
175    nonce[4..].copy_from_slice(&explicit_nonce);
176    #[allow(deprecated)]
177    let nonce = GenericArray::from_slice(&nonce);
178    let cipher = Aes128Gcm::new_from_slice(&key).unwrap();
179
180    // ciphertext will have the MAC appended
181    let ciphertext = cipher.encrypt(nonce, payload).unwrap();
182
183    // prepend the explicit nonce
184    let mut nonce_ct_mac = vec![0u8; 0];
185    nonce_ct_mac.extend(explicit_nonce.iter());
186    nonce_ct_mac.extend(ciphertext.iter());
187
188    OpaqueMessage {
189        typ: msg.typ,
190        version: msg.version,
191        payload: Payload::new(nonce_ct_mac),
192    }
193}