55 lines
1.1 KiB
Go
55 lines
1.1 KiB
Go
|
package changeset
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
|
||
|
"golang.org/x/crypto/ssh"
|
||
|
)
|
||
|
|
||
|
// Metadata is the metadata for the changeset.
|
||
|
type Metadata struct {
|
||
|
// The name of the service
|
||
|
Name string
|
||
|
// The git hash of the repo checkout that made the service
|
||
|
Version string
|
||
|
// Slug SHA256 hash, this is what is signed
|
||
|
Hash []byte
|
||
|
// The SSH signatures for this ChangeSet
|
||
|
Signatures []*ssh.Signature
|
||
|
}
|
||
|
|
||
|
type ChangeSet struct {
|
||
|
Metadata Metadata
|
||
|
SlugFile string
|
||
|
}
|
||
|
|
||
|
func (cs ChangeSet) Validate(trustedKeys []ssh.PublicKey, minNeeded int) error {
|
||
|
var gotKeys = map[string]struct{}{}
|
||
|
for _, sig := range cs.Metadata.Signatures {
|
||
|
for _, pubkey := range trustedKeys {
|
||
|
fp := ssh.FingerprintSHA256(pubkey)
|
||
|
|
||
|
if _, got := gotKeys[fp]; got {
|
||
|
return fmt.Errorf("changeset: %v signed this changeset more than once", fp)
|
||
|
}
|
||
|
|
||
|
if fp != string(sig.Rest) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
err := pubkey.Verify(cs.Metadata.Hash, sig)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
gotKeys[fp] = struct{}{}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if gk := len(gotKeys); gk != minNeeded {
|
||
|
return fmt.Errorf("wanted %d keys, only signed with %d keys", minNeeded, gk)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|