codec

package
v1.4.1 Latest Latest
Warning

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

Go to latest
Published: May 13, 2026 License: BSD-3-Clause Imports: 10 Imported by: 0

Documentation

Overview

Package codec is the bounded-decode contract for every wire format in luxfi/math (and downstream luxfi/lattice, luxfi/pulsar, luxfi/fhe).

LP-107 §"Codec and bounded reader should be centralized" — the canonical motivation. The lattigo `ReadUint64Slice` recursion bug (issue #2) and `Vector[T].ReadFrom` OOM bug (issue #4) both stemmed from unbounded slice decode on untrusted wire data; this package fixes that class permanently.

Contract:

  • No recursion. Slice readers are iterative; depth is bounded by the configured Limits.
  • No hidden growth. Every `make([]T, n)` is preceded by a `n <= cap` check against caller-supplied Limits.
  • No unbounded allocation. The largest cap is application-supplied and surfaces in error messages.
  • All readers are deterministic and reentrant; failure leaves the reader at the byte where the bound was exceeded.

Index

Constants

View Source
const KATSchemaV1 = "lux.math.kat.v1"

Variables

View Source
var DefaultLimitsLatticeWire = Limits{
	MaxFrameBytes:     16 * 1024 * 1024,
	MaxUint16SliceLen: 4096,
	MaxUint32SliceLen: 4096,
	MaxUint64SliceLen: 4096,
	MaxDepth:          4,
}

DefaultLimitsLatticeWire is the conservative default for wire-format decoding of lattice polynomials at the canonical Pulsar parameters (R_q = Z_q[X]/(X^256 + 1), Q ≈ 2^48). Callers SHOULD use a configuration tuned to their parameter set rather than this default.

MaxUint64SliceLen = 4096   matches Pulsar Vector[Poly] cap.
MaxFrameBytes = 16 MiB     allows a worst-case threshold ceremony
                           transcript without truncation.
MaxDepth = 4               Pulsar wire is 2 levels (Vector + Poly);
                           4 leaves headroom for FHE chains.
View Source
var ErrLimitExceeded = errors.New("codec: limit exceeded")

ErrLimitExceeded is the sentinel for any limit-bound rejection. errors.Is(err, ErrLimitExceeded) holds for every cap violation.

Functions

func HexDecode

func HexDecode(s string) ([]byte, error)

HexDecode is the inverse.

func HexEncode

func HexEncode(b []byte) string

HexEncode is a thin wrapper around encoding/hex for KAT JSON authoring.

func MakeUvarintFrame

func MakeUvarintFrame(length uint64, payload []uint64) []byte

MakeUvarintFrame returns the wire-format byte stream for a length- prefixed slice of uint64 with the supplied length. Used by KAT emitters to construct the canonical input bytes that ReadUint64Slice consumes.

func WriteKATBundle

func WriteKATBundle(w io.Writer, b *KATBundle) error

WriteKATBundle serializes the bundle to a writer in canonical JSON (sorted keys, indent=2). Two runs produce byte-equal output when the entries are byte-equal.

func WriteKATBundleFile

func WriteKATBundleFile(path string, b *KATBundle) error

WriteKATBundleFile writes the bundle to a file at path.

Types

type KATBundle

type KATBundle struct {
	Schema  string     `json:"schema"` // "lux.math.kat.v1"
	Entries []KATEntry `json:"entries"`
}

KATBundle is a collection of entries written to a JSON file at a stable path. C++ replay tests load the same file by path.

func ReadKATBundle

func ReadKATBundle(r io.Reader) (*KATBundle, error)

ReadKATBundle deserializes a bundle from a reader.

func ReadKATBundleFile

func ReadKATBundleFile(path string) (*KATBundle, error)

ReadKATBundleFile reads a bundle from a file at path.

type KATEntry

type KATEntry struct {
	// Header carries the canonical (parameter, backend, hash-suite,
	// implementation, version) tuple every KAT must surface.
	Header params.KATHeader `json:"header"`

	// Test is the human-readable test name (e.g. "ReadUint64Slice/
	// happy-path-3-elements", "MontMul/q=PulsarQ/100-random-pairs").
	Test string `json:"test"`

	// InputHex is the hex-encoded input byte stream consumed by the
	// substrate primitive under test.
	InputHex string `json:"input_hex"`

	// OutputHex is the hex-encoded canonical output byte stream.
	OutputHex string `json:"output_hex"`

	// OutputSHA256 is the SHA-256 commitment over OutputHex's raw
	// bytes (NOT the hex string). Used as a fast cross-runtime
	// equality check; full byte-stream is in OutputHex for diffing
	// on mismatch.
	OutputSHA256 string `json:"output_sha256"`
}

KATEntry is one cross-runtime KAT vector. Each entry pins a specific (test, parameter set, backend, input) tuple and records the SHA-256 (or BLAKE2b) digest of the byte-stream the substrate produces for that input.

Cross-runtime contract: emitting `KATEntry.Output` from Go and replaying it from the C++ side (luxcpp/crypto/math/test) MUST produce a byte-equal stream. Any divergence is a release-gate failure.

type LimitError

type LimitError struct {
	What  string // human-readable name of the cap, e.g. "MaxUint64SliceLen"
	Limit int
	Got   uint64
}

LimitError carries the specific limit that was exceeded plus the observed value. Wraps ErrLimitExceeded.

func (*LimitError) Error

func (e *LimitError) Error() string

Error implements error.

func (*LimitError) Unwrap

func (e *LimitError) Unwrap() error

Unwrap implements errors.Unwrap.

type Limits

type Limits struct {
	// MaxFrameBytes caps the total number of input bytes the Reader
	// will consume in a single decode call. Used to bound peek-ahead
	// buffers. 0 means unset and is treated as an error.
	MaxFrameBytes int

	// MaxUint16SliceLen caps the number of elements in a uint16 slice.
	MaxUint16SliceLen int
	// MaxUint32SliceLen caps the number of elements in a uint32 slice.
	MaxUint32SliceLen int
	// MaxUint64SliceLen caps the number of elements in a uint64 slice.
	MaxUint64SliceLen int

	// MaxDepth caps how deeply a recursively-shaped wire format may
	// nest before the reader rejects (Vector[Poly] etc. are 2 levels).
	MaxDepth int
}

Limits caps the largest slice / depth a Reader will accept on a single decode call. Callers MUST construct Limits explicitly; there is no implicit default.

func (Limits) Validate

func (l Limits) Validate() error

Validate reports whether the limits are coherent (all positive). Returns an error listing every zero/negative field.

type Reader

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

Reader wraps an io.Reader and a Limits config. Every slice-reading method on Reader is bounded by Limits.

func NewReader

func NewReader(r io.Reader, l Limits) (*Reader, error)

NewReader constructs a Reader from an io.Reader and a Limits config. Returns an error if Limits is invalid.

func (*Reader) Consumed

func (r *Reader) Consumed() int

Consumed returns the number of bytes read from the underlying io.Reader.

func (*Reader) EnterDepth

func (r *Reader) EnterDepth() error

EnterDepth bumps the nesting counter and returns an error if the configured MaxDepth is exceeded. Caller MUST pair with ExitDepth.

func (*Reader) ExitDepth

func (r *Reader) ExitDepth()

ExitDepth decrements the nesting counter.

func (*Reader) ReadUint16

func (r *Reader) ReadUint16() (uint16, error)

ReadUint16 reads a single little-endian uint16.

func (*Reader) ReadUint16Slice

func (r *Reader) ReadUint16Slice() ([]uint16, error)

ReadUint16Slice reads a length-prefixed slice of little-endian uint16. The length is read as a varint capped by MaxUint16SliceLen; iterative (no recursion).

func (*Reader) ReadUint32

func (r *Reader) ReadUint32() (uint32, error)

ReadUint32 reads a single little-endian uint32.

func (*Reader) ReadUint32Slice

func (r *Reader) ReadUint32Slice() ([]uint32, error)

ReadUint32Slice reads a length-prefixed slice of little-endian uint32.

func (*Reader) ReadUint64

func (r *Reader) ReadUint64() (uint64, error)

ReadUint64 reads a single little-endian uint64.

func (*Reader) ReadUint64Slice

func (r *Reader) ReadUint64Slice() ([]uint64, error)

ReadUint64Slice reads a length-prefixed slice of little-endian uint64. Bounded by MaxUint64SliceLen. The length-prefix is always read as a varint; values > MaxUint64SliceLen are rejected before any allocation.

This is the centralized fix for both lattigo issue #2 (recursive `ReadUint64Slice`) and issue #4 (`Vector[T].ReadFrom` unbounded allocation). Callers consuming untrusted lattice wire data MUST go through this method, never lattigo's raw `utils/buffer.ReadUint64Slice`.

Directories

Path Synopsis
cmd
emit_codec_kat command

Jump to

Keyboard shortcuts

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