1use const_oid::db::rfc5912;
4use rustls_pki_types as pki_types;
5use sha2::{Digest, Sha256};
6use spki::der::{Decode, oid::ObjectIdentifier};
7use tls_core::msgs::{
8 codec::Reader,
9 enums::{
10 ContentType as TlsContentType, HandshakeType, NamedGroup, ProtocolVersion, SignatureScheme,
11 },
12 handshake::{HandshakeMessagePayload, HandshakePayload, KeyExchangeAlgorithm},
13 message::OpaqueMessage,
14};
15
16use crate::{
17 connection::{
18 CertBinding, CertBindingV1_2, HandshakeData, KeyType, ServerEphemKey, ServerSignature,
19 SignatureAlgorithm, TlsVersion,
20 },
21 webpki::CertificateDer,
22};
23
24use super::{ContentType, Record, TlsTranscript, TlsTranscriptError};
25
26#[derive(Debug, Default)]
28pub struct TlsTranscriptBuilder<'a> {
29 time: Option<u64>,
30 version: Option<TlsVersion>,
31 tls_sent: Option<&'a [u8]>,
32 tls_recv: Option<&'a [u8]>,
33 app_sent: Option<&'a [u8]>,
34 app_recv: Option<&'a [u8]>,
35 records_sent: Option<Vec<Record>>,
36 records_recv: Option<Vec<Record>>,
37 server_signature: Option<ServerSignature>,
38 server_cert_chain: Option<Vec<CertificateDer>>,
39 certificate_binding: Option<CertBinding>,
40}
41
42impl<'a> TlsTranscriptBuilder<'a> {
43 pub fn time(mut self, time: u64) -> Self {
45 self.time = Some(time);
46 self
47 }
48
49 pub fn version(mut self, version: TlsVersion) -> Self {
51 self.version = Some(version);
52 self
53 }
54
55 pub fn tls_sent(mut self, data: &'a [u8]) -> Self {
57 self.tls_sent = Some(data);
58 self
59 }
60
61 pub fn tls_recv(mut self, recv: &'a [u8]) -> Self {
63 self.tls_recv = Some(recv);
64 self
65 }
66
67 pub fn app_sent(mut self, sent: &'a [u8]) -> Self {
69 self.app_sent = Some(sent);
70 self
71 }
72
73 pub fn app_recv(mut self, recv: &'a [u8]) -> Self {
75 self.app_recv = Some(recv);
76 self
77 }
78
79 pub fn records_sent(mut self, sent: Vec<Record>) -> Self {
81 self.records_sent = Some(sent);
82 self
83 }
84
85 pub fn records_recv(mut self, recv: Vec<Record>) -> Self {
88 self.records_recv = Some(recv);
89 self
90 }
91
92 pub fn server_signature(mut self, sig: ServerSignature) -> Self {
94 self.server_signature = Some(sig);
95 self
96 }
97
98 pub fn server_cert_chain(mut self, chain: Vec<CertificateDer>) -> Self {
100 self.server_cert_chain = Some(chain);
101 self
102 }
103
104 pub fn certificate_binding(mut self, binding: CertBinding) -> Self {
106 self.certificate_binding = Some(binding);
107 self
108 }
109
110 pub fn build(mut self) -> Result<TlsTranscript, TlsTranscriptError> {
114 let time = self
115 .time
116 .ok_or_else(|| TlsTranscriptError::missing("time"))?;
117
118 let sent_raw = if let Some(tls_sent) = self.tls_sent {
119 Some(parse_raw_records(tls_sent)?)
120 } else {
121 None
122 };
123
124 let recv_raw = if let Some(tls_recv) = self.tls_recv {
125 Some(parse_raw_records(tls_recv)?)
126 } else {
127 None
128 };
129
130 let (cf_hash, session_hash, sf_hash) = if let Some(sent_raw) = &sent_raw
131 && let Some(recv_raw) = &recv_raw
132 {
133 let (cf_hash, session_hash, sf_hash) =
134 self.parse_handshake_components(sent_raw, recv_raw)?;
135 (Some(cf_hash), Some(session_hash), Some(sf_hash))
136 } else {
137 (None, None, None)
138 };
139
140 let sent = if let Some(records_sent) = self.records_sent {
141 let client_finished = records_sent
142 .first()
143 .expect("client finished record should be available");
144 validate_finished_record(client_finished)?;
145 records_sent
146 } else if let Some(sent_raw) = sent_raw {
147 parse_records(&sent_raw, self.app_sent)?
148 } else {
149 return Err(TlsTranscriptError::missing("sent records"));
150 };
151 validate_seq(&sent)?;
152
153 let recv = if let Some(records_recv) = self.records_recv {
154 let server_finished = records_recv
155 .first()
156 .expect("server finished record should be available");
157 validate_finished_record(server_finished)?;
158 records_recv
159 } else if let Some(recv_raw) = recv_raw {
160 parse_records(&recv_raw, self.app_recv)?
161 } else {
162 return Err(TlsTranscriptError::missing("recv records"));
163 };
164 validate_seq(&recv)?;
165
166 let version = self
167 .version
168 .ok_or_else(|| TlsTranscriptError::missing("version"))?;
169 let certificate_binding = self
170 .certificate_binding
171 .ok_or_else(|| TlsTranscriptError::missing("certificate binding"))?;
172
173 let transcript = TlsTranscript {
174 time,
175 version,
176 server_signature: self.server_signature,
177 server_cert_chain: self.server_cert_chain,
178 certificate_binding,
179 cf_hash,
180 session_hash,
181 sf_hash,
182 sent,
183 recv,
184 };
185
186 Ok(transcript)
187 }
188
189 fn parse_handshake_components(
191 &mut self,
192 sent_records: &[OpaqueMessage],
193 recv_records: &[OpaqueMessage],
194 ) -> Result<([u8; 32], [u8; 32], SfHashInput), TlsTranscriptError> {
195 let (sent_hs_bytes, sent_hs) = parse_handshake_stream(sent_records)?;
196 let (recv_hs_bytes, recv_hs) = parse_handshake_stream(recv_records)?;
197
198 let (version, handshake) = extract_handshake(&sent_hs, &recv_hs)?;
199
200 let sent_msgs = scan_handshake_messages(&sent_hs_bytes)?;
201 let recv_msgs = scan_handshake_messages(&recv_hs_bytes)?;
202 let (cf_hash, session_hash, sf_hash_input) =
203 compute_handshake_hashes(sent_hs_bytes, recv_hs_bytes, &sent_msgs, &recv_msgs)?;
204
205 if self.version.is_none() {
206 self.version = Some(version);
207 }
208 if self.server_signature.is_none() {
209 self.server_signature = Some(handshake.sig);
210 }
211 if self.server_cert_chain.is_none() {
212 self.server_cert_chain = Some(handshake.certs);
213 }
214 if self.certificate_binding.is_none() {
215 self.certificate_binding = Some(handshake.binding);
216 }
217
218 Ok((cf_hash, session_hash, sf_hash_input))
219 }
220}
221
222#[derive(Debug, Clone)]
223pub(crate) struct SfHashInput {
224 pub(crate) sent_hs_bytes: Vec<u8>,
225 pub(crate) recv_hs_bytes: Vec<u8>,
226 pub(crate) sent_ch_end: usize,
227 pub(crate) recv_shd_end: usize,
228}
229
230const NONCE_LEN: usize = 8;
231const TAG_LEN: usize = 16;
232
233fn parse_raw_records(bytes: &[u8]) -> Result<Vec<OpaqueMessage>, TlsTranscriptError> {
235 let mut reader = Reader::init(bytes);
236 let mut records = Vec::new();
237 while reader.any_left() {
238 let msg = OpaqueMessage::read(&mut reader)
239 .map_err(|e| TlsTranscriptError::parse(format!("failed to read TLS record: {e:?}")))?;
240 records.push(msg);
241 }
242 Ok(records)
243}
244
245fn parse_handshake_stream(
248 records: &[OpaqueMessage],
249) -> Result<(Vec<u8>, Vec<HandshakeMessagePayload>), TlsTranscriptError> {
250 let handshake_bytes = collect_handshake_bytes_pre_ccs(records);
251
252 let mut reader = Reader::init(&handshake_bytes);
253 let mut messages = Vec::new();
254 while reader.any_left() {
255 let msg = HandshakeMessagePayload::read_version(&mut reader, ProtocolVersion::TLSv1_2)
256 .ok_or_else(|| TlsTranscriptError::parse("failed to parse handshake message"))?;
257 messages.push(msg);
258 }
259 Ok((handshake_bytes, messages))
260}
261
262fn compute_handshake_hashes(
266 sent_hs_bytes: Vec<u8>,
267 recv_hs_bytes: Vec<u8>,
268 sent_msgs: &[HandshakeMsgInfo],
269 recv_msgs: &[HandshakeMsgInfo],
270) -> Result<([u8; 32], [u8; 32], SfHashInput), TlsTranscriptError> {
271 let client_hello = sent_msgs
272 .first()
273 .ok_or_else(|| TlsTranscriptError::parse("missing ClientHello in sent handshake stream"))?;
274 if client_hello.typ != HandshakeType::ClientHello {
275 return Err(TlsTranscriptError::parse(
276 "first sent handshake message is not ClientHello",
277 ));
278 }
279 let ckx = sent_msgs
280 .iter()
281 .find(|m| m.typ == HandshakeType::ClientKeyExchange)
282 .ok_or_else(|| TlsTranscriptError::parse("missing ClientKeyExchange"))?;
283 if !recv_msgs
284 .iter()
285 .any(|m| m.typ == HandshakeType::ServerHello)
286 {
287 return Err(TlsTranscriptError::parse("missing ServerHello"));
288 }
289 let shd = recv_msgs
290 .iter()
291 .find(|m| m.typ == HandshakeType::ServerHelloDone)
292 .ok_or_else(|| TlsTranscriptError::parse("missing ServerHelloDone"))?;
293
294 let sent_ch_end = client_hello.end;
295 let recv_shd_end = shd.end;
296
297 let mut hasher = Sha256::new();
300 hasher.update(&sent_hs_bytes[..sent_ch_end]);
301 hasher.update(&recv_hs_bytes);
302 hasher.update(&sent_hs_bytes[sent_ch_end..ckx.end]);
303 let session_hash: [u8; 32] = hasher.finalize().into();
304
305 let mut hasher = Sha256::new();
309 hasher.update(&sent_hs_bytes[..sent_ch_end]);
310 hasher.update(&recv_hs_bytes[..recv_shd_end]);
311 hasher.update(&sent_hs_bytes[sent_ch_end..]);
312 let cf_hash: [u8; 32] = hasher.finalize().into();
313
314 Ok((
315 cf_hash,
316 session_hash,
317 SfHashInput {
318 sent_hs_bytes,
319 recv_hs_bytes,
320 sent_ch_end,
321 recv_shd_end,
322 },
323 ))
324}
325
326fn collect_handshake_bytes_pre_ccs(records: &[OpaqueMessage]) -> Vec<u8> {
330 let mut handshake_bytes = Vec::new();
331 for record in records {
332 if record.typ == TlsContentType::ChangeCipherSpec {
333 break;
334 }
335 if record.typ == TlsContentType::Handshake {
336 handshake_bytes.extend_from_slice(&record.payload.0);
337 }
338 }
339 handshake_bytes
340}
341
342struct HandshakeMsgInfo {
345 typ: HandshakeType,
346 end: usize,
349}
350
351fn scan_handshake_messages(bytes: &[u8]) -> Result<Vec<HandshakeMsgInfo>, TlsTranscriptError> {
354 let mut out = Vec::new();
355 let mut pos = 0;
356 while pos < bytes.len() {
357 if bytes.len() - pos < 4 {
358 return Err(TlsTranscriptError::parse("truncated handshake header"));
359 }
360 let typ = HandshakeType::from(bytes[pos]);
361 let len = u32::from_be_bytes([0, bytes[pos + 1], bytes[pos + 2], bytes[pos + 3]]) as usize;
362 let msg_end = pos + 4 + len;
363 if msg_end > bytes.len() {
364 return Err(TlsTranscriptError::parse(
365 "handshake message length overflows buffer",
366 ));
367 }
368 out.push(HandshakeMsgInfo { typ, end: msg_end });
369 pos = msg_end;
370 }
371 Ok(out)
372}
373
374fn split_into_record(
376 seq: u64,
377 payload: &[u8],
378 typ: TlsContentType,
379) -> Result<Record, TlsTranscriptError> {
380 let typ = ContentType::from(typ);
381
382 if payload.len() < NONCE_LEN + TAG_LEN {
383 return Err(TlsTranscriptError::parse("encrypted record too short"));
384 }
385
386 Ok(Record {
387 seq,
388 typ,
389 plaintext: None,
390 explicit_nonce: payload[..NONCE_LEN].to_vec(),
391 ciphertext: payload[NONCE_LEN..payload.len() - TAG_LEN].to_vec(),
392 tag: Some(payload[payload.len() - TAG_LEN..].to_vec()),
393 })
394}
395
396fn extract_handshake(
398 sent_hs: &[HandshakeMessagePayload],
399 recv_hs: &[HandshakeMessagePayload],
400) -> Result<(TlsVersion, HandshakeData), TlsTranscriptError> {
401 let (version, server_random) = extract_server_hello_data(recv_hs)?;
402 let client_random = extract_client_random(sent_hs)?;
403 let certs = extract_certs(recv_hs)?;
404 let (server_ephemeral_key, sig) = extract_server_key_exchange(recv_hs, &certs)?;
405
406 let binding = CertBinding::V1_2(CertBindingV1_2 {
407 client_random,
408 server_random,
409 server_ephemeral_key,
410 });
411
412 let handshake = HandshakeData {
413 certs,
414 sig,
415 binding,
416 };
417
418 Ok((version, handshake))
419}
420
421fn extract_server_hello_data(
423 recv_hs: &[HandshakeMessagePayload],
424) -> Result<(TlsVersion, [u8; 32]), TlsTranscriptError> {
425 let server_hello = recv_hs
426 .iter()
427 .find_map(|msg| match &msg.payload {
428 HandshakePayload::ServerHello(sh) => Some(sh),
429 _ => None,
430 })
431 .ok_or_else(|| TlsTranscriptError::parse("missing ServerHello"))?;
432
433 let version = TlsVersion::try_from(server_hello.legacy_version)
434 .map_err(|e| TlsTranscriptError::parse(format!("unsupported TLS version: {e}")))?;
435
436 Ok((version, server_hello.random.0))
437}
438
439fn extract_client_random(
440 sent_hs: &[HandshakeMessagePayload],
441) -> Result<[u8; 32], TlsTranscriptError> {
442 let client_hello = sent_hs
443 .iter()
444 .find_map(|msg| match &msg.payload {
445 HandshakePayload::ClientHello(ch) => Some(ch),
446 _ => None,
447 })
448 .ok_or_else(|| TlsTranscriptError::parse("missing ClientHello"))?;
449
450 Ok(client_hello.random.0)
451}
452
453fn extract_certs(
454 recv_hs: &[HandshakeMessagePayload],
455) -> Result<Vec<CertificateDer>, TlsTranscriptError> {
456 let cert_payload = recv_hs
457 .iter()
458 .find_map(|msg| match &msg.payload {
459 HandshakePayload::Certificate(certs) => Some(certs),
460 _ => None,
461 })
462 .ok_or_else(|| TlsTranscriptError::parse("missing Certificate"))?;
463
464 Ok(cert_payload
465 .iter()
466 .map(|cert| CertificateDer(cert.0.clone()))
467 .collect())
468}
469
470fn extract_server_key_exchange(
471 recv_hs: &[HandshakeMessagePayload],
472 certs: &[CertificateDer],
473) -> Result<(ServerEphemKey, ServerSignature), TlsTranscriptError> {
474 let ske = recv_hs
475 .iter()
476 .find_map(|msg| match &msg.payload {
477 HandshakePayload::ServerKeyExchange(ske) => Some(ske),
478 _ => None,
479 })
480 .ok_or_else(|| TlsTranscriptError::parse("missing ServerKeyExchange"))?;
481
482 let ecdhe = ske
483 .unwrap_given_kxa(&KeyExchangeAlgorithm::ECDHE)
484 .ok_or_else(|| TlsTranscriptError::parse("failed to parse ECDHE ServerKeyExchange"))?;
485
486 if ecdhe.params.curve_params.named_group != NamedGroup::secp256r1 {
487 return Err(TlsTranscriptError::parse(
488 "unsupported key exchange group (only secp256r1 is supported)",
489 ));
490 }
491
492 let key = ServerEphemKey {
493 typ: KeyType::SECP256R1,
494 key: ecdhe.params.public.0.clone(),
495 };
496
497 let alg = map_signature_scheme(ecdhe.dss.scheme, certs)?;
498 let sig = ServerSignature {
499 alg,
500 sig: ecdhe.dss.sig.0.clone(),
501 };
502
503 Ok((key, sig))
504}
505
506fn map_signature_scheme(
511 scheme: SignatureScheme,
512 certs: &[CertificateDer],
513) -> Result<SignatureAlgorithm, TlsTranscriptError> {
514 match scheme {
515 SignatureScheme::RSA_PKCS1_SHA256 => Ok(SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA256),
516 SignatureScheme::RSA_PKCS1_SHA384 => Ok(SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA384),
517 SignatureScheme::RSA_PKCS1_SHA512 => Ok(SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA512),
518 SignatureScheme::RSA_PSS_SHA256 => {
519 Ok(SignatureAlgorithm::RSA_PSS_2048_8192_SHA256_LEGACY_KEY)
520 }
521 SignatureScheme::RSA_PSS_SHA384 => {
522 Ok(SignatureAlgorithm::RSA_PSS_2048_8192_SHA384_LEGACY_KEY)
523 }
524 SignatureScheme::RSA_PSS_SHA512 => {
525 Ok(SignatureAlgorithm::RSA_PSS_2048_8192_SHA512_LEGACY_KEY)
526 }
527 SignatureScheme::ED25519 => Ok(SignatureAlgorithm::ED25519),
528 SignatureScheme::ECDSA_NISTP256_SHA256 => {
531 let curve_oid = extract_ec_curve_oid(certs)?;
532 match curve_oid {
533 oid if oid == rfc5912::SECP_256_R_1 => {
534 Ok(SignatureAlgorithm::ECDSA_NISTP256_SHA256)
535 }
536 oid if oid == rfc5912::SECP_384_R_1 => {
537 Ok(SignatureAlgorithm::ECDSA_NISTP384_SHA256)
538 }
539 _ => Err(TlsTranscriptError::parse(format!(
540 "unsupported EC curve: {curve_oid}"
541 ))),
542 }
543 }
544 SignatureScheme::ECDSA_NISTP384_SHA384 => {
545 let curve_oid = extract_ec_curve_oid(certs)?;
546 match curve_oid {
547 oid if oid == rfc5912::SECP_256_R_1 => {
548 Ok(SignatureAlgorithm::ECDSA_NISTP256_SHA384)
549 }
550 oid if oid == rfc5912::SECP_384_R_1 => {
551 Ok(SignatureAlgorithm::ECDSA_NISTP384_SHA384)
552 }
553 _ => Err(TlsTranscriptError::parse(format!(
554 "unsupported EC curve: {curve_oid}"
555 ))),
556 }
557 }
558 _ => Err(TlsTranscriptError::parse(format!(
559 "unsupported signature scheme: {scheme:?}"
560 ))),
561 }
562}
563
564fn extract_ec_curve_oid(certs: &[CertificateDer]) -> Result<ObjectIdentifier, TlsTranscriptError> {
566 let ee_cert = certs
567 .first()
568 .ok_or_else(|| TlsTranscriptError::parse("missing end-entity certificate"))?;
569
570 let cert = pki_types::CertificateDer::from(ee_cert.0.as_slice());
571 let ee = webpki::EndEntityCert::try_from(&cert)
572 .map_err(|e| TlsTranscriptError::parse(format!("invalid end-entity certificate: {e}")))?;
573 let spki_der = ee.subject_public_key_info();
574 let spki = spki::SubjectPublicKeyInfoRef::from_der(spki_der.as_ref())
575 .map_err(|e| TlsTranscriptError::parse(format!("invalid SPKI: {e}")))?;
576 spki.algorithm
577 .parameters
578 .ok_or_else(|| TlsTranscriptError::parse("missing EC curve parameters in SPKI"))?
579 .decode_as::<ObjectIdentifier>()
580 .map_err(|e| TlsTranscriptError::parse(format!("failed to decode EC curve OID: {e}")))
581}
582
583fn validate_finished_record(value: &Record) -> Result<(), TlsTranscriptError> {
584 if !matches!(value.typ, ContentType::Handshake) {
585 return Err(TlsTranscriptError::validation(format!(
586 "first record expected to be a handshake finished message, but has type {:?}",
587 value.typ
588 )));
589 }
590 if value.seq != 0 {
591 return Err(TlsTranscriptError::validation(format!(
592 "first record should have sequence number 0, but has {}",
593 value.seq
594 )));
595 }
596
597 if let Some(payload) = &value.plaintext {
598 let mut reader = Reader::init(payload);
599 let payload = HandshakeMessagePayload::read_version(&mut reader, ProtocolVersion::TLSv1_2)
600 .ok_or_else(|| {
601 TlsTranscriptError::validation("expected finished record but record is malformed")
602 })?;
603
604 if !matches!(payload.payload, HandshakePayload::Finished(_)) {
605 return Err(TlsTranscriptError::validation("expected finished record"));
606 }
607 }
608
609 Ok(())
610}
611
612fn validate_seq(records: &[Record]) -> Result<(), TlsTranscriptError> {
613 for pair in records.windows(2) {
614 if pair[1].seq <= pair[0].seq {
615 return Err(TlsTranscriptError::validation(format!(
616 "records must have strictly increasing sequence numbers, but got {} after {}",
617 pair[1].seq, pair[0].seq,
618 )));
619 }
620 }
621 Ok(())
622}
623
624fn parse_records(
626 records: &[OpaqueMessage],
627 app_data: Option<&[u8]>,
628) -> Result<Vec<Record>, TlsTranscriptError> {
629 let mut parsed = Vec::new();
630
631 let ccs = records
633 .iter()
634 .position(|r| r.typ == TlsContentType::ChangeCipherSpec)
635 .ok_or_else(|| TlsTranscriptError::missing("ccs record is missing"))?;
636 let raw_finished = records
637 .get(ccs + 1)
638 .ok_or_else(|| TlsTranscriptError::parse("missing Finished record after CCS"))?;
639
640 let payload = &raw_finished.payload.0;
641 if payload.len() < NONCE_LEN + TAG_LEN {
642 return Err(TlsTranscriptError::parse("encrypted record too short"));
643 }
644
645 let typ = raw_finished.typ.into();
646 if !matches!(typ, ContentType::Handshake) {
647 return Err(TlsTranscriptError::validation(format!(
648 "expected content type handshake for finished record, but got {:?}",
649 typ
650 )));
651 }
652
653 let finished_record = Record {
654 seq: 0,
655 typ: ContentType::Handshake,
656 plaintext: None,
657 explicit_nonce: payload[..NONCE_LEN].to_vec(),
658 ciphertext: payload[NONCE_LEN..payload.len() - TAG_LEN].to_vec(),
659 tag: Some(payload[payload.len() - TAG_LEN..].to_vec()),
660 };
661 parsed.push(finished_record);
662
663 let mut consumed = 0;
665 for (seq, record) in (1u64..).zip(records.iter().skip(ccs + 2)) {
666 let mut rec = split_into_record(seq, &record.payload.0, record.typ)?;
667
668 if rec.typ == ContentType::ApplicationData
669 && let Some(app_data) = app_data
670 {
671 let cipher_len = rec.ciphertext.len();
672 if app_data[consumed..].len() >= cipher_len {
673 rec.plaintext = Some(app_data[consumed..consumed + cipher_len].to_vec());
674 consumed += cipher_len;
675 } else {
676 return Err(TlsTranscriptError::parse(
677 "insufficient plaintext application data",
678 ));
679 }
680 }
681 parsed.push(rec);
682 }
683
684 Ok(parsed)
685}
686
687#[cfg(test)]
688mod tests {
689 use super::*;
690 use tls_server_fixture::SERVER_CERT_DER;
691
692 const SENT: &[u8] = include_bytes!("../fixtures/tls_sent.bin");
698 const RECV: &[u8] = include_bytes!("../fixtures/tls_recv.bin");
699 const APP_SENT: &[u8] = include_bytes!("../fixtures/tls_app_sent.bin");
700 const APP_RECV: &[u8] = include_bytes!("../fixtures/tls_app_recv.bin");
701 const MSG_COUNT: usize = 4;
702
703 #[test]
704 fn test_parse_handshake() {
705 let transcript = TlsTranscript::builder()
706 .time(0)
707 .tls_sent(SENT)
708 .tls_recv(RECV)
709 .build()
710 .unwrap();
711
712 assert_eq!(transcript.version(), TlsVersion::V1_2);
713
714 let certs = transcript.server_cert_chain().expect("cert chain parsed");
716 assert_eq!(certs[0].0, SERVER_CERT_DER);
717
718 let alg = &transcript.server_signature().expect("signature parsed").alg;
720 assert!(
721 matches!(
722 alg,
723 SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA256
724 | SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA384
725 | SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA512
726 | SignatureAlgorithm::RSA_PSS_2048_8192_SHA256_LEGACY_KEY
727 | SignatureAlgorithm::RSA_PSS_2048_8192_SHA384_LEGACY_KEY
728 | SignatureAlgorithm::RSA_PSS_2048_8192_SHA512_LEGACY_KEY
729 ),
730 "expected RSA signature algorithm, got {:?}",
731 alg
732 );
733
734 let CertBinding::V1_2(binding) = transcript.certificate_binding();
736
737 assert_ne!(binding.client_random, [0u8; 32]);
738 assert_ne!(binding.server_random, [0u8; 32]);
739 assert_eq!(binding.server_ephemeral_key.typ, KeyType::SECP256R1);
740 assert_eq!(binding.server_ephemeral_key.key.len(), 65);
742 assert_eq!(binding.server_ephemeral_key.key[0], 0x04);
743 }
744
745 #[test]
746 fn test_parse_app_records() {
747 let sent_raw = parse_raw_records(SENT).unwrap();
748 let recv_raw = parse_raw_records(RECV).unwrap();
749 let sent_records = parse_records(&sent_raw, Some(APP_SENT)).unwrap();
750 let recv_records = parse_records(&recv_raw, Some(APP_RECV)).unwrap();
751
752 assert_eq!(sent_records.len(), MSG_COUNT + 1);
754 for (i, record) in sent_records.iter().enumerate() {
755 let expected_seq = (i) as u64;
756 if i == 0 {
757 assert_eq!(record.typ, ContentType::Handshake);
758 } else {
759 assert_eq!(record.typ, ContentType::ApplicationData);
760 }
761 assert_eq!(record.seq, expected_seq);
762 assert_eq!(record.explicit_nonce.len(), 8);
763 assert!(!record.ciphertext.is_empty());
764 assert_eq!(record.tag.as_ref().unwrap().len(), 16);
765 }
766
767 assert_eq!(recv_records.len(), MSG_COUNT + 1);
769 for (i, record) in recv_records.iter().enumerate() {
770 let expected_seq = (i) as u64;
771 assert_eq!(record.seq, expected_seq);
772 if i == 0 {
773 assert_eq!(record.typ, ContentType::Handshake);
774 } else {
775 assert_eq!(record.typ, ContentType::ApplicationData);
776 }
777 assert_eq!(record.explicit_nonce.len(), 8);
778 assert!(!record.ciphertext.is_empty());
779 assert_eq!(record.tag.as_ref().unwrap().len(), 16);
780 }
781 }
782
783 #[test]
784 fn test_parse_into_transcript() {
785 let transcript = TlsTranscript::builder()
786 .time(0)
787 .tls_sent(SENT)
788 .tls_recv(RECV)
789 .app_sent(APP_SENT)
790 .app_recv(APP_RECV)
791 .build()
792 .unwrap();
793
794 assert_eq!(transcript.version(), TlsVersion::V1_2);
795 assert!(transcript.server_cert_chain().is_some());
796 assert!(transcript.server_signature().is_some());
797
798 assert!(!transcript.client_finished().ciphertext.is_empty());
800 assert!(!transcript.server_finished().ciphertext.is_empty());
801
802 assert_eq!(transcript.sent().len(), MSG_COUNT + 1);
804 assert_eq!(transcript.recv().len(), MSG_COUNT + 1);
805 assert_eq!(transcript.sent()[0].seq, 0);
806 assert_eq!(transcript.recv()[0].seq, 0);
807 }
808}