| 1 | package release |
| 2 | |
| 3 | import ( |
| 4 | "crypto/ed25519" |
| 5 | "encoding/base64" |
| 6 | "fmt" |
| 7 | "time" |
| 8 | ) |
| 9 | |
| 10 | // Signer is a release signing identity: a private key, and — when that key is |
| 11 | // scoped — the root-signed grant that says which rungs it may reach. |
| 12 | // |
| 13 | // It also carries the deployment's ROOT public key, which is the verification |
| 14 | // anchor for pointers being REPLACED. That used to be the signing key's own |
| 15 | // public half, and for a root signer it still is; for a scoped signer it |
| 16 | // cannot be, because the pointer a scoped key is about to replace was almost |
| 17 | // certainly signed by something else (the root key, or another scoped key). |
| 18 | // One anchor, both directions. |
| 19 | type Signer struct { |
| 20 | key ed25519.PrivateKey |
| 21 | // grant is the sub-object attached to every wrapper this signer writes; |
| 22 | // nil for a root signer, which attaches nothing and so writes the exact |
| 23 | // bytes it wrote before scoped keys existed. |
| 24 | grant *signedGrant |
| 25 | // scope is grant's verified contents — nil for a root signer, which may |
| 26 | // sign any channel. |
| 27 | scope *Grant |
| 28 | root ed25519.PublicKey |
| 29 | } |
| 30 | |
| 31 | // RootSigner is today's identity: the deployment's root key, unrestricted. |
| 32 | func RootSigner(key ed25519.PrivateKey) Signer { |
| 33 | pub, _ := key.Public().(ed25519.PublicKey) |
| 34 | return Signer{key: key, root: pub} |
| 35 | } |
| 36 | |
| 37 | // ScopedSigner is a delegated identity: a scoped key plus the base64 grant |
| 38 | // blob that authorises it (CARLOS_RELEASE_GRANT), checked here against the |
| 39 | // deployment's root public key. The grant is verified at construction — a |
| 40 | // signer that cannot prove its own authority must not exist long enough to |
| 41 | // sign anything. |
| 42 | func ScopedSigner(key ed25519.PrivateKey, grantB64 string, root ed25519.PublicKey) (Signer, error) { |
| 43 | if len(key) != ed25519.PrivateKeySize { |
| 44 | return Signer{}, fmt.Errorf("scoped signing: CARLOS_RELEASE_KEY is not a signing key") |
| 45 | } |
| 46 | if len(root) != ed25519.PublicKeySize { |
| 47 | return Signer{}, fmt.Errorf("scoped signing needs the deployment's root public key in CARLOS_RELEASE_PUBKEY: a scoped key's grant is only meaningful against the key the fleet trusts") |
| 48 | } |
| 49 | sg, err := parseGrantBlob(grantB64, "CARLOS_RELEASE_GRANT") |
| 50 | if err != nil { |
| 51 | return Signer{}, err |
| 52 | } |
| 53 | g, err := verifyGrant(root, sg) |
| 54 | if err != nil { |
| 55 | return Signer{}, err |
| 56 | } |
| 57 | pub, _ := key.Public().(ed25519.PublicKey) |
| 58 | if g.Pub != base64.StdEncoding.EncodeToString(pub) { |
| 59 | return Signer{}, fmt.Errorf("key grant %q is for a different key than CARLOS_RELEASE_KEY holds", g.Name) |
| 60 | } |
| 61 | return Signer{key: key, grant: sg, scope: g, root: root}, nil |
| 62 | } |
| 63 | |
| 64 | // verifyPub is the anchor this signer verifies EXISTING pointers against: the |
| 65 | // root public key, whether this signer is the root or a delegate of it. |
| 66 | func (s Signer) verifyPub() ed25519.PublicKey { return s.root } |
| 67 | |
| 68 | // allows reports whether this signer may sign for a channel at a moment. A |
| 69 | // root signer may sign anything, which is the property scoped keys exist to |
| 70 | // take away from everything else. |
| 71 | func (s Signer) allows(channel string, at time.Time) error { |
| 72 | if s.scope == nil { |
| 73 | return nil |
| 74 | } |
| 75 | return s.scope.allows(channel, at) |
| 76 | } |
| 77 | |
| 78 | // usable rejects a zero Signer before it reaches ed25519.Sign, which panics |
| 79 | // on a wrong-sized key. |
| 80 | func (s Signer) usable() error { |
| 81 | if len(s.key) != ed25519.PrivateKeySize { |
| 82 | return fmt.Errorf("no usable release signing key") |
| 83 | } |
| 84 | return nil |
| 85 | } |
| 86 | |