Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions badgeverify/badgeverify.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,14 @@ const (
// credentials: ship the new key alongside the old, issue under the new
// kid, retire the old once its credentials expire.
//
// Overridable at build time (the compiled-in default is a placeholder —
// it MUST be replaced at release):
// The compiled-in default pins the production badge issuer key (kid bdg-v1),
// whose private half lives only in Cloud KMS (EC_SIGN_ED25519, key ring
// pilot-badges/badge-issuer). The public key is non-secret by design — it is
// meant to be embedded in every verifier. Overridable at build time for
// rotation:
//
// -ldflags "-X github.com/pilot-protocol/common/badgeverify.keyringB64=v1=<b64>"
var keyringB64 = "v1=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
// -ldflags "-X github.com/pilot-protocol/common/badgeverify.keyringB64=bdg-v2=<b64>"
var keyringB64 = "bdg-v1=Y2jjSAS+J6LVXAguY4P51vMGhHl7qgy5qBJZGS0Cmms="

// recoveryKeyringB64 is a SEPARATE pinned keyring holding only the COLD
// recovery-authority keys, which sign nothing but recovery authorizations.
Expand Down
62 changes: 54 additions & 8 deletions badgeverify/badgeverify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,61 @@ func TestUnknownKidFailsClosed(t *testing.T) {
}
}

func TestPlaceholderKeyFailsClosed(t *testing.T) {
// With the compiled-in all-zeros placeholder, verification must fail
// CLOSED with ErrNoKey — never attempting an ed25519.Verify against the
// low-order zero key. Guards against shipping without the -ldflags
// issuer-key override.
func TestIssuerKeyPinned(t *testing.T) {
// The production badge issuer key (kid bdg-v1) is pinned in the compiled-in
// keyring. Confirm it is present and is NOT the all-zero placeholder, and
// that a badge bearing kid bdg-v1 but signed by a FOREIGN key is rejected
// with ErrBadSignature — i.e. a real ed25519.Verify runs against the pinned
// key, rather than failing closed with ErrNoKey (which would mean the key
// was never pinned).
pk := keyFor("bdg-v1")
if pk == nil {
t.Fatal("bdg-v1 issuer key is not pinned in the compiled-in keyring")
}
if isAllZero(pk) {
t.Fatal("bdg-v1 issuer key is still the all-zero placeholder")
}
_, priv, _ := ed25519.GenerateKey(rand.Reader)
s, sig := sign(t, priv, validBadge())
if _, err := Verify(s, sig); !errors.Is(err, ErrNoKey) {
t.Fatalf("placeholder keyring must fail closed with ErrNoKey, got %v", err)
b := validBadge()
b.Kid = "bdg-v1"
s, sig := sign(t, priv, b)
if _, err := Verify(s, sig); !errors.Is(err, ErrBadSignature) {
t.Fatalf("foreign-signed badge under the pinned kid must fail with ErrBadSignature, got %v", err)
}
}

func TestPinnedIssuerGoldenVector(t *testing.T) {
// A real badge signed by the production KMS issuer key (bdg-v1) must verify
// against the pinned public key. This vector was produced with
// `gcloud kms asymmetric-sign` (key ring pilot-badges/badge-issuer) over the
// canonical badge below; baking it in locks down that genuine KMS signatures
// validate offline — with no KMS access required at test time.
const badge = "pilotbadge:v1:12345:github:1781827200:0:bdg-v1:"
const sig = "Gt7fdGmEYppTEFGSRIGsjb79ol6vffH1kinMgbis3ok6uCOPKyVSuDivgiCPlqNod9/X7CK9FiCLS+5YlFVVBg=="

b, err := Verify(badge, sig)
if err != nil {
t.Fatalf("KMS-signed golden badge must verify against the pinned key: %v", err)
}
if b.NodeID != 12345 || b.Provider != "github" || b.Kid != "bdg-v1" {
t.Fatalf("unexpected parsed badge: %+v", b)
}
// Node-binding rule: the same vector binds to node 12345 and no other.
if _, err := VerifyForNode(badge, sig, 12345); err != nil {
t.Errorf("VerifyForNode(12345) should pass: %v", err)
}
if _, err := VerifyForNode(badge, sig, 99999); !errors.Is(err, ErrNodeMismatch) {
t.Errorf("VerifyForNode(99999) must fail ErrNodeMismatch, got %v", err)
}
}

func TestRecoveryKeyStillPlaceholderFailsClosed(t *testing.T) {
// The COLD recovery keyring stays an all-zero placeholder until the sole
// custodian pins rec-v1. Until then every recovery authorization must fail
// closed with ErrNoKey — pinning the badge issuer key above must NOT have
// accidentally enabled recovery.
if pk := recoveryKeyFor("rec-v1"); pk != nil && !isAllZero(pk) {
t.Fatal("recovery keyring is no longer a placeholder — rec-v1 must stay unpinned")
}
}

Expand Down
Loading