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 }