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 ¶
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 ¶
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 ¶
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) 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.
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.
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 ¶
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 ¶
Format intercepts every fmt verb (incl. %#v and %x) so the root key material can never leak through formatting that bypasses String().