tlsn_core/fixtures/
transcript.rs

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