///|
/// The TLS 1.3 key schedule, one step at a time (RFC 8446 §7.1).
///
/// moon run src/examples/tour
#coverage.skip
fn main {
// No PSK, so the Early Secret is the extract of two all-zero inputs — the
// value RFC 8448 publishes.
let early = @keys.early()
println("early " + @base16.encode(early))
// A fixed ECDHE secret stands in for the one X25519 would produce.
let ecdhe = Bytes::make(32, b'\x2a')
let handshake = @keys.handshake(early[:], ecdhe[:])
println("handshake " + @base16.encode(handshake))
let master = @keys.master(handshake[:])
println("master " + @base16.encode(master))
// The transcript is the running hash of the handshake messages.
let transcript = @keys.Transcript::new()
transcript.add(b"ClientHello"[:])
transcript.add(b"ServerHello"[:])
let through_hello = transcript.hash()
println("transcript " + @base16.encode(through_hello))
let client = @keys.client_handshake(handshake[:], through_hello[:])
let server = @keys.server_handshake(handshake[:], through_hello[:])
println("c hs traffic " + @base16.encode(client))
println("s hs traffic " + @base16.encode(server))
// A Finished MACs the transcript under a key derived from that secret.
let verify = @keys.finished(client[:], through_hello[:])
println("verify_data " + @base16.encode(verify))
println(
"accepted " +
@keys.finished_ok(client[:], through_hello[:], verify[:]).to_string(),
)
let tampered = verify.to_array()
tampered[0] = (tampered[0].to_int() ^ 1).to_byte()
println(
"one bit flipped " +
@keys.finished_ok(
client[:],
through_hello[:],
Bytes::from_array(tampered)[:],
).to_string(),
)
// A SHA-384 suite runs the same ladder 48 octets wide.
println("early (SHA-384) " + @base16.encode(@keys.early(digest=Sha384)))
// The record layer keys itself from a traffic secret and seals under it.
let t = @record.traffic(client[:])
println("record key " + @base16.encode(t.key))
println("record iv " + @base16.encode(t.iv))
let write = @record.Cipher::new(client[:])
let read = @record.Cipher::new(client[:])
try {
let wrapped = write.seal(Handshake, b"hello"[:])
println("sealed " + @base16.encode(wrapped))
let (kind, content) = read.open(wrapped[:])
println(
"opened type " +
kind.code().to_string() +
", content " +
@base16.encode(content),
)
} catch {
Alert(kind~, ..) => println("record refused " + kind.code().to_string())
}
// An alert says why an endpoint gave up, in two octets.
let refusal = @alert.fatal(@alert.Kind::DecodeError)
println("alert " + @base16.encode(@alert.encode(refusal)))
}