| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- package verify
- import (
- "encoding/json"
- "strings"
- "time"
- "github.com/DataDog/go-tuf/data"
- "github.com/DataDog/go-tuf/internal/roles"
- "github.com/secure-systems-lab/go-securesystemslib/cjson"
- )
- type signedMeta struct {
- Type string `json:"_type"`
- Expires time.Time `json:"expires"`
- Version int64 `json:"version"`
- }
- func (db *DB) VerifyIgnoreExpiredCheck(s *data.Signed, role string, minVersion int64) error {
- if err := db.VerifySignatures(s, role); err != nil {
- return err
- }
- sm := &signedMeta{}
- if err := json.Unmarshal(s.Signed, sm); err != nil {
- return err
- }
- if roles.IsTopLevelRole(role) {
- // Top-level roles can only sign metadata of the same type (e.g. snapshot
- // metadata must be signed by the snapshot role).
- if !strings.EqualFold(sm.Type, role) {
- return ErrWrongMetaType
- }
- } else {
- // Delegated (non-top-level) roles may only sign targets metadata.
- if strings.ToLower(sm.Type) != "targets" {
- return ErrWrongMetaType
- }
- }
- if sm.Version < minVersion {
- return ErrLowVersion{sm.Version, minVersion}
- }
- return nil
- }
- func (db *DB) Verify(s *data.Signed, role string, minVersion int64) error {
- // Verify signatures and versions
- err := db.VerifyIgnoreExpiredCheck(s, role, minVersion)
- if err != nil {
- return err
- }
- sm := &signedMeta{}
- if err := json.Unmarshal(s.Signed, sm); err != nil {
- return err
- }
- // Verify expiration
- if IsExpired(sm.Expires) {
- return ErrExpired{sm.Expires}
- }
- return nil
- }
- var IsExpired = func(t time.Time) bool {
- return time.Until(t) <= 0
- }
- func (db *DB) VerifySignatures(s *data.Signed, role string) error {
- if len(s.Signatures) == 0 {
- return ErrNoSignatures
- }
- roleData := db.GetRole(role)
- if roleData == nil {
- return ErrUnknownRole{role}
- }
- var decoded map[string]interface{}
- if err := json.Unmarshal(s.Signed, &decoded); err != nil {
- return err
- }
- msg, err := cjson.EncodeCanonical(decoded)
- if err != nil {
- return err
- }
- // Verify that a threshold of keys signed the data. Since keys can have
- // multiple key ids, we need to protect against multiple attached
- // signatures that just differ on the key id.
- seen := make(map[string]struct{})
- valid := 0
- for _, sig := range s.Signatures {
- if !roleData.ValidKey(sig.KeyID) {
- continue
- }
- verifier, err := db.GetVerifier(sig.KeyID)
- if err != nil {
- continue
- }
- if err := verifier.Verify(msg, sig.Signature); err != nil {
- return ErrInvalid
- }
- // Only consider this key valid if we haven't seen any of it's
- // key ids before.
- if _, ok := seen[sig.KeyID]; !ok {
- for _, id := range verifier.MarshalPublicKey().IDs() {
- seen[id] = struct{}{}
- }
- valid++
- }
- }
- if valid < roleData.Threshold {
- return ErrRoleThreshold{roleData.Threshold, valid}
- }
- return nil
- }
- func (db *DB) Unmarshal(b []byte, v interface{}, role string, minVersion int64) error {
- s := &data.Signed{}
- if err := json.Unmarshal(b, s); err != nil {
- return err
- }
- if err := db.Verify(s, role, minVersion); err != nil {
- return err
- }
- return json.Unmarshal(s.Signed, v)
- }
- // UnmarshalExpired is exactly like Unmarshal except ignores expired timestamp error.
- func (db *DB) UnmarshalIgnoreExpired(b []byte, v interface{}, role string, minVersion int64) error {
- s := &data.Signed{}
- if err := json.Unmarshal(b, s); err != nil {
- return err
- }
- // Note: If verification fails, then we wont attempt to unmarshal
- // unless when verification error is errExpired.
- verifyErr := db.Verify(s, role, minVersion)
- if verifyErr != nil {
- if _, ok := verifyErr.(ErrExpired); !ok {
- return verifyErr
- }
- }
- return json.Unmarshal(s.Signed, v)
- }
- func (db *DB) UnmarshalTrusted(b []byte, v interface{}, role string) error {
- s := &data.Signed{}
- if err := json.Unmarshal(b, s); err != nil {
- return err
- }
- if err := db.VerifySignatures(s, role); err != nil {
- return err
- }
- return json.Unmarshal(s.Signed, v)
- }
|