ratchet

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 30, 2026 License: AGPL-3.0 Imports: 3 Imported by: 0

Documentation

Overview

Package ratchet implements the Double Ratchet key schedule: the chain-key step, message-key derivation, root-key/DH ratchet step, and the PQXDH master secret. It is a pure-Go port of rust/protocol/src/ratchet/keys.rs and rust/protocol/src/ratchet.rs, and its outputs are vector-locked against upstream libsignal v0.91.0 (see compat/vectors/hkdf.json).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func PQXDHSecret

func PQXDHSecret(dh1, dh2, dh3, dh4, kyberSharedSecret []byte) []byte

PQXDHSecret exposes the PQXDH master-secret assembly (0xFF*32 || DH1..DH4 || kyber_ss) for callers/tests that need to verify the pre-KDF byte layout against upstream. The session layer (later task) computes the DH agreements; this only concatenates them in the fixed order.

Types

type ChainKey

type ChainKey struct {
	// contains filtered or unexported fields
}

ChainKey is a symmetric chain key in the Double Ratchet sending or receiving chain. It advances by HMAC and yields per-message MessageKeys, mirroring ChainKey in rust/protocol/src/ratchet/keys.rs.

func NewChainKey

func NewChainKey(key []byte, index uint32) (ChainKey, error)

NewChainKey builds a ChainKey from a 32-byte key and its chain index. It returns an error if key is not 32 bytes.

func (ChainKey) Format

func (c ChainKey) Format(f fmt.State, _ rune)

Format intercepts every fmt verb — including Go-syntax %#v and %x — so the key material can never leak through formatting that bypasses String(). Mirrors curve.PrivateKey.Format.

func (ChainKey) Index

func (c ChainKey) Index() uint32

Index returns the chain key's index (message counter within the chain).

func (ChainKey) Key

func (c ChainKey) Key() []byte

Key returns a copy of the 32-byte chain key material.

func (ChainKey) MessageKeys

func (c ChainKey) MessageKeys() MessageKeyGenerator

MessageKeys returns the message-key generator for this chain index. The message-key seed is HMAC-SHA256(key, 0x01); the generator defers the final HKDF until GenerateKeys is called with the (optional) Sparse Post-Quantum Ratchet message key, so the SPQR secret can be folded in per message — and so a SKIPPED message's seed can be cached and the keys derived later, once that specific message (and thus its SPQR key) arrives. Mirrors ChainKey::message_keys in rust/protocol/src/ratchet/keys.rs.

func (ChainKey) Next

func (c ChainKey) Next() ChainKey

Next returns the next chain key: its material is HMAC-SHA256(key, 0x02) and its index is incremented by one.

func (ChainKey) String

func (c ChainKey) String() string

String redacts the key material so chain keys never leak into logs.

type InitialKeys

type InitialKeys struct {
	RootKey  RootKey
	ChainKey ChainKey
	// PQRSeed is the 32-byte initial seed for the Sparse Post-Quantum Ratchet.
	// SPQR itself is ported later (PR phase per the plan); this carries the
	// seed through so the derivation is complete and vector-exact now.
	PQRSeed [32]byte
}

InitialKeys is the output of the PQXDH master-secret derivation: the initial root key, the initial chain key, and the seed for the post-quantum ratchet (SPQR). Mirrors the (RootKey, ChainKey, InitialPQRKey) tuple from ratchet.rs derive_keys.

func DeriveInitialKeys

func DeriveInitialKeys(dh1, dh2, dh3, dh4, kyberSharedSecret []byte) (InitialKeys, error)

DeriveInitialKeys runs the PQXDH master-secret key schedule (ratchet.rs derive_keys): it assembles the master secret (0xFF*32 || DH1 || DH2 || DH3 [|| DH4] || kyber_shared_secret), then HKDF-SHA256(ikm=secret, salt=nil, info=pqxdhInfo) -> 32B root || 32B chain || 32B pqr. The DH agreements must already be in the upstream order.

DH4 (= ephemeral × one-time prekey) is OPTIONAL: the one-time prekey is not always present in a PreKeyBundle. When it is absent, callers pass an empty dh4 and it is omitted from the master secret, exactly as upstream conditions the fourth agreement on Some(one_time_prekey) (rust/protocol/src/pqxdh.rs:220 for the initiator and :360 for the recipient). DH1..DH3 are always present.

func (InitialKeys) Format

func (k InitialKeys) Format(f fmt.State, _ rune)

Format intercepts every fmt verb (incl. %#v and %x) so neither the embedded keys nor the raw PQRSeed [32]byte can leak through formatting that bypasses String(). Without this, %#v on InitialKeys dumps PQRSeed as a byte array.

func (InitialKeys) String

func (k InitialKeys) String() string

String redacts all key material (root key, chain key, and the PQR seed).

type MessageKeyGenerator

type MessageKeyGenerator struct {
	// contains filtered or unexported fields
}

MessageKeyGenerator produces a message's MessageKeys, either by deferring the final key derivation from a stored seed (the common case, allowing the SPQR per-message key to be mixed in at derivation time) or by holding an already materialized MessageKeys (for cached keys from older, pre-SPQR sessions). Mirrors MessageKeyGenerator in rust/protocol/src/ratchet/keys.rs.

func NewMessageKeyGeneratorFromKeys

func NewMessageKeyGeneratorFromKeys(keys MessageKeys) MessageKeyGenerator

NewMessageKeyGeneratorFromKeys builds a Keys-variant generator wrapping already-derived MessageKeys (used when reloading cached keys produced by a pre-SPQR session, which stored the derived keys rather than the seed).

func NewMessageKeyGeneratorFromSeed

func NewMessageKeyGeneratorFromSeed(seed []byte, counter uint32) MessageKeyGenerator

NewMessageKeyGeneratorFromSeed builds a Seed-variant generator: the keys are derived later from seed at the given counter. Mirrors MessageKeyGenerator::new_from_seed.

func (MessageKeyGenerator) Format

func (g MessageKeyGenerator) Format(f fmt.State, _ rune)

Format intercepts every fmt verb — including Go-syntax %#v and hex %x — so the seed (Seed variant) or the embedded MessageKeys bytes (Keys variant) can never leak through formatting that bypasses String(). The generator needs its OWN Format: %#v of the outer struct recurses into the embedded MessageKeys by reflection, which does NOT invoke MessageKeys' Format. Mirrors the pattern on every other secret-bearing ratchet type (MessageKeys/ChainKey/RootKey).

func (MessageKeyGenerator) FromSeed

func (g MessageKeyGenerator) FromSeed() bool

FromSeed reports whether this generator defers derivation from a seed (true) or wraps already-materialized keys (false). The session storage codec uses this to decide whether to persist the seed or the derived keys.

func (MessageKeyGenerator) GenerateKeys

func (g MessageKeyGenerator) GenerateKeys(pqrKey []byte) (MessageKeys, error)

GenerateKeys materializes the MessageKeys, mixing in the optional Sparse Post-Quantum Ratchet message key (pqrKey) as the HKDF salt. A nil/empty pqrKey means no SPQR key for this message (V0 or pre-negotiation), which yields exactly the pre-SPQR derivation (salt=nil) — preserving backward compatibility. Mirrors MessageKeyGenerator::generate_keys.

For a Keys-variant generator (cached pre-SPQR keys) pqrKey MUST be nil: pre-SPQR sessions never mix a PQR key, and the keys are already derived.

func (MessageKeyGenerator) Seed

func (g MessageKeyGenerator) Seed() ([]byte, uint32, bool)

Seed returns the stored seed and counter for a Seed-variant generator; the bool is false for a Keys-variant generator.

func (MessageKeyGenerator) String

func (g MessageKeyGenerator) String() string

String redacts the secret material so the deferred seed (Seed variant) or the wrapped MessageKeys (Keys variant) never leak into logs.

type MessageKeys

type MessageKeys struct {
	// contains filtered or unexported fields
}

MessageKeys is the triple of symmetric keys for a single message: an AES cipher key, an HMAC key, and an IV, plus the chain index they were derived at. Mirrors MessageKeys in rust/protocol/src/ratchet/keys.rs.

func NewMessageKeys

func NewMessageKeys(cipherKey, macKey, iv []byte, index uint32) (MessageKeys, error)

NewMessageKeys assembles a MessageKeys from its component byte slices (used when reloading cached, already-derived keys from session storage). It validates each length.

func (MessageKeys) CipherKey

func (m MessageKeys) CipherKey() []byte

CipherKey returns a copy of the 32-byte AES cipher key.

func (MessageKeys) Format

func (m MessageKeys) Format(f fmt.State, _ rune)

Format intercepts every fmt verb (incl. %#v and %x) so the cipher key, MAC key, and IV can never leak through formatting that bypasses String().

func (MessageKeys) IV

func (m MessageKeys) IV() []byte

IV returns a copy of the 16-byte initialization vector.

func (MessageKeys) Index

func (m MessageKeys) Index() uint32

Index returns the chain index these message keys were derived at.

func (MessageKeys) MACKey

func (m MessageKeys) MACKey() []byte

MACKey returns a copy of the 32-byte HMAC key.

func (MessageKeys) String

func (m MessageKeys) String() string

String redacts the key material.

type RootKey

type RootKey struct {
	// contains filtered or unexported fields
}

RootKey is the Double Ratchet root key. Each DH ratchet step consumes the current root key plus a fresh ECDH agreement to produce the next root key and a new sending/receiving chain key. Mirrors RootKey in keys.rs.

func NewRootKey

func NewRootKey(key []byte) (RootKey, error)

NewRootKey builds a RootKey from 32 bytes, erroring if the length is wrong.

func (RootKey) CreateChain

func (r RootKey) CreateChain(theirRatchetKey curve.PublicKey, ourRatchetKey curve.PrivateKey) (RootKey, ChainKey, error)

CreateChain performs one DH ratchet step (RootKey::create_chain): it computes the ECDH agreement between ourRatchetKey and theirRatchetKey, then HKDF-SHA256(ikm=agreement, salt=currentRootKey, info="WhisperRatchet") to derive 32B next root key || 32B chain key. The returned chain key starts at index 0.

func (RootKey) Format

func (r RootKey) Format(f fmt.State, _ rune)

Format intercepts every fmt verb (incl. %#v and %x) so the root key material can never leak through formatting that bypasses String().

func (RootKey) Key

func (r RootKey) Key() []byte

Key returns a copy of the 32-byte root key material.

func (RootKey) String

func (r RootKey) String() string

String redacts the key material.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL