package release import ( "crypto/ed25519" "encoding/base64" "fmt" "time" ) // Signer is a release signing identity: a private key, and — when that key is // scoped — the root-signed grant that says which rungs it may reach. // // It also carries the deployment's ROOT public key, which is the verification // anchor for pointers being REPLACED. That used to be the signing key's own // public half, and for a root signer it still is; for a scoped signer it // cannot be, because the pointer a scoped key is about to replace was almost // certainly signed by something else (the root key, or another scoped key). // One anchor, both directions. type Signer struct { key ed25519.PrivateKey // grant is the sub-object attached to every wrapper this signer writes; // nil for a root signer, which attaches nothing and so writes the exact // bytes it wrote before scoped keys existed. grant *signedGrant // scope is grant's verified contents — nil for a root signer, which may // sign any channel. scope *Grant root ed25519.PublicKey } // RootSigner is today's identity: the deployment's root key, unrestricted. func RootSigner(key ed25519.PrivateKey) Signer { pub, _ := key.Public().(ed25519.PublicKey) return Signer{key: key, root: pub} } // ScopedSigner is a delegated identity: a scoped key plus the base64 grant // blob that authorises it (CARLOS_RELEASE_GRANT), checked here against the // deployment's root public key. The grant is verified at construction — a // signer that cannot prove its own authority must not exist long enough to // sign anything. func ScopedSigner(key ed25519.PrivateKey, grantB64 string, root ed25519.PublicKey) (Signer, error) { if len(key) != ed25519.PrivateKeySize { return Signer{}, fmt.Errorf("scoped signing: CARLOS_RELEASE_KEY is not a signing key") } if len(root) != ed25519.PublicKeySize { 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") } sg, err := parseGrantBlob(grantB64, "CARLOS_RELEASE_GRANT") if err != nil { return Signer{}, err } g, err := verifyGrant(root, sg) if err != nil { return Signer{}, err } pub, _ := key.Public().(ed25519.PublicKey) if g.Pub != base64.StdEncoding.EncodeToString(pub) { return Signer{}, fmt.Errorf("key grant %q is for a different key than CARLOS_RELEASE_KEY holds", g.Name) } return Signer{key: key, grant: sg, scope: g, root: root}, nil } // verifyPub is the anchor this signer verifies EXISTING pointers against: the // root public key, whether this signer is the root or a delegate of it. func (s Signer) verifyPub() ed25519.PublicKey { return s.root } // allows reports whether this signer may sign for a channel at a moment. A // root signer may sign anything, which is the property scoped keys exist to // take away from everything else. func (s Signer) allows(channel string, at time.Time) error { if s.scope == nil { return nil } return s.scope.allows(channel, at) } // usable rejects a zero Signer before it reaches ed25519.Sign, which panics // on a wrong-sized key. func (s Signer) usable() error { if len(s.key) != ed25519.PrivateKeySize { return fmt.Errorf("no usable release signing key") } return nil }