feat(license): add signing and the trusted key list
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
package license
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hyperboloide/lk"
|
||||
)
|
||||
|
||||
// trustedPublicKeys are the keys a licence may be signed with, newest first.
|
||||
//
|
||||
// To rotate: prepend the new key, ship a server release that trusts both, then
|
||||
// reissue. Remove a retired key only once every licence signed with it has
|
||||
// expired.
|
||||
//
|
||||
// This is a slice from day one even though it holds one entry, because
|
||||
// retrofitting a single-key verifier into a multi-key one during an incident is
|
||||
// not a thing to plan for.
|
||||
//
|
||||
// These are compiled in and deliberately not configurable. A configurable trust
|
||||
// root is a licensing bypass: a self-hosted operator could point it at a keypair
|
||||
// they generated themselves.
|
||||
var trustedPublicKeys = []string{
|
||||
// Populated in Task 6 with the real production key.
|
||||
// Until then this slice is empty and every licence fails to verify,
|
||||
// which is the correct default for a build with no trust root.
|
||||
}
|
||||
|
||||
func publicKeys() ([]*lk.PublicKey, error) {
|
||||
out := make([]*lk.PublicKey, 0, len(trustedPublicKeys))
|
||||
for i, s := range trustedPublicKeys {
|
||||
k, err := lk.PublicKeyFromB32String(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("trusted public key %d is malformed: %w", i, err)
|
||||
}
|
||||
out = append(out, k)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//go:build !noSign
|
||||
|
||||
package license
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/hyperboloide/lk"
|
||||
)
|
||||
|
||||
// Sign marshals a licence and signs it, returning the base32 blob.
|
||||
//
|
||||
// This file carries the !noSign build tag so the signing path can be compiled
|
||||
// out of the control plane. The server has no reason to hold signing code and
|
||||
// no reason to ship it into a customer's data centre.
|
||||
//
|
||||
// privateKeyB32 comes from LICENSE_SIGNING_KEY on the issuing side only.
|
||||
func Sign(l License, privateKeyB32 string) (string, error) {
|
||||
if privateKeyB32 == "" {
|
||||
return "", fmt.Errorf("no signing key provided")
|
||||
}
|
||||
priv, err := lk.PrivateKeyFromB32String(privateKeyB32)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parse signing key: %w", err)
|
||||
}
|
||||
|
||||
data, err := json.Marshal(l)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("marshal licence: %w", err)
|
||||
}
|
||||
|
||||
signed, err := lk.NewLicense(priv, data)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("sign licence: %w", err)
|
||||
}
|
||||
|
||||
blob, err := signed.ToB32String()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("encode licence: %w", err)
|
||||
}
|
||||
return blob, nil
|
||||
}
|
||||
Reference in New Issue
Block a user