| package signed |
| |
| import ( |
| "encoding/json" |
| "errors" |
| "time" |
| |
| "github.com/agl/ed25519" |
| "github.com/flynn/go-tuf/data" |
| "github.com/flynn/go-tuf/keys" |
| "github.com/tent/canonical-json-go" |
| ) |
| |
| 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") |
| ErrRoleThreshold = errors.New("tuf: valid signatures did not meet threshold") |
| ErrWrongType = errors.New("tuf: meta file has wrong type") |
| ) |
| |
| type signedMeta struct { |
| Type string `json:"_type"` |
| Expires time.Time `json:"expires"` |
| Version int `json:"version"` |
| } |
| |
| func Verify(s *data.Signed, role string, minVersion int, db *keys.DB) error { |
| if err := VerifySignatures(s, role, db); err != nil { |
| return err |
| } |
| |
| sm := &signedMeta{} |
| if err := json.Unmarshal(s.Signed, sm); err != nil { |
| return err |
| } |
| if sm.Type != role { |
| return ErrWrongType |
| } |
| if IsExpired(sm.Expires) { |
| return ErrExpired{sm.Expires} |
| } |
| if sm.Version < minVersion { |
| return ErrLowVersion{sm.Version, minVersion} |
| } |
| |
| return nil |
| } |
| |
| var IsExpired = func(t time.Time) bool { |
| return t.Sub(time.Now()) <= 0 |
| } |
| |
| func VerifySignatures(s *data.Signed, role string, db *keys.DB) error { |
| if len(s.Signatures) == 0 { |
| return ErrNoSignatures |
| } |
| |
| roleData := db.GetRole(role) |
| if roleData == nil { |
| return ErrUnknownRole |
| } |
| |
| var decoded map[string]interface{} |
| if err := json.Unmarshal(s.Signed, &decoded); err != nil { |
| return err |
| } |
| msg, err := cjson.Marshal(decoded) |
| if err != nil { |
| return err |
| } |
| |
| valid := make(map[string]struct{}) |
| var sigBytes [ed25519.SignatureSize]byte |
| for _, sig := range s.Signatures { |
| if sig.Method != "ed25519" { |
| return ErrWrongMethod |
| } |
| if len(sig.Signature) != len(sigBytes) { |
| return ErrInvalid |
| } |
| |
| if !roleData.ValidKey(sig.KeyID) { |
| continue |
| } |
| key := db.GetKey(sig.KeyID) |
| if key == nil { |
| continue |
| } |
| |
| copy(sigBytes[:], sig.Signature) |
| if !ed25519.Verify(&key.Public, msg, &sigBytes) { |
| return ErrInvalid |
| } |
| valid[sig.KeyID] = struct{}{} |
| } |
| if len(valid) < roleData.Threshold { |
| return ErrRoleThreshold |
| } |
| |
| return nil |
| } |
| |
| func Unmarshal(b []byte, v interface{}, role string, minVersion int, db *keys.DB) 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) |
| } |
| |
| func UnmarshalTrusted(b []byte, v interface{}, role string, db *keys.DB) 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) |
| } |