AirPlay.V2.Crypto (AirPlay v0.1.0)

Copy Markdown View Source

Crypto primitives for AirPlay 2 pairing + the encrypted channel, mapping the AirPlay 2 scheme onto Erlang :crypto:

  • HKDF-SHA512 (RFC 5869) — key derivation for pair-setup/verify + the session.
  • ChaCha20-Poly1305 AEAD — the encrypted control channel + audio packets.
  • X25519 ECDH + Ed25519 — used by legacy/HomeKit pair-verify.

See the protocol notes for the salts/infos and where each is used.

Summary

Functions

Inverse of audio_encrypt/3 (for tests): payload is ct<>tag16<>nonce8.

Encrypt one AP2 audio packet payload (ChaChaPoly), porting AirPlayClientV2. Encrypt: a per-packet 12-byte nonce <<0::32, rand64::64>>, AEAD over pcm with aad (the RTP header), and the random 8 nonce bytes appended as a trailer so the receiver can reconstruct the nonce. Returns ciphertext <> tag16 <> nonce8.

ChaCha20-Poly1305 decrypt → plaintext or :error.

ChaCha20-Poly1305 encrypt → {ciphertext, tag16}.

Generate an Ed25519 keypair {public, private}.

Ed25519 signature over message with private key.

Verify an Ed25519 signature over message with public key.

HKDF-SHA512: extract+expand ikm to len bytes with salt/info.

Generate an X25519 keypair {public, private} (32 bytes each).

X25519 ECDH shared secret from our private + peer public key.

Functions

audio_decrypt(key, payload, aad \\ <<>>)

@spec audio_decrypt(binary(), binary(), binary()) :: binary() | :error

Inverse of audio_encrypt/3 (for tests): payload is ct<>tag16<>nonce8.

audio_encrypt(key, pcm, aad \\ <<>>)

@spec audio_encrypt(binary(), binary(), binary()) :: binary()

Encrypt one AP2 audio packet payload (ChaChaPoly), porting AirPlayClientV2. Encrypt: a per-packet 12-byte nonce <<0::32, rand64::64>>, AEAD over pcm with aad (the RTP header), and the random 8 nonce bytes appended as a trailer so the receiver can reconstruct the nonce. Returns ciphertext <> tag16 <> nonce8.

chacha_decrypt(key, nonce, ciphertext, tag, aad \\ <<>>)

@spec chacha_decrypt(binary(), binary(), binary(), binary(), binary()) ::
  binary() | :error

ChaCha20-Poly1305 decrypt → plaintext or :error.

chacha_encrypt(key, nonce, plaintext, aad \\ <<>>)

@spec chacha_encrypt(binary(), binary(), binary(), binary()) :: {binary(), binary()}

ChaCha20-Poly1305 encrypt → {ciphertext, tag16}.

ed25519_keypair()

@spec ed25519_keypair() :: {binary(), binary()}

Generate an Ed25519 keypair {public, private}.

ed25519_sign(message, private)

@spec ed25519_sign(binary(), binary()) :: binary()

Ed25519 signature over message with private key.

ed25519_verify(message, signature, public)

@spec ed25519_verify(binary(), binary(), binary()) :: boolean()

Verify an Ed25519 signature over message with public key.

hkdf(ikm, salt, info, len)

@spec hkdf(binary(), binary(), binary(), pos_integer()) :: binary()

HKDF-SHA512: extract+expand ikm to len bytes with salt/info.

x25519_keypair()

@spec x25519_keypair() :: {binary(), binary()}

Generate an X25519 keypair {public, private} (32 bytes each).

x25519_shared(private, peer_public)

@spec x25519_shared(binary(), binary()) :: binary()

X25519 ECDH shared secret from our private + peer public key.