mirror of https://github.com/docker/docs.git
181 lines
4.9 KiB
Go
181 lines
4.9 KiB
Go
package signed
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/docker/notary/tuf/data"
|
|
"github.com/docker/notary/tuf/keys"
|
|
"github.com/jfrazelle/go/canonical/json"
|
|
)
|
|
|
|
// Various basic signing errors
|
|
var (
|
|
ErrMissingKey = errors.New("tuf: missing key")
|
|
ErrNoSignatures = errors.New("tuf: data has no signatures")
|
|
ErrInvalid = errors.New("tuf: signature verification failed")
|
|
ErrWrongMethod = errors.New("tuf: invalid signature type")
|
|
ErrUnknownRole = errors.New("tuf: unknown role")
|
|
ErrWrongType = errors.New("tuf: meta file has wrong type")
|
|
)
|
|
|
|
// VerifyRoot checks if a given root file is valid against a known set of keys.
|
|
// Threshold is always assumed to be 1
|
|
func VerifyRoot(s *data.Signed, minVersion int, keys map[string]data.PublicKey) error {
|
|
if len(s.Signatures) == 0 {
|
|
return ErrNoSignatures
|
|
}
|
|
|
|
var decoded map[string]interface{}
|
|
if err := json.Unmarshal(s.Signed, &decoded); err != nil {
|
|
return err
|
|
}
|
|
msg, err := json.MarshalCanonical(decoded)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, sig := range s.Signatures {
|
|
// method lookup is consistent due to Unmarshal JSON doing lower case for us.
|
|
method := sig.Method
|
|
verifier, ok := Verifiers[method]
|
|
if !ok {
|
|
logrus.Debugf("continuing b/c signing method is not supported for verify root: %s\n", sig.Method)
|
|
continue
|
|
}
|
|
|
|
key, ok := keys[sig.KeyID]
|
|
if !ok {
|
|
logrus.Debugf("continuing b/c signing key isn't present in keys: %s\n", sig.KeyID)
|
|
continue
|
|
}
|
|
|
|
if err := verifier.Verify(key, sig.Signature, msg); err != nil {
|
|
logrus.Debugf("continuing b/c signature was invalid\n")
|
|
continue
|
|
}
|
|
// threshold of 1 so return on first success
|
|
return verifyMeta(s, "root", minVersion)
|
|
}
|
|
return ErrRoleThreshold{}
|
|
}
|
|
|
|
// Verify checks the signatures and metadata (expiry, version) for the signed role
|
|
// data
|
|
func Verify(s *data.Signed, role string, minVersion int, db *keys.KeyDB) error {
|
|
if err := VerifySignatures(s, role, db); err != nil {
|
|
return err
|
|
}
|
|
return verifyMeta(s, role, minVersion)
|
|
}
|
|
|
|
func verifyMeta(s *data.Signed, role string, minVersion int) error {
|
|
sm := &data.SignedCommon{}
|
|
if err := json.Unmarshal(s.Signed, sm); err != nil {
|
|
return err
|
|
}
|
|
if !data.ValidTUFType(sm.Type, role) {
|
|
return ErrWrongType
|
|
}
|
|
if IsExpired(sm.Expires) {
|
|
logrus.Errorf("Metadata for %s expired", role)
|
|
return ErrExpired{Role: role, Expired: sm.Expires.Format("Mon Jan 2 15:04:05 MST 2006")}
|
|
}
|
|
if sm.Version < minVersion {
|
|
return ErrLowVersion{sm.Version, minVersion}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsExpired checks if the given time passed before the present time
|
|
func IsExpired(t time.Time) bool {
|
|
return t.Before(time.Now())
|
|
}
|
|
|
|
// VerifySignatures checks the we have sufficient valid signatures for the given role
|
|
func VerifySignatures(s *data.Signed, role string, db *keys.KeyDB) error {
|
|
if len(s.Signatures) == 0 {
|
|
return ErrNoSignatures
|
|
}
|
|
|
|
roleData := db.GetRole(role)
|
|
if roleData == nil {
|
|
return ErrUnknownRole
|
|
}
|
|
|
|
if roleData.Threshold < 1 {
|
|
return ErrRoleThreshold{}
|
|
}
|
|
logrus.Debugf("%s role has key IDs: %s", role, strings.Join(roleData.KeyIDs, ","))
|
|
|
|
var decoded map[string]interface{}
|
|
if err := json.Unmarshal(s.Signed, &decoded); err != nil {
|
|
return err
|
|
}
|
|
msg, err := json.MarshalCanonical(decoded)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
valid := make(map[string]struct{})
|
|
for _, sig := range s.Signatures {
|
|
logrus.Debug("verifying signature for key ID: ", sig.KeyID)
|
|
if !roleData.ValidKey(sig.KeyID) {
|
|
logrus.Debugf("continuing b/c keyid was invalid: %s for roledata %s\n", sig.KeyID, roleData)
|
|
continue
|
|
}
|
|
key := db.GetKey(sig.KeyID)
|
|
if key == nil {
|
|
logrus.Debugf("continuing b/c keyid lookup was nil: %s\n", sig.KeyID)
|
|
continue
|
|
}
|
|
// method lookup is consistent due to Unmarshal JSON doing lower case for us.
|
|
method := sig.Method
|
|
verifier, ok := Verifiers[method]
|
|
if !ok {
|
|
logrus.Debugf("continuing b/c signing method is not supported: %s\n", sig.Method)
|
|
continue
|
|
}
|
|
|
|
if err := verifier.Verify(key, sig.Signature, msg); err != nil {
|
|
logrus.Debugf("continuing b/c signature was invalid\n")
|
|
continue
|
|
}
|
|
valid[sig.KeyID] = struct{}{}
|
|
|
|
}
|
|
if len(valid) < roleData.Threshold {
|
|
return ErrRoleThreshold{}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Unmarshal unmarshals and verifys the raw bytes for a given role's metadata
|
|
func Unmarshal(b []byte, v interface{}, role string, minVersion int, db *keys.KeyDB) error {
|
|
s := &data.Signed{}
|
|
if err := json.Unmarshal(b, s); err != nil {
|
|
return err
|
|
}
|
|
if err := Verify(s, role, minVersion, db); err != nil {
|
|
return err
|
|
}
|
|
return json.Unmarshal(s.Signed, v)
|
|
}
|
|
|
|
// UnmarshalTrusted unmarshals and verifies signatures only, not metadata, for a
|
|
// given role's metadata
|
|
func UnmarshalTrusted(b []byte, v interface{}, role string, db *keys.KeyDB) error {
|
|
s := &data.Signed{}
|
|
if err := json.Unmarshal(b, s); err != nil {
|
|
return err
|
|
}
|
|
if err := VerifySignatures(s, role, db); err != nil {
|
|
return err
|
|
}
|
|
return json.Unmarshal(s.Signed, v)
|
|
}
|