← Week 2: Custom Binary Protocols

Day 13: Message Authentication

Phase 3 · Jul 13, 2026

← Week 2: Custom Binary Protocols

Agenda (2–3 hours)

  • Read (45 min): RFC 2104 (HMAC); NIST SP 800-38D §2 (GCM — AEAD); TLS record layer for comparison (from Phase 1 secure-comms course)
  • Study (45 min): Why is MAC-then-Encrypt vulnerable (Padding Oracle)? Why is Encrypt-then-MAC (TLS 1.3) or AEAD the correct pattern?
  • Practice (45 min): Add HMAC-SHA256 authentication to the KV protocol from Day 9; test detection of tampered messages
  • Challenge (30 min): Design a replay-attack prevention scheme using a sequence number + nonce + sliding window
← Week 2: Custom Binary Protocols

Why Authenticate Binary Protocols?

Without authentication on a binary protocol:

  • A man-in-the-middle can modify commands (change GET to SET, modify values)
  • A replay attacker can resend captured valid requests
  • Bit-flip errors silently corrupt data

Authentication options:

  1. HMAC: keyed MAC over the message; detects tampering, not replay
  2. AEAD (e.g., AES-256-GCM, ChaCha20-Poly1305): encryption + authentication together
  3. TLS: use TLS/mTLS at the transport layer (covers everything, at a cost of ~1ms handshake)
← Week 2: Custom Binary Protocols

HMAC-SHA256 Authentication

use hmac::{Hmac, Mac};
use sha2::Sha256;

type HmacSha256 = Hmac<Sha256>;

fn sign_message(key: &[u8], header: &[u8], body: &[u8]) -> [u8; 32] {
    let mut mac = HmacSha256::new_from_slice(key).unwrap();
    mac.update(header);
    mac.update(body);
    mac.finalize().into_bytes().into()
}

fn verify_message(key: &[u8], header: &[u8], body: &[u8], tag: &[u8]) -> bool {
    let mut mac = HmacSha256::new_from_slice(key).unwrap();
    mac.update(header);
    mac.update(body);
    mac.verify_slice(tag).is_ok() // constant-time comparison
}
← Week 2: Custom Binary Protocols

Replay Attack Prevention

Without a nonce/sequence number, a valid signed message can be captured and replayed:

Header extension:
  sequence_number: u64   (monotonically increasing per connection)
  nonce: [u8; 12]        (random per message, included in HMAC input)

Sequence number: server keeps expected next sequence; reject anything <= last seen.

Sliding window (handles reordering):

  • Maintain a bitmask of received sequence numbers in window [last - W, last]
  • Reject any message with seq ≤ (last - W) or already seen within window
← Week 2: Custom Binary Protocols

AEAD vs HMAC + Encrypt

Scheme Confidentiality Integrity Auth Notes
HMAC only No Yes Yes Use when traffic is logged/observable
AEAD (GCM) Yes Yes Yes Standard for confidential channel
Encrypt-then-HMAC Yes Yes Yes Manual combination; subtle to implement
TLS/mTLS Yes Yes Yes Transport-level; handles key exchange too

For a binary protocol over an internal network, AEAD or TLS is the right choice. HMAC-only is appropriate only when you want visible-but-authenticated traffic.

← Week 2: Custom Binary Protocols

Key Takeaways

  • HMAC provides integrity + authentication; use with sequence numbers to prevent replay
  • AEAD (AES-GCM, ChaCha20-Poly1305) adds confidentiality with a single primitive
  • Always use constant-time comparison (verify_slice) to prevent timing attacks
  • For production: TLS/mTLS at the transport layer is simpler and more auditable than rolling your own

Tomorrow: Challenge — multiplexed binary protocol client/server.