ExWapp.Signal.WireFormat (ExWapp v0.1.2)

Copy Markdown View Source

Signal Protocol wire format implementation.

Based on the official Signal Protocol specification and libsignal-protocol-java.

Wire format:

  • SignalMessage: version_byte || protobuf || mac(8 bytes)
  • PreKeySignalMessage: version_byte || protobuf

Key format:

  • Public keys are serialized as: 0x05 || 32-byte-key (33 bytes total)

Version byte format:

  • (current_version << 4) | current_version

  • For version 3: 0x33

Summary

Functions

Decrypts ciphertext using AES-256-CBC and validates PKCS7 padding.

Deserializes a public key, removing the type prefix.

Encodes an unsigned integer as a varint.

Encrypts plaintext using AES-256-CBC with PKCS7 padding.

Returns the message type atom for the given serialized message.

Parses a serialized PreKeySignalMessage.

Parses a serialized SignalMessage.

Serializes a public key with the DJB type prefix (0x05).

Verifies a Signal MAC with compatibility fallbacks used by production WA clients.

Functions

decrypt_aes_cbc(ciphertext, cipher_key, iv)

@spec decrypt_aes_cbc(binary(), binary(), binary()) ::
  {:ok, binary()}
  | {:error, :invalid_ciphertext | :invalid_padding | :decrypt_failed}

Decrypts ciphertext using AES-256-CBC and validates PKCS7 padding.

deserialize_public_key(key)

Deserializes a public key, removing the type prefix.

encode_varint(value)

Encodes an unsigned integer as a varint.

encrypt_aes_cbc(plaintext, cipher_key, iv)

Encrypts plaintext using AES-256-CBC with PKCS7 padding.

This is the encryption used inside Signal messages (not AES-GCM!).

message_type(arg1)

Returns the message type atom for the given serialized message.

parse_prekey_signal_message(arg1)

@spec parse_prekey_signal_message(binary()) :: {:ok, map()} | {:error, term()}

Parses a serialized PreKeySignalMessage.

Returns map keys:

  • :version_byte
  • :registration_id
  • :prekey_id
  • :signed_prekey_id
  • :base_key
  • :identity_key
  • :message (inner SignalMessage bytes)

parse_signal_message(arg1)

@spec parse_signal_message(binary()) :: {:ok, map()} | {:error, term()}

Parses a serialized SignalMessage.

Returns map keys:

  • :version_byte
  • :version_and_proto (used for MAC verification)
  • :mac
  • :ratchet_key (32 bytes)
  • :counter
  • :previous_counter
  • :ciphertext

serialize_prekey_signal_message(registration_id, prekey_id, signed_prekey_id, base_key, identity_key, signal_message)

Serializes a PreKeySignalMessage.

Args:

  • registration_id: sender's registration ID
  • prekey_id: one-time prekey ID (optional)
  • signed_prekey_id: signed prekey ID
  • base_key: ephemeral public key from X3DH
  • identity_key: sender's identity public key
  • signal_message: already-serialized SignalMessage bytes

Returns the serialized prekey message bytes.

serialize_public_key(key)

Serializes a public key with the DJB type prefix (0x05).

serialize_signal_message(ratchet_key, counter, previous_counter, ciphertext, mac_key, sender_identity, receiver_identity)

Serializes a SignalMessage (whisper message).

Args:

  • ratchet_key: 32-byte sender's current ratchet public key
  • counter: message counter
  • previous_counter: previous chain's counter
  • ciphertext: AES-CBC encrypted message
  • mac_key: key for HMAC
  • sender_identity: sender's identity public key
  • receiver_identity: receiver's identity public key

Returns the serialized message bytes.

valid_signal_mac?(mac, mac_key, sender_identity, receiver_identity, version_and_proto)

@spec valid_signal_mac?(binary(), binary(), binary(), binary(), binary()) :: boolean()

Verifies the Signal MAC over version_and_proto.

verify_signal_mac(mac, mac_key, sender_identity, receiver_identity, version_and_proto)

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

Verifies a Signal MAC with compatibility fallbacks used by production WA clients.

Returns {:ok, strategy} when a check matches, or :error otherwise.