pulsar: GATE-2 hardened to call-graph reachability (GATE C) + CI legacy-tag invariant (items 3-4)

RED GATE-2 bypass: the structural gate greps only distributed_bcc.go for
KeyFromSeed(/func LargeCombine, so a reconstruct-then-sign wired via
deriveKeyMaterial+bccSign elsewhere PASSES.

GATE C (gate2_reachability_test.go): stdlib go/ast name call-graph over the
default-build files. Bans {KeyFromSeed, deriveKeyMaterial, bccSign,
shamirReconstruct*, LargeCombine, DealAlgShares} from being REACHABLE from the
committee sign entrypoints (AggregateBCC[WithBlame], NewDistributedBCCSigner,
DistributedBCCSigner.{Round1,Round2,Finalize,FinalizeWithBlame,SetNonceShare}).
Callee side over-approximated by name -> sound bias (over-report, never miss).
Two tests: (1) the REAL package is clean (8 entrypoints, none reach a banned
primitive); (2) the checker CATCHES RED's deriveKeyMaterial+bccSign bypass, both
as a synthetic graph AND via the real parse->graph pipeline on rogue source;
plus a clean-path negative control (no false positive).

Item 4 (ci_build_invariant_test.go): the production (default-tag) build never
carries legacy_trusted_dealer -- asserts the tag gates real reconstruct files,
they are absent from the default build, LargeCombine is quarantined, no
default file requires the tag, and DealAlgShares (trusted-dealer keygen) is
defined ONLY in _test.go.

Full suite green.
This commit is contained in:
zeekay
2026-06-27 21:15:13 -07:00
parent 42b580dc43
commit 6eee87d466
2 changed files with 431 additions and 0 deletions
@@ -0,0 +1,139 @@
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package pulsar
// ci_build_invariant_test.go — item 4: the PRODUCTION build must never ship the
// reconstruct-at-sign quarantine. RED LOW: the structural GATE-2 inspects
// build.Default (no tags), so CI must ALSO assert the production binary is never
// compiled with `-tags legacy_trusted_dealer` (which would re-link LargeCombine,
// the H-1 reconstruct footgun) and that the trusted-dealer keygen is test-only.
import (
"go/build"
"os"
"path/filepath"
"strings"
"testing"
)
const legacyTrustedDealerTag = "legacy_trusted_dealer"
func defaultBuildGoFiles(t *testing.T) (dir string, files map[string]bool) {
t.Helper()
pkg, err := build.Default.ImportDir(".", 0)
if err != nil {
t.Fatalf("enumerate default-build package: %v", err)
}
files = make(map[string]bool, len(pkg.GoFiles))
for _, f := range pkg.GoFiles {
files[f] = true
}
return pkg.Dir, files
}
func taggedBuildGoFiles(t *testing.T, tag string) map[string]bool {
t.Helper()
ctx := build.Default
ctx.BuildTags = append(append([]string{}, ctx.BuildTags...), tag)
pkg, err := ctx.ImportDir(".", 0)
if err != nil {
t.Fatalf("enumerate %s-tagged package: %v", tag, err)
}
out := make(map[string]bool, len(pkg.GoFiles))
for _, f := range pkg.GoFiles {
out[f] = true
}
return out
}
// goBuildLine returns the `//go:build ...` constraint line of a file (or "").
func goBuildLine(t *testing.T, path string) string {
t.Helper()
src, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read %s: %v", path, err)
}
for _, line := range strings.Split(string(src), "\n") {
l := strings.TrimSpace(line)
if strings.HasPrefix(l, "//go:build ") {
return l
}
// build constraints must precede the package clause.
if strings.HasPrefix(l, "package ") {
break
}
}
return ""
}
// TestCI_ProductionBuildExcludesLegacyTrustedDealer asserts the default build
// excludes every legacy_trusted_dealer file, that the tag actually gates real
// reconstruct files, and that the reconstruct-at-sign combiner is never in prod.
func TestCI_ProductionBuildExcludesLegacyTrustedDealer(t *testing.T) {
dir, defaultFiles := defaultBuildGoFiles(t)
taggedFiles := taggedBuildGoFiles(t, legacyTrustedDealerTag)
// Files the tag ADDS (present under -tags legacy_trusted_dealer, absent by
// default): these are the quarantined reconstruct files.
var legacyOnly []string
for f := range taggedFiles {
if !defaultFiles[f] {
legacyOnly = append(legacyOnly, f)
}
}
if len(legacyOnly) == 0 {
t.Fatalf("CI invariant MISCONFIGURED: the %q tag gates NO files — quarantine mechanism is inert", legacyTrustedDealerTag)
}
// Every tag-gated file must (a) be absent from the default/production build
// and (b) carry the legacy_trusted_dealer constraint (intentional gating).
for _, f := range legacyOnly {
if defaultFiles[f] {
t.Fatalf("CI FAILED: %s is in BOTH the default and tagged build", f)
}
line := goBuildLine(t, filepath.Join(dir, f))
if !strings.Contains(line, legacyTrustedDealerTag) {
t.Fatalf("CI FAILED: %s is tag-gated but its build line %q does not mention %q", f, line, legacyTrustedDealerTag)
}
}
// The reconstruct-at-sign combiner MUST be quarantined, never in prod.
if defaultFiles["large_threshold.go"] {
t.Fatalf("CI FAILED: large_threshold.go (LargeCombine, reconstruct-at-sign) is in the PRODUCTION build")
}
if !taggedFiles["large_threshold.go"] {
t.Fatalf("CI MISCONFIGURED: large_threshold.go not found even under the legacy tag")
}
// No default-build file may itself require the legacy tag (belt-and-suspenders
// against an accidental positive constraint that only builds under the tag).
for f := range defaultFiles {
if line := goBuildLine(t, filepath.Join(dir, f)); strings.Contains(line, legacyTrustedDealerTag) {
t.Fatalf("CI FAILED: default-build file %s carries a %q constraint", f, legacyTrustedDealerTag)
}
}
t.Logf("CI PASS: production build excludes %d legacy_trusted_dealer file(s) %v; LargeCombine quarantined", len(legacyOnly), legacyOnly)
}
// TestCI_TrustedDealerKeygenIsTestOnly asserts the trusted-dealer s1-share
// keygen (DealAlgShares) is defined ONLY in _test.go (uncompilable into any
// production binary) — the quarantine the no-reconstruct claim rests on.
func TestCI_TrustedDealerKeygenIsTestOnly(t *testing.T) {
dir, defaultFiles := defaultBuildGoFiles(t)
for f := range defaultFiles {
src, err := os.ReadFile(filepath.Join(dir, f))
if err != nil {
t.Fatalf("read %s: %v", f, err)
}
if strings.Contains(string(src), "func DealAlgShares(") {
t.Fatalf("CI FAILED: DealAlgShares (trusted-dealer keygen) defined in PRODUCTION file %s — must be _test.go only", f)
}
}
// Sanity: it IS defined somewhere in the test build (the bootstrap file).
if _, err := os.Stat(filepath.Join(dir, "bootstrap_dealer_test.go")); err != nil {
t.Fatalf("bootstrap_dealer_test.go missing — trusted-dealer keygen quarantine file gone: %v", err)
}
t.Logf("CI PASS: DealAlgShares is test-only (bootstrap_dealer_test.go); never in the production build")
}
@@ -0,0 +1,292 @@
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package pulsar
// gate2_reachability_test.go — GATE 2 HARDENED to call-graph REACHABILITY,
// closing RED's GATE-2 bypass (the old gate greps only distributed_bcc.go for
// `KeyFromSeed(` / `func LargeCombine`, so a reconstruct-then-sign wired via
// deriveKeyMaterial+bccSign elsewhere PASSES the scan).
//
// The hardened gate proves the no-reconstruct invariant where it matters: NO
// reconstruct / seed-expand / single-key-sign primitive is REACHABLE in the
// default-build call graph from ANY committee sign entrypoint. It is a stdlib
// (go/ast) name call-graph — no new dependency. The callee side is
// OVER-APPROXIMATED by name (a CallExpr to name X follows every definition
// named X), which is the SOUND bias for a security gate: it can over-report a
// reach, never miss one.
//
// GATE C is two tests: (1) the REAL package is clean (the invariant holds);
// (2) the checker actually CATCHES a deriveKeyMaterial+bccSign bypass wired into
// a sign entrypoint (a synthetic graph modeling RED's PoC).
import (
"go/ast"
"go/build"
"go/parser"
"go/token"
"path/filepath"
"sort"
"testing"
)
// bannedReconstructPrimitives are the seed-expand / reconstruct / single-key
// reconstruct-then-sign primitives that MUST be unreachable from the committee
// sign path. All are free functions (defKey == name).
var bannedReconstructPrimitives = map[string]bool{
"KeyFromSeed": true, // seed -> sk expansion
"deriveKeyMaterial": true, // seed -> full FIPS key material
"bccSign": true, // single-key reconstruct-then-sign reference
"LargeCombine": true, // reconstruct-at-sign combiner
"DealAlgShares": true, // trusted-dealer s1-share keygen
"shamirReconstruct": true,
"shamirReconstructGF": true,
"shamirReconstructGFQ": true,
"shamirReconstructQ": true,
}
// committeeSignEntrypoints are the production committee sign-combine roots that
// MUST stay reconstruct-free. Method keys are receiver-qualified.
var committeeSignEntrypoints = []string{
"AggregateBCC",
"AggregateBCCWithBlame",
"NewDistributedBCCSigner",
"DistributedBCCSigner.Round1",
"DistributedBCCSigner.Round2",
"DistributedBCCSigner.Finalize",
"DistributedBCCSigner.FinalizeWithBlame",
"DistributedBCCSigner.SetNonceShare",
}
// callGraph: defKey -> set of callee (unqualified) names, plus an index from
// unqualified name -> all defKeys with that name.
type callGraph struct {
calls map[string]map[string]bool
nameIndex map[string][]string
}
func recvTypeName(fn *ast.FuncDecl) string {
if fn.Recv == nil || len(fn.Recv.List) == 0 {
return ""
}
switch t := fn.Recv.List[0].Type.(type) {
case *ast.StarExpr:
if id, ok := t.X.(*ast.Ident); ok {
return id.Name
}
case *ast.Ident:
return t.Name
}
return ""
}
func defKeyFor(fn *ast.FuncDecl) (key, unqualified string) {
if rt := recvTypeName(fn); rt != "" {
return rt + "." + fn.Name.Name, fn.Name.Name
}
return fn.Name.Name, fn.Name.Name
}
// calleeName extracts the called function/method name from a CallExpr.
func calleeName(call *ast.CallExpr) string {
switch fn := call.Fun.(type) {
case *ast.Ident:
return fn.Name
case *ast.SelectorExpr:
return fn.Sel.Name
}
return ""
}
func buildCallGraph(files []*ast.File) *callGraph {
g := &callGraph{calls: map[string]map[string]bool{}, nameIndex: map[string][]string{}}
for _, f := range files {
for _, decl := range f.Decls {
fn, ok := decl.(*ast.FuncDecl)
if !ok || fn.Body == nil {
continue
}
key, unqual := defKeyFor(fn)
if g.calls[key] == nil {
g.calls[key] = map[string]bool{}
}
g.nameIndex[unqual] = appendUnique(g.nameIndex[unqual], key)
ast.Inspect(fn.Body, func(n ast.Node) bool {
if call, ok := n.(*ast.CallExpr); ok {
if name := calleeName(call); name != "" {
g.calls[key][name] = true
}
}
return true
})
}
}
return g
}
func appendUnique(s []string, v string) []string {
for _, x := range s {
if x == v {
return s
}
}
return append(s, v)
}
// reachBanned does BFS from the entrypoint defKeys over callee names and reports
// the first banned primitive reached, with the path that reaches it.
func (g *callGraph) reachBanned(entrypoints []string, banned map[string]bool) (hit string, path []string, found bool) {
type item struct {
key string
path []string
}
visited := map[string]bool{}
var queue []item
for _, e := range entrypoints {
queue = append(queue, item{key: e, path: []string{e}})
visited[e] = true
}
for len(queue) > 0 {
cur := queue[0]
queue = queue[1:]
callees := make([]string, 0, len(g.calls[cur.key]))
for c := range g.calls[cur.key] {
callees = append(callees, c)
}
sort.Strings(callees) // deterministic path reporting
for _, callee := range callees {
if banned[callee] {
return callee, append(append([]string{}, cur.path...), callee), true
}
for _, nextKey := range g.nameIndex[callee] {
if !visited[nextKey] {
visited[nextKey] = true
queue = append(queue, item{key: nextKey, path: append(append([]string{}, cur.path...), nextKey)})
}
}
}
}
return "", nil, false
}
// defaultBuildFuncFiles parses the package's DEFAULT-build (non-test,
// tag-honouring) Go files into ASTs — the same file set a production binary
// links (legacy_trusted_dealer files excluded).
func defaultBuildFuncFiles(t *testing.T) []*ast.File {
t.Helper()
pkg, err := build.Default.ImportDir(".", 0)
if err != nil {
t.Fatalf("enumerate default-build package: %v", err)
}
fset := token.NewFileSet()
files := make([]*ast.File, 0, len(pkg.GoFiles))
for _, name := range pkg.GoFiles {
f, err := parser.ParseFile(fset, filepath.Join(pkg.Dir, name), nil, 0)
if err != nil {
t.Fatalf("parse %s: %v", name, err)
}
files = append(files, f)
}
return files
}
// GATE C (the invariant) — no reconstruct primitive is reachable from the
// committee sign path in the real default-build call graph.
func TestGATE_C_NoReconstructReachableFromSignPath(t *testing.T) {
files := defaultBuildFuncFiles(t)
g := buildCallGraph(files)
// Every entrypoint must actually exist (guard against a renamed entrypoint
// silently emptying the gate).
for _, e := range committeeSignEntrypoints {
if _, ok := g.calls[e]; !ok {
t.Fatalf("GATE C MISCONFIGURED: sign entrypoint %q not found in the default build", e)
}
}
if hit, path, found := g.reachBanned(committeeSignEntrypoints, bannedReconstructPrimitives); found {
t.Fatalf("GATE C FAILED: reconstruct primitive %q is REACHABLE from the committee sign path:\n %v", hit, path)
}
// Also assert the banned primitives are actually DEFINED in the build (so we
// are testing reachability of real functions, not dead names).
defined := 0
for name := range bannedReconstructPrimitives {
if _, ok := g.nameIndex[name]; ok {
defined++
}
}
t.Logf("GATE C PASS: %d sign entrypoints; %d/%d banned reconstruct primitives present in the build, NONE reachable from the sign path",
len(committeeSignEntrypoints), defined, len(bannedReconstructPrimitives))
}
// GATE C (the catch) — the checker actually DETECTS RED's bypass: a synthetic
// graph in which the committee aggregator reaches deriveKeyMaterial+bccSign
// (reconstruct-then-sign) must be flagged. This proves the gate would FAIL if
// the bypass were wired in, rather than silently passing like the old grep.
func TestGATE_C_CatchesReconstructBypass(t *testing.T) {
// Model: AggregateBCC -> rogueReconstructSign -> {deriveKeyMaterial, bccSign}
g := &callGraph{
calls: map[string]map[string]bool{
"AggregateBCC": {"rogueReconstructSign": true},
"rogueReconstructSign": {"deriveKeyMaterial": true, "bccSign": true},
"deriveKeyMaterial": {},
"bccSign": {},
},
nameIndex: map[string][]string{
"AggregateBCC": {"AggregateBCC"},
"rogueReconstructSign": {"rogueReconstructSign"},
"deriveKeyMaterial": {"deriveKeyMaterial"},
"bccSign": {"bccSign"},
},
}
hit, path, found := g.reachBanned([]string{"AggregateBCC"}, bannedReconstructPrimitives)
if !found {
t.Fatalf("GATE C BROKEN: the reachability checker did NOT catch the deriveKeyMaterial+bccSign bypass")
}
t.Logf("GATE C catch PASS: bypass detected — banned %q reachable via %v", hit, path)
// Negative control: a clean sign path that never reaches a banned primitive
// must NOT be flagged (no false positive).
clean := &callGraph{
calls: map[string]map[string]bool{
"AggregateBCC": {"VerifyPartialProof": true, "FlatAggregateZ": true},
"VerifyPartialProof": {"partialFSChallenges": true},
"partialFSChallenges": {},
"FlatAggregateZ": {},
},
nameIndex: map[string][]string{
"AggregateBCC": {"AggregateBCC"},
"VerifyPartialProof": {"VerifyPartialProof"},
"partialFSChallenges": {"partialFSChallenges"},
"FlatAggregateZ": {"FlatAggregateZ"},
},
}
if _, _, found := clean.reachBanned([]string{"AggregateBCC"}, bannedReconstructPrimitives); found {
t.Fatalf("GATE C false positive: a clean sign path was flagged")
}
// Full-pipeline catch: parse ROGUE source (RED's exact bypass — a sign
// entrypoint that reconstructs via deriveKeyMaterial then bccSign) through
// the REAL buildCallGraph and assert reachBanned flags it. This exercises
// the actual AST extraction the gate runs on production files, not just a
// hand-built graph.
rogueSrc := `package pulsar
func AggregateBCC() { rogueReconstructSign() }
func rogueReconstructSign() {
km, _ := deriveKeyMaterial(ModeP65, nil)
_, _, _ = bccSign(km, ModeP65, nil, nil, nil, 1)
}
`
fset := token.NewFileSet()
rf, err := parser.ParseFile(fset, "rogue.go", rogueSrc, 0)
if err != nil {
t.Fatalf("parse rogue source: %v", err)
}
rg := buildCallGraph([]*ast.File{rf})
hit2, path2, found2 := rg.reachBanned([]string{"AggregateBCC"}, bannedReconstructPrimitives)
if !found2 {
t.Fatalf("GATE C BROKEN: real parse→graph pipeline missed the deriveKeyMaterial+bccSign bypass")
}
t.Logf("GATE C full-pipeline catch PASS: parsed rogue source flagged banned %q via %v", hit2, path2)
}