Files
Privatae e4c9b69b46
ci / personal-data (push) Successful in 5s
ci / test (push) Successful in 3m4s
ci / fuzz (push) Successful in 3m45s
bastion 0.53.2
2026-10-20 04:42:14 +00:00

200 lines
8.1 KiB
Go

package main
import (
"crypto/ed25519"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"os"
"strings"
"testing"
)
// The shared vector. client-go/release_sign_test.go holds the same three
// values; if the statement format changes on one side only, one of the two
// tests fails.
var (
vectorSeed = strings.Repeat("\x01", ed25519.SeedSize)
vectorManifest = []byte(`{"version":"1.2.3","binaries":{}}`)
)
func vectorKey() ed25519.PrivateKey { return ed25519.NewKeyFromSeed([]byte(vectorSeed)) }
func TestSharedVector(t *testing.T) {
priv := vectorKey()
sum := sha256.Sum256(vectorManifest)
sig := base64.StdEncoding.EncodeToString(ed25519.Sign(priv, Statement("bastion", "1.2.3", hex.EncodeToString(sum[:]))))
if sig != sharedVectorSig {
t.Fatalf("the statement format changed: signature is now %s", sig)
}
pub := base64.StdEncoding.EncodeToString(priv.Public().(ed25519.PublicKey))
if got, err := VerifyManifest("bastion", vectorManifest, []byte(sig), []string{pub}); err != nil || got != pub {
t.Fatalf("verify: %v", err)
}
}
const sharedVectorSig = "tr5h3oXjBDmwvwQiZSriW4T6Y6oBFQDymfofLEGXMdTJSXfoQAKN+ajhCs89FO9vdCEZH3Y8mYzYXPZ7RzZVDw=="
// The C-Series Cloud vector. The box's updater (vm/cloud in the C-Series
// tree) checks the same seed, manifest and signature, so a change to how a
// cseries-cloud statement is built fails on both sides.
var cloudVectorManifest = []byte(`{"version":"1.2.3","images":{}}`)
const cloudVectorSig = "69+ug9q0ndTtLijHaOlpD8aFiQcO9/TRvmB+4jLNNrDSbMzx1mvDlOXzOgxDvQLTWa8LZmqxHABLF75kxYl4Bw=="
func TestCloudKindSignsAndVerifiesOnlyAsItself(t *testing.T) {
priv := vectorKey()
pub := base64.StdEncoding.EncodeToString(priv.Public().(ed25519.PublicKey))
sum := sha256.Sum256(cloudVectorManifest)
if err := checkStatement("cseries-cloud", "1.2.3", hex.EncodeToString(sum[:])); err != nil {
t.Fatalf("a cseries-cloud statement must be signable: %v", err)
}
sig := base64.StdEncoding.EncodeToString(ed25519.Sign(priv, Statement("cseries-cloud", "1.2.3", hex.EncodeToString(sum[:]))))
if sig != cloudVectorSig {
t.Fatalf("the cseries-cloud statement changed: signature is now %s", sig)
}
if got, err := VerifyManifest("cseries-cloud", cloudVectorManifest, []byte(sig), []string{pub}); err != nil || got != pub {
t.Fatalf("verify: %v", err)
}
for _, other := range []string{"bastion", "bastion-agent"} {
if _, err := VerifyManifest(other, cloudVectorManifest, []byte(sig), []string{pub}); err == nil {
t.Fatalf("a cseries-cloud signature must not pass as %s", other)
}
}
bastionSig := []byte(base64.StdEncoding.EncodeToString(ed25519.Sign(priv, Statement("bastion", "1.2.3", hex.EncodeToString(sum[:])))))
if _, err := VerifyManifest("cseries-cloud", cloudVectorManifest, bastionSig, []string{pub}); err == nil {
t.Fatal("a bastion signature must not pass as a cseries-cloud signature")
}
}
// The C-Series App Store catalog. The C-Series Host's public store reader
// builds the same statement and asserts these exact bytes for it, so a change
// to the text on either side fails a test.
var storeVectorCatalog = []byte(`{"version":"1.2.3","apps":[]}`)
const storeVectorSig = "+wAepICIvr4vLfhhbbONLJjP2KnoEysh7hMZjZtgRbqrxVcUiHKSWbzP3AoOKemvH7oHjRMkpqQAX/GVlmjWDg=="
func TestStoreKindSignsAndVerifiesOnlyAsItself(t *testing.T) {
want := "privatae-release-signature-v1\ncseries-store\n1.2.3\n" + strings.Repeat("ab", 32) + "\n"
if got := string(Statement("cseries-store", "1.2.3", strings.Repeat("ab", 32))); got != want {
t.Fatalf("the cseries-store statement is %q, the C-Series reader builds %q", got, want)
}
priv := vectorKey()
pub := base64.StdEncoding.EncodeToString(priv.Public().(ed25519.PublicKey))
sum := sha256.Sum256(storeVectorCatalog)
if err := checkStatement("cseries-store", "1.2.3", hex.EncodeToString(sum[:])); err != nil {
t.Fatalf("a cseries-store statement must be signable: %v", err)
}
sig := base64.StdEncoding.EncodeToString(ed25519.Sign(priv, Statement("cseries-store", "1.2.3", hex.EncodeToString(sum[:]))))
if sig != storeVectorSig {
t.Fatalf("the cseries-store statement changed: signature is now %s", sig)
}
if got, err := VerifyManifest("cseries-store", storeVectorCatalog, []byte(sig), []string{pub}); err != nil || got != pub {
t.Fatalf("verify: %v", err)
}
for _, other := range []string{"bastion", "bastion-agent", "cseries-cloud"} {
if _, err := VerifyManifest(other, storeVectorCatalog, []byte(sig), []string{pub}); err == nil {
t.Fatalf("a cseries-store signature must not pass as %s", other)
}
otherSig := []byte(base64.StdEncoding.EncodeToString(ed25519.Sign(priv, Statement(other, "1.2.3", hex.EncodeToString(sum[:])))))
if _, err := VerifyManifest("cseries-store", storeVectorCatalog, otherSig, []string{pub}); err == nil {
t.Fatalf("a %s signature must not pass as a cseries-store signature", other)
}
}
}
func TestSealAndOpen(t *testing.T) {
priv := vectorKey()
kf, err := seal(priv.Seed(), priv.Public().(ed25519.PublicKey), []byte("correct horse battery"))
if err != nil {
t.Fatal(err)
}
got, err := open(kf, []byte("correct horse battery"))
if err != nil || !got.Equal(priv) {
t.Fatalf("the right passphrase must open the key: %v", err)
}
if _, err := open(kf, []byte("wrong horse battery")); err == nil {
t.Fatal("a wrong passphrase must not open the key")
}
kf.Pub = base64.StdEncoding.EncodeToString(make([]byte, 32))
if _, err := open(kf, []byte("correct horse battery")); err == nil {
t.Fatal("a key file whose public key was swapped must not open")
}
}
func TestVerifyRefusesTheWrongKindOrAChangedManifest(t *testing.T) {
priv := vectorKey()
pub := base64.StdEncoding.EncodeToString(priv.Public().(ed25519.PublicKey))
sum := sha256.Sum256(vectorManifest)
sig := []byte(base64.StdEncoding.EncodeToString(ed25519.Sign(priv, Statement("bastion", "1.2.3", hex.EncodeToString(sum[:])))))
if _, err := VerifyManifest("bastion-agent", vectorManifest, sig, []string{pub}); err == nil {
t.Fatal("a bastion signature must not pass as an agent signature")
}
if _, err := VerifyManifest("bastion", []byte(`{"version":"1.2.4","binaries":{}}`), sig, []string{pub}); err == nil {
t.Fatal("a changed manifest must not verify")
}
}
func TestSignRefusesAnythingThatIsNotAStatement(t *testing.T) {
good := strings.Repeat("a", 64)
for _, c := range [][3]string{
{"other", "1.2.3", good},
{"bastion", "1.2.3\nextra", good},
{"bastion", "1.2.3", "abc"},
{"bastion", "1.2.3", strings.Repeat("A", 64)},
} {
if err := checkStatement(c[0], c[1], c[2]); err == nil {
t.Fatalf("%q must be refused", c)
}
}
if err := checkStatement("bastion-agent", "0.1.12", good); err != nil {
t.Fatal(err)
}
}
func TestPinnedKeysReadsWhatTheCompilerSees(t *testing.T) {
dir := t.TempDir()
write := func(src string) string {
p := dir + "/release_sign.go"
if err := os.WriteFile(p, []byte(src), 0o600); err != nil {
t.Fatal(err)
}
return p
}
k1 := strings.Repeat("A", 43) + "="
k2 := strings.Repeat("B", 43) + "="
for name, c := range map[string]struct {
src string
want int
}{
"empty": {"package main\nvar productionReleaseKeys = []string{}\nfunc x() {\n\t_ = \"" + k1 + "\"\n}\n", 0},
"one line": {"package main\nvar productionReleaseKeys = []string{\"" + k1 + "\", \"" + k2 + "\"}\n", 2},
"commented": {"package main\nvar productionReleaseKeys = []string{\n\t// \"" + k2 + "\",\n\t\"" + k1 + "\",\n}\n", 1},
"var block": {"package main\nvar (\n\tother = 1\n\tproductionReleaseKeys = []string{\n\t\t\"" + k1 + "\",\n\t\t\"" + k2 + "\",\n\t}\n)\n", 2},
} {
keys, err := PinnedKeys(write(c.src))
if err != nil || len(keys) != c.want {
t.Fatalf("%s: got %v %v, want %d keys", name, keys, err, c.want)
}
}
}
func TestOpenRefusesOutOfRangeSettings(t *testing.T) {
priv := vectorKey()
kf, err := seal(priv.Seed(), priv.Public().(ed25519.PublicKey), []byte("correct horse battery"))
if err != nil {
t.Fatal(err)
}
for _, mutate := range []func(*keyFile){
func(k *keyFile) { k.Time = 0 },
func(k *keyFile) { k.Thread = 0 },
func(k *keyFile) { k.Memory = 1 << 31 },
} {
bad := *kf
mutate(&bad)
if _, err := open(&bad, []byte("correct horse battery")); err == nil {
t.Fatal("out-of-range settings must be refused, not run")
}
}
}