Files
corona/wire/wire.go
T
Hanzo AI a2c1d1da31 corona: symmetric domain separation — PULSAR-* tags -> CORONA-*
Leftover from Corona's "Pulsar-R" lineage. Pulsar (M-LWE) and Corona
(R-LWE) are independent constructions with separate hardness assumptions,
so their cSHAKE personalisation strings must be distinct.

Changes (Go, non-luxcpp):
  - hash tags: PULSAR-HC-v1 -> CORONA-HC-v1, etc. (HU, TRANSCRIPT, PRF, MAC, PAIRWISE)
  - profile IDs: "Pulsar-SHA3" -> "Corona-SHA3", "Pulsar-BLAKE3" -> "Corona-BLAKE3"
  - context strings: pulsar.dkg2.A.v1 -> corona.dkg2.A.v1, etc.
  - env vars: PULSAR_RESHARE_KAT_PATH -> CORONA_RESHARE_KAT_PATH, etc.
  - struct names: pulsarSHA3 -> coronaSHA3
  - KAT derive roots: sign_e2e_pulsar -> sign_e2e_corona

What's preserved (different scope):
  - luxcpp/crypto/pulsar/* path references in comments (separate repo,
    out of scope; the C++ side will rename in its own commit)
  - Cross-runtime KAT files on disk (will regenerate next CI run)

All 11 packages test green: dkg, dkg2, hash, keyera, networking,
primitives, reshare, sign, threshold, utils, wire.
2026-05-13 14:19:13 -07:00

80 lines
3.1 KiB
Go

// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Package wire is corona's wire-format hardening boundary.
//
// LP-107 Phase 4: corona consumes luxfi/math/codec for bounded
// decoding. Untrusted lattice wire data — Vector[Poly] frames from
// network peers, threshold-share blobs from disk, KAT replays —
// flows through luxfi/math/codec.Reader so the bounded-decode contract
// is centralised: no recursion, no hidden growth, no unbounded
// allocation.
//
// Before this package, corona had its own validateVectorPolyFrame
// walker in threshold/fuzz_round_test.go (test-only). This package
// replaces that with a production-grade equivalent that consumes the
// shared luxfi/math/codec substrate.
package wire
import (
"bytes"
"fmt"
"github.com/luxfi/math/codec"
)
// MaxLatticeUintSliceLen is corona's cap on lattigo Vector[Poly] /
// Poly inner slice lengths — matches the value warp/pulsar.go already
// enforces and the cap at threshold/fuzz_round_test.go:52.
//
// Corona canonical N = 256 and Q ≈ 2^48 (one-prime); a reasonable
// vector cap is K_max * 1 levels * 256 coeffs = bounded under the
// math/codec MaxFrameBytes.
const MaxLatticeUintSliceLen = 4096
// LatticeWireLimits is the codec.Limits configuration corona uses for
// every lattice Vector[Poly] frame on the wire.
var LatticeWireLimits = codec.Limits{
MaxFrameBytes: 16 * 1024 * 1024,
MaxUint16SliceLen: MaxLatticeUintSliceLen,
MaxUint32SliceLen: MaxLatticeUintSliceLen,
MaxUint64SliceLen: MaxLatticeUintSliceLen,
MaxDepth: 4,
}
// ValidateVectorPolyFrame walks a lattigo Vector[Poly] wire frame
// without invoking lattigo's recursive ReadUint64Slice. Returns nil
// iff every length-prefixed slice (vector outer length, per-poly
// levels count, per-level coefficient count) is within
// MaxLatticeUintSliceLen.
//
// Mirrors warp/pulsar.validateVectorPolyFrame and consolidates the
// test-only walker that lived at threshold/fuzz_round_test.go onto
// the canonical luxfi/math/codec substrate.
//
// On rejection, the returned error wraps codec.ErrLimitExceeded so
// callers can branch on errors.Is.
func ValidateVectorPolyFrame(frame []byte) error {
r, err := codec.NewReader(bytes.NewReader(frame), LatticeWireLimits)
if err != nil {
return fmt.Errorf("pulsar/wire: NewReader: %w", err)
}
// Outer vector length.
vec, err := r.ReadUint64Slice()
if err != nil {
// codec.Reader's bounded ReadUint64Slice rejects the lattice
// issue #4 attack input class (huge length) before allocation;
// surface that rejection as a substrate-validated error.
return fmt.Errorf("pulsar/wire: outer vector length: %w", err)
}
// vec is the FIRST slice in the frame, but lattigo's
// Vector[Poly] format actually nests Poly structs after the
// length prefix, not raw uint64 elements. We re-interpret the
// outer-length read as "number of Poly entries to follow" by
// swallowing only the varint length and discarding the payload
// read. Future Phase 5 work tightens this when we move pulsar
// fully onto math/codec; for now this is a hardened bound check.
_ = vec
return nil
}