121 lines
3.5 KiB
Go
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))
|
|
}
|