AirPlay.V2.SecureChannel (AirPlay v0.2.0)

Copy Markdown View Source

The encrypted RTSP control channel used after AirPlay 2 pairing — a port of EncryptionContext. Once paired, every RTSP message is wrapped: split into ≤1024-byte blocks, each emitted as [len:u16-le][ciphertext][tag:16], ChaCha20-Poly1305 with a 12-byte nonce <<0::32, counter::little-64>> (separate write/read counters) and AAD = the 2-byte length prefix.

Write/read keys are HKDF-SHA512(session_key) with the HomeKit control salts: write = info "Control-Write-Encryption-Key", read = "Control-Read-Encryption-Key", both salt "Control-Salt". (From the controller side: we write with the Write key and read the device's replies with the Read key.)

Summary

Functions

Decrypt as many complete blocks as are buffered (incl. carried inbuf). Returns {plaintext, channel}plaintext may be empty if no full block yet; leftover partial bytes stay in channel.inbuf.

Encrypt plaintext into the block-framed wire form; returns {bytes, channel}.

Derive a control channel from the pairing session_key.

Types

t()

@type t() :: %AirPlay.V2.SecureChannel{
  inbuf: term(),
  read_ctr: term(),
  read_key: term(),
  write_ctr: term(),
  write_key: term()
}

Functions

decrypt(ch, data)

@spec decrypt(t(), binary()) :: {binary(), t()}

Decrypt as many complete blocks as are buffered (incl. carried inbuf). Returns {plaintext, channel}plaintext may be empty if no full block yet; leftover partial bytes stay in channel.inbuf.

encrypt(ch, plaintext)

@spec encrypt(t(), binary()) :: {binary(), t()}

Encrypt plaintext into the block-framed wire form; returns {bytes, channel}.

new(session_key)

@spec new(binary()) :: t()

Derive a control channel from the pairing session_key.