| 1 | package update |
| 2 | |
| 3 | import ( |
| 4 | "errors" |
| 5 | "fmt" |
| 6 | |
| 7 | "aead.dev/minisign" |
| 8 | ) |
| 9 | |
| 10 | // publicKey is the minisign public key that desktop release artifacts are signed |
| 11 | // with. The public half is safe to embed; the private half lives only in CI |
| 12 | // secrets (generated with `cmd/sign genkey`). Key ID AF12CA46F4A9EBB0. If the |
| 13 | // signing key is ever rotated, regenerate and update this constant in lockstep |
| 14 | // with the CI secret. |
| 15 | const publicKey = `untrusted comment: minisign public key: AF12CA46F4A9EBB0 |
| 16 | RWSw66n0RsoSr6Zhh6qt5YO95YkpCayTOCMFVDNUQSjJYwxoYngNVBSq` |
| 17 | |
| 18 | // Verify reports whether sig (the contents of a .minisig file) is a valid minisign |
| 19 | // signature of data under the embedded public key. A nil return means the artifact |
| 20 | // is authentic; any error means do not trust it. Callers MUST verify before |
| 21 | // touching disk — never apply an update whose signature has not checked out. |
| 22 | func Verify(data, sig []byte) error { return verifyWith(publicKey, data, sig) } |
| 23 | |
| 24 | // PublicKey returns the embedded public key in its canonical two-line text form, |
| 25 | // so docs/UI can surface it for manual `minisign -Vm <file>` verification. |
| 26 | func PublicKey() string { return publicKey } |
| 27 | |
| 28 | // verifyWith is the testable core: it parses an arbitrary public-key text and |
| 29 | // verifies the signature, letting tests use a throwaway key pair without the |
| 30 | // embedded key's (secret) counterpart. |
| 31 | func verifyWith(pubText string, data, sig []byte) error { |
| 32 | var key minisign.PublicKey |
| 33 | if err := key.UnmarshalText([]byte(pubText)); err != nil { |
| 34 | return fmt.Errorf("update: parse public key: %w", err) |
| 35 | } |
| 36 | if !minisign.Verify(key, data, sig) { |
| 37 | return errors.New("update: signature verification failed") |
| 38 | } |
| 39 | return nil |
| 40 | } |
| 41 |