Amarula.Protocol.Crypto.NoiseHandler (amarula v0.1.0)

View Source

Noise protocol handler for WhatsApp WebSocket communication.

This module implements the Noise_XX_25519_AESGCM_SHA256 protocol used by WhatsApp for secure WebSocket communication. It provides stateless functions that operate on noise state, which is stored in the Connection's GenServer state.

The noise state is recreated on every new WebSocket connection with fresh ephemeral keys.

Summary

Functions

Update running hash with data for authentication.

Decode incoming frames and extract messages.

Decrypt ciphertext using current decryption key and counter.

Decrypt with explicit AAD (used by tests to ensure exact handshake semantics).

Encode data into protocol frame format.

Encrypt plaintext using current encryption key and counter.

Complete handshake initialization by splitting keys.

Mix data into key using HKDF.

Create initial noise state with ephemeral key pair and configuration.

Process server hello message during handshake.

Types

decrypt_result()

@type decrypt_result() :: {:ok, binary(), noise_state()} | {:error, term()}

encrypt_result()

@type encrypt_result() :: {binary(), noise_state()}

frame_result()

@type frame_result() :: {:ok, [binary()], noise_state()}

handshake_result()

@type handshake_result() :: {:ok, binary(), noise_state()} | {:error, term()}

noise_state()

@type noise_state() :: %{
  ephemeral_key_pair: Amarula.Protocol.Crypto.Crypto.key_pair(),
  hash: binary(),
  salt: binary(),
  enc_key: binary(),
  dec_key: binary(),
  read_counter: non_neg_integer(),
  write_counter: non_neg_integer(),
  handshake_state:
    :init | :awaiting_server_hello | :handshake_complete | :transport,
  sent_intro: boolean(),
  in_bytes: binary(),
  routing_info: binary() | nil,
  noise_header: binary()
}

Functions

authenticate(state, data)

@spec authenticate(noise_state(), binary()) :: noise_state()

Update running hash with data for authentication.

Returns updated noise state.

decode_frame(state, new_data)

@spec decode_frame(noise_state(), binary()) :: {:ok, [binary()], noise_state()}

Decode incoming frames and extract messages.

Returns {:ok, frames, updated_state} where frames is a list of decoded messages.

decrypt(state, ciphertext)

@spec decrypt(noise_state(), binary()) :: decrypt_result()

Decrypt ciphertext using current decryption key and counter.

Returns {:ok, decrypted_data, updated_state} or {:error, reason}.

decrypt(state, ciphertext, aad)

@spec decrypt(noise_state(), binary(), binary()) :: decrypt_result()

Decrypt with explicit AAD (used by tests to ensure exact handshake semantics).

Returns {:ok, decrypted_data, updated_state} or {:error, reason}.

encode_frame(state, data)

@spec encode_frame(noise_state(), binary()) :: encrypt_result()

Encode data into protocol frame format.

Returns {frame_binary, updated_state}.

encrypt(state, plaintext)

@spec encrypt(noise_state(), binary()) :: encrypt_result()

Encrypt plaintext using current encryption key and counter.

Returns {encrypted_data, updated_state}.

finish_init(state)

@spec finish_init(noise_state()) :: noise_state()

Complete handshake initialization by splitting keys.

Returns updated noise state with finished handshake.

mix_into_key(state, data)

@spec mix_into_key(noise_state(), binary()) :: noise_state()

Mix data into key using HKDF.

Returns updated noise state with new keys.

new(ephemeral_key_pair, opts \\ [])

Create initial noise state with ephemeral key pair and configuration.

Returns a new noise state struct ready for handshake.

process_handshake(state, map, noise_key)

Process server hello message during handshake.

Returns {:ok, encrypted_key, updated_state} or {:error, reason}.