Amarula.Protocol.Signal.SessionBuilder (amarula v0.1.0)
View SourceX3DH responder session establishment, ported from
node_modules/libsignal/src/session_builder.js.
Only the incoming (responder) path is implemented — we build a session when we receive a PreKeyWhisperMessage. The outgoing/initiator path (needed only to send the first message in a new conversation) is out of scope for the decrypt-only milestone.
Keys here are raw 32-byte X25519 keys (the leading 0x05 type byte is stripped
before DH). our_identity, prekeys etc. come from the session store.
Summary
Functions
Set up the initiator's first sending chain (used only on the initiator path).
Process an incoming PreKeyWhisperMessage: build a responder session into
record and return {updated_record, pre_key_id}.
Initiator X3DH: build an outgoing session from a fetched prekey bundle,
ported from libsignal session_builder.js initOutgoing.
Build the session state (X3DH + initial ratchet). Mirrors libsignal initSession. Public keys are passed wire-form (33 bytes with 0x05 prefix); DH strips it.
Functions
Set up the initiator's first sending chain (used only on the initiator path).
@spec init_incoming(Amarula.Protocol.Signal.SessionRecord.t(), map(), map()) :: {Amarula.Protocol.Signal.SessionRecord.t(), non_neg_integer() | nil}
Process an incoming PreKeyWhisperMessage: build a responder session into
record and return {updated_record, pre_key_id}.
store must provide:
load_pre_key.(id)-> %{public, private} | nilload_signed_pre_key.(id)-> %{public, private}our_identity-> %{public, private}
message is the decoded PreKeyWhisperMessage (see WhisperProtocol).
@spec init_outgoing(Amarula.Protocol.Signal.SessionRecord.t(), map(), map()) :: Amarula.Protocol.Signal.SessionRecord.t()
Initiator X3DH: build an outgoing session from a fetched prekey bundle,
ported from libsignal session_builder.js initOutgoing.
device is a map with the peer's bundle:
:registration_id— integer:identity_key— wire-form pubkey (33B 0x05-prefixed):signed_pre_key=>%{key_id, public, signature}:pre_key=>%{key_id, public}| nil (one-time prekey, optional)
Returns the updated SessionRecord with an open session whose
pending_pre_key makes the first send a pkmsg (SessionCipher.encrypt wraps
it). store.our_identity supplies our identity for the DH legs.
Build the session state (X3DH + initial ratchet). Mirrors libsignal initSession. Public keys are passed wire-form (33 bytes with 0x05 prefix); DH strips it.