Skip to content

net/srtp: Encrypted Key Transport (RFC 8870) - #447

Merged
haaspors merged 2 commits into
masterfrom
srtp-ekt
Aug 3, 2026
Merged

net/srtp: Encrypted Key Transport (RFC 8870)#447
haaspors merged 2 commits into
masterfrom
srtp-ekt

Conversation

@haaspors

@haaspors haaspors commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Implements RFC 8870 Encrypted Key Transport, closing #442. Two commits: the reusable AES Key Wrap primitive, then EKT layered on it.

AES Key Wrap (RFC 3394 + RFC 5649)

r_cipher_aes_key_wrap / _unwrap (whole-64-bit-block form) and _wrap_pad / _unwrap_pad (arbitrary length, incl. the single-block case), built on the exposed AES-ECB block primitive. Both carry the 64-bit integrity check and fail closed on a wrong key or tampered ciphertext. Tested against the RFC 3394 §4.1 and RFC 5649 §6 vectors plus round-trip and failure cases. A reusable primitive independent of SRTP.

EKT (RFC 8870)

Carries a sender's SRTP master key in-band so a peer (typically a conference focus) can learn and rotate it without separate signalling. Configuring an EKTKey (r_srtp_add_ekt_key — AES Key Wrap cipher + key, SPI, and the SRTP suite + master salt the transported keys use) turns EKT framing on for the context.

  • Protect: an EKT field trails the SRTP/SRTCP auth tag — a Full field (AES-KW(mklen‖key‖SSRC‖ROC) ‖ SPI ‖ Epoch ‖ Length ‖ 0x02) in a startup burst, periodically thereafter (r_srtp_set_ekt_full_interval) so a mid-stream joiner can key itself, and a Short field (0x00) otherwise. It is outside the SRTP-authenticated portion and carries its own AES-KW integrity check.
  • Unprotect: the field is stripped from a zero-copy view before SRTP decoding; a Full field is ingested first — the SPI selects the EKTKey, the wrapped key is unwrapped, the inner SSRC is checked against the packet, and the recovered key + salt + ROC are installed for that SSRC so the same packet decrypts with it.
  • Anti-replay: epoch is tracked per (SPI, SSRC), outside the stream so it survives a key reinstall; a Full field whose epoch was already seen — a burst repeat or a replay, including one that alternates SPIs — is not re-installed (RFC 8870 §4.3.2).
  • Rekey: r_srtp_update_crypto_context_for_ssrc rolls a sender's own key in place, keeping the packet index continuous; EKT bumps the epoch and re-announces. The application decides when to switch encrypting to the new key.

Testing

New tests cover the AES-KW RFC vectors + failures, and EKT round-trip (RTP + SRTCP), Full/Short burst, periodic resend, sender rekey, and a multi-SPI replay-downgrade regression — proven load-bearing by neutering the anti-replay and epoch-bump checks. Validated on the linux and ASan tiers (both warning-free); full suite 2345 passed / 0 failed. Reviewed with two rounds of adversarial audit (memory-safety, crypto/protocol, docs), including a fix hardening the epoch anti-replay to per-(SPI, SSRC).

🤖 Generated with Claude Code

haaspors and others added 2 commits August 3, 2026 16:35
Add deterministic key wrapping under an AES key-encryption key, built on the
exposed AES-ECB block primitive: r_cipher_aes_key_wrap / _unwrap for the
RFC 3394 (NIST SP 800-38F) whole-64-bit-block form, and the
r_cipher_aes_key_wrap_pad / _unwrap_pad RFC 5649 padded form for arbitrary
lengths, including the single-block case. Both carry the 64-bit integrity
check, so unwrap verifies as it decrypts and fails closed on a wrong key or
tampered ciphertext.

Needed as the EKT cipher for Encrypted Key Transport (RFC 8870); a reusable
primitive on its own. Tested against the RFC 3394 and RFC 5649 vectors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Carry a sender's SRTP master key in-band so a peer (typically a conference
focus) can learn and rotate it without separate signalling. Configuring an
EKTKey (r_srtp_add_ekt_key: an AES Key Wrap cipher + key, an SPI, and the SRTP
suite and master salt the transported keys use) turns EKT framing on for the
context.

On protect, an EKT field is appended after the SRTP/SRTCP auth tag: a Full
field (AES-KW of SRTPMasterKeyLength || key || SSRC || ROC, then SPI || Epoch
|| Length || 0x02) in a short startup burst, periodically thereafter
(r_srtp_set_ekt_full_interval) so a mid-stream joiner can key itself, and a
Short field (0x00) otherwise. The field is outside the SRTP-authenticated
portion and carries its own AES-KW integrity check.

On unprotect, the field is stripped from a zero-copy view before SRTP
decoding; a Full field is ingested first -- its SPI selects the EKTKey, the
wrapped key is unwrapped, the inner SSRC is matched against the packet, and
the recovered key + salt + ROC are installed as the crypto context for that
SSRC, so the same packet then decrypts with it. Epoch state is tracked per
(SPI, SSRC), outside the stream so it survives a key reinstall, so a Full
field whose epoch was already seen -- a burst repeat or a replay, including
one that alternates SPIs -- is not re-installed (RFC 8870 4.3.2).

r_srtp_update_crypto_context_for_ssrc rolls a sender's own key in place,
keeping the packet index continuous; EKT then bumps the epoch and re-announces
in a fresh burst. The application decides when to switch encrypting to the new
key.

Closes #442

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@haaspors
haaspors merged commit 3205f14 into master Aug 3, 2026
18 checks passed
@haaspors
haaspors deleted the srtp-ekt branch August 3, 2026 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant