AirPlay.V2.Crypto (AirPlay v0.3.2)

Copy Markdown View Source

AirPlay 2 cryptographic helpers.

This module intentionally stays small: HKDF-SHA512, ChaCha20-Poly1305 and AP2 audio packet encryption. Pairing and RTSP framing build on these primitives.

Summary

Functions

Decrypt an AP2 RTP audio payload encrypted by audio_encrypt/3.

Encrypt an AP2 RTP audio payload using fields parsed from a 12-byte RTP header.

Decrypt with ChaCha20-Poly1305. Returns {:ok, plaintext} or :error.

Encrypt with ChaCha20-Poly1305. Returns {ciphertext, tag}.

Compatibility wrapper for ChaCha20-Poly1305 decryption.

Compatibility wrapper for ChaCha20-Poly1305 encryption.

Generate an Ed25519 keypair as {public_key, private_key}.

Sign a message with Ed25519.

Verify an Ed25519 signature.

Compatibility wrapper for HKDF-SHA512 using (ikm, salt, info, len) argument order.

HKDF-SHA512 derive len bytes from input keying material.

Generate an X25519 keypair as {public_key, private_key}.

Compute an X25519 shared secret.

Functions

audio_decrypt(session_key, packet, rtp_header)

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

Decrypt an AP2 RTP audio payload encrypted by audio_encrypt/3.

audio_encrypt(session_key, rtp_header, payload)

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

Encrypt an AP2 RTP audio payload using fields parsed from a 12-byte RTP header.

audio_encrypt(session_key, rtp_timestamp, ssrc, sequence, payload)

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

Encrypt an AP2 RTP audio payload.

The wire format matches owntone and airplay2-rs:

  • nonce = <<0::32, seq::little-16, 0::48>>
  • transmitted nonce suffix = <<seq::little-16, 0::48>>
  • AAD = <<rtp_timestamp::32, ssrc::32>>
  • packet trailer = tag16 <> nonce8

The receiver reconstructs the 12-byte nonce by prefixing four zero bytes to the transmitted eight-byte suffix.

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

@spec chacha20_poly1305_decrypt(binary(), binary(), binary(), binary(), binary()) ::
  {:ok, binary()} | :error

Decrypt with ChaCha20-Poly1305. Returns {:ok, plaintext} or :error.

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

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

Encrypt with ChaCha20-Poly1305. Returns {ciphertext, tag}.

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

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

Compatibility wrapper for ChaCha20-Poly1305 decryption.

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

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

Compatibility wrapper for ChaCha20-Poly1305 encryption.

ed25519_keypair()

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

Generate an Ed25519 keypair as {public_key, private_key}.

ed25519_sign(message, private_key)

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

Sign a message with Ed25519.

ed25519_verify(message, signature, public_key)

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

Verify an Ed25519 signature.

hkdf(ikm, salt, info, len)

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

Compatibility wrapper for HKDF-SHA512 using (ikm, salt, info, len) argument order.

hkdf_sha512(ikm, len, salt, info)

@spec hkdf_sha512(binary(), non_neg_integer(), binary(), binary()) :: binary()

HKDF-SHA512 derive len bytes from input keying material.

x25519_keypair()

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

Generate an X25519 keypair as {public_key, private_key}.

x25519_shared(private_key, public_key)

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

Compute an X25519 shared secret.