Secure Frame (SFrame): Lightweight Authenticated Encryption for Real-Time Media
Extracted elements (29)
SFrame can be applied per frame (higher efficiency, lower overhead) or per packet (simpler integration where depacketization inspects media content). Per-frame encryption requires reassembly of ciphertext fragments across multiple transport packets and a generic RTP packetization scheme, while per-packet adds roughly 22 bytes per packet.
SFrame intentionally decouples media encryption from key management (Section 3, Goal 2). Key provisioning, rotation schedules, and KID assignment strategies are left entirely to the application, allowing SFrame to be used with sender-key schemes, MLS, or any future E2E key management system without changes to the SFrame specification.
SFrame was designed because SRTP 'double encryption' (RFC 8723) has poor efficiency, high complexity, and entanglement with RTP that makes it unworkable in realistic SFU scenarios. SFrame is a transport-agnostic E2EE framing that can operate over SRTP, QUIC, or any other HBH-encrypted transport.
During a key rotation triggered by a participant joining, the SFU may request a video key frame while the sender is still using the old E2EE key. The new receiver discards the frame, then re-requests a key frame; if the sender does not generate a key frame under the new key promptly, video startup is delayed. Audio streams are unaffected because each frame is independently decodable.
In simulcast, each of the N encoded representations of the same input image is independently encrypted by the SFrame layer and assigned a unique CTR value. SFUs can forward any simulcast layer without needing to decrypt or re-encrypt the payload.
When an SFU reuses RTP streams (SSRCs or MIDs) to carry media from different participants, receivers authenticate the true sender from the SFrame KID value rather than from the transport-layer stream identifier, preventing the SFU from causing receivers to misattribute media.
When SFrame is applied per frame, partial decoding is not possible: the AEAD authentication tag spans the entire frame, so no media data can be released to the decoder until the entire frame has arrived and been successfully decrypted.
A given base_key MUST NOT be used for encryption by multiple senders; such reuse would cause multiple encrypted frames to be generated with the same (key, nonce) pair. Implementations MUST mark each base_key as usable for encryption or decryption only, never both.
A receiver MUST NOT use SFrame-authenticated metadata for any purpose other than preparing an SFrame ciphertext for decryption until after the SFrame decrypt function has successfully authenticated it.
Applications MUST ensure that each (base_key, KID, CTR) combination is used for at most one SFrame encryption operation, ensuring the AEAD (key, nonce) pairs are never reused. The typical approach is a per-sender KID with a monotonically incrementing CTR.
Applications MUST ensure that each (KID, CTR) combination is used for exactly one SFrame encryption operation. This prevents nonce reuse, which would harm the security properties of many AEAD algorithms.
Every client SHOULD change their keys when new clients join or leave the call in order to provide forward secrecy and post-compromise security.
For Scalable Video Coding (SVC), the sender MUST encapsulate each spatial or temporal layer in a different SFrame ciphertext so that the SFU can selectively drop layers without breaking decryptability.
If a ciphertext fails to decrypt because no key is available for the KID in the SFrame header, the client MAY buffer the ciphertext and retry once a key with that KID is received.
If a ciphertext fails to decrypt for any reason other than a missing key for the KID, the client MUST discard the ciphertext. Invalid ciphertexts SHOULD be discarded indistinguishably from valid ones (i.e., in constant time) to prevent timing side-channels.
In the MLS-based key scheme, receivers MUST remove an old epoch when a new epoch with the same low-order E bits is introduced, preventing unbounded key accumulation during epoch rollover.
Senders MUST reject requests to encrypt multiple times with the same key and nonce, since several AEAD algorithms fail catastrophically under nonce reuse (per RFC 5116 Section 5.1.1).
Five cipher suites are defined, identified by a 2-byte identifier in the SFrame Cipher Suites IANA registry: AES_128_CTR_HMAC_SHA256_80 (0x0001), AES_128_CTR_HMAC_SHA256_64 (0x0002), AES_128_CTR_HMAC_SHA256_32 (0x0003), AES_128_GCM_SHA256_128 (0x0004), and AES_256_GCM_SHA512_128 (0x0005). Each specifies a hash function (Nh), key size (Nk), nonce size (Nn), and tag size (Nt).
For MLS-based sessions, the KID encodes three fields: a context value in the high (64-S-E) bits, the sender's MLS member index in S bits, and the low E bits of the epoch. The base_key per epoch is: MLS-Exporter('SFrame 1.0 Base Key', '', AEAD.Nk).
Key derivation uses HKDF (RFC 5869): sframe_secret = HKDF-Extract('', base_key); sframe_key = HKDF-Expand(sframe_secret, 'SFrame 1.0 Secret key ' + KID_8bytes + cipher_suite_2bytes, AEAD.Nk); sframe_salt = HKDF-Expand(sframe_secret, 'SFrame 1.0 Secret salt ' + KID_8bytes + cipher_suite_2bytes, AEAD.Nn). The hash function is determined by the cipher suite.
The AEAD nonce is constructed by XORing the sframe_salt with the CTR value (encoded as a big-endian integer of length AEAD.Nn). The SFrame header is concatenated with application-supplied metadata and used as AAD for the AEAD operation.
The AES-CTR with SHA2 compound AEAD (Section 4.5.1) uses an encrypt-then-MAC approach: AES-CTR encryption with a subkey (first Nka bytes of sframe_key) followed by HMAC over aad_len || ct_len || tag_len || nonce || aad || ct, truncated to Nt bytes. This allows very short tag sizes (e.g., 32-bit) not supported by native AEADs.
The Sender Keys scheme encodes the KID as two concatenated unsigned integers: a key generation in the high (64-R) bits and a ratchet step modulo 2^R in the low R bits. The ratchet key update is: base_key[i+1] = HKDF-Expand(HKDF-Extract('', base_key[i]), 'SFrame 1.0 Ratchet', CipherSuite.Nh).
IANA created the 'SFrame Cipher Suites' registry under the 'SFrame' group. The cipher suite field is 2 bytes (0x0000-0xFFFF); 0x0000 is reserved, 0x0001-0x0005 are initial recommended entries, 0xF000-0xFFFF are reserved for private use. New assignments require Specification Required; setting Recommended to 'Y' or 'D' requires Standards Action.
SFrame does not provide per-sender authentication of media data because it uses symmetric keys. Any receiver who holds the decryption key also holds sufficient material to forge encrypted packets attributed to the sender; preventing impersonation requires a digital-signature layer not defined by SFrame.
SFrame provides integrity protection for the SFrame header (KID and CTR) but no confidentiality. Any observer of SFrame ciphertexts can learn which parties are sending (from KID) and at what rate (from CTR). HBH security (e.g., SRTP) is required to protect header confidentiality between hops.
Short authentication tags (as low as 32 bits in AES_128_CTR_HMAC_SHA256_32) enable brute-force forgery attacks; a gigabit link saturating a 32-bit tag succeeds roughly once per hour. Mitigations include HBH-secure channels, rate monitoring, no decryption-failure feedback, and anti-replay. Full-size tags are recommended when feasible.
An SFrame ciphertext is an SFrame header followed by the output of an AEAD encryption (per RFC 5116), with the header provided as additional authenticated data (AAD). The authentication tag is considered part of the ciphertext, not a separate field.
The SFrame header begins with a one-byte config byte containing: X flag (1 bit, extended KID), K field (3 bits, KID value or KID length minus one), Y flag (1 bit, extended CTR), and C field (3 bits, CTR value or CTR length minus one). When X or Y is set, the KID or CTR follows as a big-endian integer of 1-8 bytes appended after the config byte, KID first then CTR.