// Package updater handles binary self-update with Ed25519 signature verification. // See bible/rules/patterns/release-signing/contract.md for the full contract. package updater import ( "crypto/ed25519" "encoding/base64" "fmt" "strings" ) // trustedKeysRaw is injected at release build time via: // // -ldflags "-X bee/audit/internal/updater.trustedKeysRaw=base64key1:base64key2" // // Empty in dev builds — updates are disabled when empty. var trustedKeysRaw string // TrustedKeys decodes the embedded public keys. // Returns an error if the binary was built without key injection (dev build). func TrustedKeys() ([]ed25519.PublicKey, error) { if strings.TrimSpace(trustedKeysRaw) == "" { return nil, fmt.Errorf("dev build: trusted keys not embedded, updates disabled") } var keys []ed25519.PublicKey for _, enc := range strings.Split(trustedKeysRaw, ":") { enc = strings.TrimSpace(enc) if enc == "" { continue } b, err := base64.StdEncoding.DecodeString(enc) if err != nil { return nil, fmt.Errorf("decode trusted key: %w", err) } if len(b) != ed25519.PublicKeySize { return nil, fmt.Errorf("trusted key has wrong size: got %d, want %d", len(b), ed25519.PublicKeySize) } keys = append(keys, ed25519.PublicKey(b)) } if len(keys) == 0 { return nil, fmt.Errorf("no valid trusted keys found in build") } return keys, nil } // VerifySignature returns nil if sig was produced by any trusted key over data. // Returns an error (never panics) on any failure — caller logs and continues. func VerifySignature(data, sig []byte) error { keys, err := TrustedKeys() if err != nil { return err } for _, key := range keys { if ed25519.Verify(key, data, sig) { return nil } } return fmt.Errorf("signature verification failed: no trusted key matched") }