tlsn_core/fixtures/
transcript.rs

1//! Transcript fixtures for testing.
2
3use aead::Payload as AeadPayload;
4use aes_gcm::{aead::Aead, Aes128Gcm, NewAead};
5#[allow(deprecated)]
6use generic_array::GenericArray;
7use rand::{rngs::StdRng, Rng, SeedableRng};
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, VerifyData},
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        let server_cert_chain = tlsn.server_cert_data.certs;
58        let server_signature = tlsn.server_cert_data.sig;
59        let cert_binding = tlsn.server_cert_data.binding;
60
61        let cf_vd: [u8; 12] = rng.random();
62        let sf_vd: [u8; 12] = rng.random();
63
64        let verify_data = VerifyData {
65            client_finished: cf_vd.to_vec(),
66            server_finished: sf_vd.to_vec(),
67        };
68
69        let sent = self.gen_records(cf_vd, sent);
70        let recv = self.gen_records(sf_vd, recv);
71
72        TlsTranscript::new(
73            time,
74            version,
75            Some(server_cert_chain),
76            Some(server_signature),
77            cert_binding,
78            verify_data,
79            sent,
80            recv,
81        )
82        .unwrap()
83    }
84
85    fn gen_records(&self, vd: [u8; 12], plaintext: &[u8]) -> Vec<Record> {
86        let mut records = Vec::new();
87
88        let handshake = self.gen_handshake(vd);
89        records.push(handshake);
90
91        for (seq, msg) in (1_u64..).zip(plaintext.chunks(RECORD_SIZE)) {
92            let record = self.gen_app_data(seq, msg);
93            records.push(record);
94        }
95
96        records
97    }
98
99    fn gen_app_data(&self, seq: u64, plaintext: &[u8]) -> Record {
100        assert!(
101            plaintext.len() <= 1 << 14,
102            "plaintext len per record must be smaller than 2^14 bytes"
103        );
104
105        let explicit_nonce: [u8; 8] = seq.to_be_bytes();
106        let msg = PlainMessage {
107            typ: ContentType::ApplicationData.into(),
108            version: ProtocolVersion::TLSv1_2,
109            payload: Payload::new(plaintext),
110        };
111        let opaque = aes_gcm_encrypt(self.key, self.iv, seq, explicit_nonce, &msg);
112
113        let mut payload = opaque.payload.0;
114        let mut ciphertext = payload.split_off(8);
115        let tag = ciphertext.split_off(ciphertext.len() - 16);
116
117        Record {
118            seq,
119            typ: ContentType::ApplicationData,
120            plaintext: Some(plaintext.to_vec()),
121            explicit_nonce: explicit_nonce.to_vec(),
122            ciphertext,
123            tag: Some(tag),
124        }
125    }
126
127    fn gen_handshake(&self, vd: [u8; 12]) -> Record {
128        let seq = 0_u64;
129        let explicit_nonce = seq.to_be_bytes();
130
131        let mut plaintext = Vec::new();
132
133        let payload = Payload(vd.to_vec());
134        let hs_payload = HandshakePayload::Finished(payload);
135        let handshake_message = HandshakeMessagePayload {
136            typ: HandshakeType::Finished,
137            payload: hs_payload,
138        };
139        handshake_message.encode(&mut plaintext);
140
141        let msg = PlainMessage {
142            typ: ContentType::Handshake.into(),
143            version: ProtocolVersion::TLSv1_2,
144            payload: Payload::new(plaintext.clone()),
145        };
146
147        let opaque = aes_gcm_encrypt(self.key, self.iv, seq, explicit_nonce, &msg);
148        let mut payload = opaque.payload.0;
149        let mut ciphertext = payload.split_off(8);
150        let tag = ciphertext.split_off(ciphertext.len() - 16);
151
152        Record {
153            seq,
154            typ: ContentType::Handshake,
155            plaintext: Some(plaintext),
156            explicit_nonce: explicit_nonce.to_vec(),
157            ciphertext,
158            tag: Some(tag),
159        }
160    }
161}
162
163fn aes_gcm_encrypt(
164    key: [u8; 16],
165    iv: [u8; 4],
166    seq: u64,
167    explicit_nonce: [u8; 8],
168    msg: &PlainMessage,
169) -> OpaqueMessage {
170    let mut aad = [0u8; 13];
171
172    aad[..8].copy_from_slice(&seq.to_be_bytes());
173    aad[8] = msg.typ.get_u8();
174    aad[9..11].copy_from_slice(&msg.version.get_u16().to_be_bytes());
175    aad[11..13].copy_from_slice(&(msg.payload.0.len() as u16).to_be_bytes());
176    let payload = AeadPayload {
177        msg: &msg.payload.0,
178        aad: &aad,
179    };
180
181    let mut nonce = [0u8; 12];
182    nonce[..4].copy_from_slice(&iv);
183    nonce[4..].copy_from_slice(&explicit_nonce);
184    #[allow(deprecated)]
185    let nonce = GenericArray::from_slice(&nonce);
186    let cipher = Aes128Gcm::new_from_slice(&key).unwrap();
187
188    // ciphertext will have the MAC appended
189    let ciphertext = cipher.encrypt(nonce, payload).unwrap();
190
191    // prepend the explicit nonce
192    let mut nonce_ct_mac = vec![0u8; 0];
193    nonce_ct_mac.extend(explicit_nonce.iter());
194    nonce_ct_mac.extend(ciphertext.iter());
195
196    OpaqueMessage {
197        typ: msg.typ,
198        version: msg.version,
199        payload: Payload::new(nonce_ct_mac),
200    }
201}