writeonce/runtime/test/gen_tls_record.py
shoney.arickathil 7e71c1a171 feat(tls): TLS 1.3 record layer (rv2 9 phase F1)
- new tls.c/tls.h on the crypto ladder: wo_tls_record_seal/open
  (RFC 8446 §5.2) — TLSInnerPlaintext (content||type, no padding),
  5-byte header as AEAD additional-data, per-record nonce = iv XOR
  seq big-endian (§5.3)
- suite dispatch: TLS_AES_128_GCM_SHA256 (mandatory) +
  TLS_CHACHA20_POLY1305_SHA256 (AES-NI-less fallback), over phase-A AEAD
- open() strips trailing zero padding to recover the inner content type;
  rejects a length-field lie before the AEAD, and auth failure after
- KAT vs python AEAD oracle (test/gen_tls_record.py): sealed record
  byte-for-byte both suites, open() recovers it, 5-seq round-trip,
  tamper + wrong-seq + bad-suite rejected. test_tls 51 pass, ASan/UBSan

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 5021a99f8359f78a642bb0cc2b28ab66f6b624e2)
2026-09-15 01:15:31 +02:00

45 lines
1.8 KiB
Python

#!/usr/bin/env python3
"""TLS 1.3 record-layer KAT vectors (RFC 8446 §5.2). For a fixed key/iv/seq/
plaintext/content-type, compute the full wire record with python's AEAD as the
oracle, for both AES-128-GCM and ChaCha20-Poly1305. Emits C literals."""
from cryptography.hazmat.primitives.ciphers.aead import AESGCM, ChaCha20Poly1305
def nonce(iv, seq):
n = bytearray(iv)
for i in range(8):
n[4 + i] ^= (seq >> (8 * (7 - i))) & 0xff
return bytes(n)
def record(aead, key, iv, seq, ct, pt):
inner = pt + bytes([ct]) # no padding
payload_len = len(inner) + 16
hdr = bytes([23, 3, 3, payload_len >> 8, payload_len & 0xff])
enc = aead(key).encrypt(nonce(iv, seq), inner, hdr)
return hdr + enc
def hexlit(b):
return '"' + "".join("\\x%02x" % x for x in b) + '"'
# AES-128-GCM: 16-byte key, ChaCha: 32-byte key. Shared iv/seq/pt/type.
aes_key = bytes.fromhex("000102030405060708090a0b0c0d0e0f")
cha_key = bytes.fromhex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
iv = bytes.fromhex("cafebabecafebabecafebabe")
seq = 0x0102030405060708
pt = b"hello writeonce tls"
ct = 22 # handshake
r_aes = record(AESGCM, aes_key, iv, seq, ct, pt)
r_cha = record(ChaCha20Poly1305, cha_key, iv, seq, ct, pt)
print("/* Generated by scratchpad/gen_tls_record.py (python cryptography). */")
print("#define TLSREC_IV %s" % hexlit(iv))
print("#define TLSREC_AESKEY %s" % hexlit(aes_key))
print("#define TLSREC_CHAKEY %s" % hexlit(cha_key))
print("#define TLSREC_SEQ 0x%016xULL" % seq)
print("#define TLSREC_CT %d" % ct)
print("#define TLSREC_PT %s" % hexlit(pt))
print("#define TLSREC_PTLEN %d" % len(pt))
print("#define TLSREC_AES %s" % hexlit(r_aes))
print("#define TLSREC_AESLEN %d" % len(r_aes))
print("#define TLSREC_CHA %s" % hexlit(r_cha))
print("#define TLSREC_CHALEN %d" % len(r_cha))