spqr

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Jul 2, 2026 License: AGPL-3.0 Imports: 10 Imported by: 0

Documentation

Overview

Package spqr implements Signal's Sparse Post-Quantum Ratchet (SPQR), the Stage-2 post-quantum layer that augments the Double Ratchet with chunked ML-KEM-768 key agreement. It is a pure-Go port of the SPQR reference crate (sparsepostquantumratchet v1.5.1) layered on the incremental ML-KEM-768 KEM in internal/mlkem768incr.

This file is Slice A: the ratchet-state codec. A SPQR ratchet state is serialized as the signal.proto.pq_ratchet.PqRatchetState protobuf — version negotiation, the Double-Ratchet-style epoch Chain, and a V1State holding the in-flight incremental-KEM artifacts (header, encaps key, encaps state, ciphertexts) plus the chunk-transport encoder/decoder state. The codec is a transparent byte round-trip over that proto: it stores and returns the exact bytes, performing no normalization (the libcrux issue-1275 endianness fix lives in the encapsulate2 path, not here). The chunked transport (Slice B) and the send/recv state machine (Slice C) build on this codec.

Index

Constants

View Source
const DefaultMaxJump uint32 = 25000

DefaultMaxJump is the SPQR Chain's default cap on how far ahead of the current counter a message key may be requested; a stored ChainParams.max_jump of 0 resolves to this. It matches the protocol-wide forward-jump cap (the same 25000 value as the Double Ratchet / sender-key bound, groups.MaxForwardJumps); SPQR carries it as its own ChainParams field (proto pq_ratchet ChainParams), so it is named here rather than imported across the domain boundary.

Variables

View Source
var (
	// ErrInvalidCtMac is returned when a ciphertext MAC does not verify.
	// Mirrors Error::InvalidCtMac.
	ErrInvalidCtMac = errors.New("spqr: ciphertext MAC is invalid")
	// ErrInvalidHdrMac is returned when an encapsulation-key header MAC does not
	// verify. Mirrors Error::InvalidHdrMac.
	ErrInvalidHdrMac = errors.New("spqr: encapsulation-key header MAC is invalid")
)

Authenticator MAC-verification errors, mirroring authenticator.rs Error.

View Source
var (
	// ErrKeyJump is returned when a requested key index is more than max_jump
	// ahead of the current counter. Mirrors Error::KeyJump.
	ErrKeyJump = errors.New("spqr: requested key too far ahead (max jump)")
	// ErrKeyTrimmed is returned when a requested out-of-order key is older than
	// the retention window. Mirrors Error::KeyTrimmed.
	ErrKeyTrimmed = errors.New("spqr: requested key already trimmed (too old)")
	// ErrKeyAlreadyRequested is returned when a key index was already consumed.
	// Mirrors Error::KeyAlreadyRequested.
	ErrKeyAlreadyRequested = errors.New("spqr: key already requested")
	// ErrEpochOutOfRange is returned for an epoch not in the retained window.
	// Mirrors Error::EpochOutOfRange.
	ErrEpochOutOfRange = errors.New("spqr: epoch out of range")
	// ErrSendKeyEpochDecreased is returned when send_key is asked for an epoch
	// older than the current send epoch. Mirrors Error::SendKeyEpochDecreased.
	ErrSendKeyEpochDecreased = errors.New("spqr: send key epoch decreased")
	// ErrChainDecode is returned when a Chain proto is structurally invalid.
	// Mirrors Error::StateDecode in the chain context.
	ErrChainDecode = errors.New("spqr: invalid chain state")
)

Chain errors, mirroring the relevant chain.rs Error variants.

View Source
var (
	// ErrVersionMismatch is returned when a peer presents a lower version than
	// ours and we are not allowed to negotiate. Mirrors Error::VersionMismatch.
	ErrVersionMismatch = errors.New("spqr: version mismatch after negotiation")
	// ErrMinimumVersion is returned when a peer's version is below our configured
	// minimum. Mirrors Error::MinimumVersion.
	ErrMinimumVersion = errors.New("spqr: peer version below minimum")
	// ErrChainNotAvailable is returned when a V1 state needs its Chain but none is
	// present and no version-negotiation block can build one. Mirrors
	// Error::ChainNotAvailable.
	ErrChainNotAvailable = errors.New("spqr: chain not available")
)

Orchestration errors, mirroring the relevant lib.rs Error variants.

View Source
var (
	// ErrEpochOutOfRangeV1 is returned when a received message's epoch is ahead
	// of the state's. Mirrors Error::EpochOutOfRange in the v1 dispatch.
	ErrEpochOutOfRangeV1 = errors.New("spqr: v1 message epoch out of range")
	// ErrErroneousData is returned when a received ek does not match the header
	// it should correspond to. Mirrors Error::ErroneousDataReceived.
	ErrErroneousData = errors.New("spqr: erroneous data received")
)

v1 errors mirroring the relevant reference Error variants.

View Source
var (
	// ErrInvalidState is returned when a serialized state cannot be decoded as a
	// PqRatchetState protobuf.
	ErrInvalidState = errors.New("spqr: invalid serialized state")
)

Errors returned by the state codec, %w-wrappable and errors.Is-matchable.

View Source
var ErrMsgDecode = errors.New("spqr: invalid serialized message")

ErrMsgDecode is returned when a serialized v1 message is malformed (wrong version byte, zero/absent epoch, truncated chunk, or an unknown message type). Mirrors the reference Error::MsgDecode.

View Source
var ErrV1StateDecode = errors.New("spqr: invalid v1 state")

ErrV1StateDecode is returned when a proto.V1State cannot be decoded into a v1State (missing inner variant, missing required sub-message, or a malformed encoder/decoder). Mirrors the reference Error::StateDecode.

Functions

func CurrentVersion

func CurrentVersion(b SerializedState) (proto.Version, error)

CurrentVersion reports the SPQR version a serialized state is operating at. A state with no inner is V0 (SPQR disabled); a V1State inner is V1. Mirrors the reference current_version (the version-negotiation detail — whether negotiation is still in progress — is a Slice C concern and not decoded here).

func DecodeState

func DecodeState(b SerializedState) (*proto.PqRatchetState, error)

DecodeState parses a serialized state into the generated PqRatchetState proto. An empty input decodes to a zero PqRatchetState (V0). Malformed input returns ErrInvalidState and never panics.

func EmbeddedEncapsState

func EmbeddedEncapsState(st *proto.PqRatchetState) ([]byte, bool)

EmbeddedEncapsState returns the incremental-KEM EncapsState (es) bytes carried by the state's current V1State variant, or (nil, false) when the variant holds no es. The send_ct side stores an es between encapsulate1 and encapsulate2; it lives in the Ct1Sent / Ct1SentEkReceived unchunked states (reached via the Ct1Sampled, Ct1Acknowledged, and EkReceivedCt1Sampled chunked variants).

The bytes are returned verbatim, exactly as stored — possibly carrying the libcrux issue-1275 swapped endianness. The codec never normalizes them; the encapsulate2 path (mlkem768incr.FixEncapsStateEndianness) does, just before use. This accessor lets that path (Slice C) and the codec's own round-trip test reach the embedded es.

func ResolveMaxJump

func ResolveMaxJump(p *proto.ChainParams) uint32

ResolveMaxJump returns the effective max-jump cap for a ChainParams: the stored value, or DefaultMaxJump when it is zero (the proto default). Mirrors the reference "if zero, defaults to 25,000".

Types

type EpochSecret

type EpochSecret struct {
	Epoch  uint64
	Secret []byte
}

EpochSecret is a per-epoch shared secret the Double Ratchet should mix into its key schedule. (Exposed at the package boundary; internally the v1 machine and Chain pass the unexported epochSecret.)

type Params

type Params struct {
	Direction   proto.Direction
	Version     proto.Version
	MinVersion  proto.Version
	AuthKey     []byte
	ChainParams *proto.ChainParams
}

Params configures a fresh SPQR state. Mirrors lib.rs Params.

type RecvResult

type RecvResult struct {
	State SerializedState
	Key   []byte // nil when no message key was produced
}

RecvResult is the output of Recv: the new serialized state and an optional message key.

func Recv

Recv folds an inbound SPQR message into the state. It first performs version negotiation (a lower-version message may downgrade us, or be rejected), then drives the v1 recv step, folds any epoch secret into the Chain, and derives the message key. Mirrors lib.rs recv.

type SendResult

type SendResult struct {
	State SerializedState
	Msg   SerializedMessage
	Key   []byte // nil when no message key was produced
}

SendResult is the output of Send: the new serialized state, the message to transmit, and an optional message key produced this step.

func Send

func Send(state SerializedState, rng io.Reader) (*SendResult, error)

Send produces the next outbound SPQR message and the updated state. For a V0 state it returns empty state/msg and no key. Mirrors lib.rs send.

type SerializedMessage

type SerializedMessage = []byte

SerializedMessage is a SPQR message in wire form (the v1 message codec bytes; empty for V0).

type SerializedState

type SerializedState = []byte

SerializedState is a SPQR ratchet state in its wire/storage form: the PqRatchetState protobuf bytes. The empty slice is the initial "no SPQR yet" state (libcrux empty_state()).

func EmptyState

func EmptyState() SerializedState

EmptyState returns the initial serialized state: empty bytes. Decoding it yields a PqRatchetState with no inner version (V0 / SPQR disabled), matching the reference empty_state().

func EncodeState

func EncodeState(st *proto.PqRatchetState) (SerializedState, error)

EncodeState serializes a PqRatchetState back to its wire bytes. For a state obtained from DecodeState and left unmodified, this reproduces the input bytes exactly (the codec is a transparent byte round-trip — see the package doc and the fixture round-trip test).

func InitialState

func InitialState(p Params) (SerializedState, error)

InitialState builds the initial serialized state for the given params. V0 yields the empty state; V1 yields a PqRatchetState with the role's initial v1 inner state and a version-negotiation block. Mirrors lib.rs initial_state.

type VersionStatus

type VersionStatus struct {
	Negotiating bool
	Version     proto.Version // the active/proposed version
	MinVersion  proto.Version // valid only while Negotiating
}

VersionStatus reports a state's negotiation status. Mirrors lib.rs CurrentVersion: either still negotiating (with the proposed and minimum versions) or negotiation complete (at a fixed version).

func Negotiation

func Negotiation(b SerializedState) (VersionStatus, error)

Negotiation reports the negotiation status of a serialized state. Mirrors lib.rs current_version.

Jump to

Keyboard shortcuts

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