2017-04-17 18:08:24 -04:00
|
|
|
package signed
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2017-08-07 05:52:40 -04:00
|
|
|
"github.com/sirupsen/logrus"
|
2017-04-17 18:08:24 -04:00
|
|
|
"github.com/docker/go/canonical/json"
|
|
|
|
"github.com/docker/notary/tuf/data"
|
|
|
|
)
|
|
|
|
|
|
|
|
// 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")
|
|
|
|
)
|
|
|
|
|
|
|
|
// IsExpired checks if the given time passed before the present time
|
|
|
|
func IsExpired(t time.Time) bool {
|
|
|
|
return t.Before(time.Now())
|
|
|
|
}
|
|
|
|
|
|
|
|
// VerifyExpiry returns ErrExpired if the metadata is expired
|
|
|
|
func VerifyExpiry(s *data.SignedCommon, role string) error {
|
|
|
|
if IsExpired(s.Expires) {
|
|
|
|
logrus.Errorf("Metadata for %s expired", role)
|
|
|
|
return ErrExpired{Role: role, Expired: s.Expires.Format("Mon Jan 2 15:04:05 MST 2006")}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// VerifyVersion returns ErrLowVersion if the metadata version is lower than the min version
|
|
|
|
func VerifyVersion(s *data.SignedCommon, minVersion int) error {
|
|
|
|
if s.Version < minVersion {
|
|
|
|
return ErrLowVersion{Actual: s.Version, Current: minVersion}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// VerifySignatures checks the we have sufficient valid signatures for the given role
|
|
|
|
func VerifySignatures(s *data.Signed, roleData data.BaseRole) error {
|
|
|
|
if len(s.Signatures) == 0 {
|
|
|
|
return ErrNoSignatures
|
|
|
|
}
|
|
|
|
|
|
|
|
if roleData.Threshold < 1 {
|
|
|
|
return ErrRoleThreshold{}
|
|
|
|
}
|
|
|
|
logrus.Debugf("%s role has key IDs: %s", roleData.Name, strings.Join(roleData.ListKeyIDs(), ","))
|
|
|
|
|
|
|
|
// remarshal the signed part so we can verify the signature, since the signature has
|
|
|
|
// to be of a canonically marshalled signed object
|
|
|
|
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 i := range s.Signatures {
|
|
|
|
sig := &(s.Signatures[i])
|
|
|
|
logrus.Debug("verifying signature for key ID: ", sig.KeyID)
|
|
|
|
key, ok := roleData.Keys[sig.KeyID]
|
|
|
|
if !ok {
|
|
|
|
logrus.Debugf("continuing b/c keyid lookup was nil: %s\n", sig.KeyID)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Check that the signature key ID actually matches the content ID of the key
|
|
|
|
if key.ID() != sig.KeyID {
|
|
|
|
return ErrInvalidKeyID{}
|
|
|
|
}
|
|
|
|
if err := VerifySignature(msg, sig, key); err != nil {
|
|
|
|
logrus.Debugf("continuing b/c %s", err.Error())
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
valid[sig.KeyID] = struct{}{}
|
|
|
|
}
|
|
|
|
if len(valid) < roleData.Threshold {
|
|
|
|
return ErrRoleThreshold{
|
|
|
|
Msg: fmt.Sprintf("valid signatures did not meet threshold for %s", roleData.Name),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// VerifySignature checks a single signature and public key against a payload
|
|
|
|
// If the signature is verified, the signature's is valid field will actually
|
|
|
|
// be mutated to be equal to the boolean true
|
|
|
|
func VerifySignature(msg []byte, sig *data.Signature, pk data.PublicKey) error {
|
|
|
|
// method lookup is consistent due to Unmarshal JSON doing lower case for us.
|
|
|
|
method := sig.Method
|
|
|
|
verifier, ok := Verifiers[method]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("signing method is not supported: %s\n", sig.Method)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := verifier.Verify(pk, sig.Signature, msg); err != nil {
|
|
|
|
return fmt.Errorf("signature was invalid\n")
|
|
|
|
}
|
|
|
|
sig.IsValid = true
|
|
|
|
return nil
|
|
|
|
}
|