carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
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.
19type 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.
32func 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.
42func 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.
66func (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.
71func (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.
80func (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