Files
go-synapse-backupper/pkg/adapters/crypto/composite/golden_generate_test.go
T

121 lines
3.5 KiB
Go

//go:build golden_generate
// The golden_generate build tag is intentionally separate so CI never
// regenerates the committed fixture. Run ONCE locally to (re)commit:
//
// ~/sdk/go1.26.5/bin/go test -tags golden_generate \
// -run TestGenerateGoldenFixture -v \
// ./pkg/adapters/crypto/composite/...
//
// Then commit the produced testdata/golden-1byte.pqenc and
// testdata/golden-keys.json. Non-`-update` runs of TestGoldenFormat load
// the committed artifacts and verify decrypt-equality.
package composite
import (
"bytes"
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/json"
"fmt"
"os"
"testing"
"git.tswf.io/infra/go-synapse-backupper/pkg/domain/crypto"
)
// deterministicRand implements io.Reader via a SHA-256 counter stream so the
// golden fixture is byte-for-byte reproducible across machines and Go
// toolchain versions.
type deterministicRand struct {
seq uint64
}
func (d *deterministicRand) Read(
p []byte,
) (int, error) {
for offset := 0; offset < len(p); {
var b [8]byte
binary.BigEndian.PutUint64(b[:], d.seq)
d.seq++
out := sha256.New()
out.Write(b[:])
hashed := out.Sum(nil)
n := copy(p[offset:], hashed)
offset += n
}
return len(p), nil
}
// Hard-coded priv seeds so the committed golden-keys.json stays stable across
// builds — these are the test-only private "keys" the committed golden file
// decrypts against.
var (
goldenPqSeed = [32]byte{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
}
goldenClassicalSeed = [32]byte{
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
}
)
func TestGenerateGoldenFixture(
t *testing.T,
) {
pqPub := newFakePub(fakePqSchemeID, goldenPqSeed[:])
pqPriv := newFakePriv(fakePqSchemeID, goldenPqSeed[:])
classicalPub := newFakePub(fakeClassicalSchemeID, goldenClassicalSeed[:])
classicalPriv := newFakePriv(fakeClassicalSchemeID, goldenClassicalSeed[:])
reg := crypto.NewRegistry()
if err := reg.Register(fakePqSchemeID, newFakePqKem); err != nil {
t.Fatalf("register pq fake: %v", err)
}
if err := reg.Register(fakeClassicalSchemeID, newFakeClassicalKem); err != nil {
t.Fatalf("register classical fake: %v", err)
}
enc := NewEncryptor(reg)
rng := &deterministicRand{}
var encrypted bytes.Buffer
if err := enc.Encrypt(
bytes.NewReader([]byte{0xAA}),
[]crypto.RecipientPub{pqPub, classicalPub},
&encrypted,
rng,
); err != nil {
t.Fatalf("Encrypt: %v", err)
}
if err := os.MkdirAll("testdata", 0o755); err != nil {
t.Fatalf("mkdir testdata: %v", err)
}
if err := os.WriteFile("testdata/golden-1byte.pqenc", encrypted.Bytes(), 0o644); err != nil {
t.Fatalf("write golden file: %v", err)
}
keys := goldenKeyFile{
Pq: base64.StdEncoding.EncodeToString(pqPriv.Raw()),
Classical: base64.StdEncoding.EncodeToString(classicalPriv.Raw()),
}
marshalled, err := json.MarshalIndent(keys, "", " ")
if err != nil {
t.Fatalf("marshal keys: %v", err)
}
marshalled = append(marshalled, '\n')
if err := os.WriteFile("testdata/golden-keys.json", marshalled, 0o644); err != nil {
t.Fatalf("write keys json: %v", err)
}
fmt.Printf("Golden fixture written: testdata/golden-1byte.pqenc (%d bytes), "+
"testdata/golden-keys.json (%d bytes)\n", len(encrypted.Bytes()), len(marshalled))
}