...

Source file src/github.com/theupdateframework/go-tuf/data/types.go

Documentation: github.com/theupdateframework/go-tuf/data

     1  package data
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha256"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"path"
    11  	"strings"
    12  	"sync"
    13  	"time"
    14  
    15  	"github.com/secure-systems-lab/go-securesystemslib/cjson"
    16  )
    17  
    18  type KeyType string
    19  
    20  type KeyScheme string
    21  
    22  type HashAlgorithm string
    23  
    24  const (
    25  	KeyIDLength = sha256.Size * 2
    26  
    27  	KeyTypeEd25519 KeyType = "ed25519"
    28  	// From version 1.0.32, the reference implementation defines 'ecdsa',
    29  	// not 'ecdsa-sha2-nistp256' for NIST P-256 curves.
    30  	KeyTypeECDSA_SHA2_P256         KeyType = "ecdsa"
    31  	KeyTypeECDSA_SHA2_P256_OLD_FMT KeyType = "ecdsa-sha2-nistp256"
    32  	KeyTypeRSASSA_PSS_SHA256       KeyType = "rsa"
    33  
    34  	KeySchemeEd25519           KeyScheme = "ed25519"
    35  	KeySchemeECDSA_SHA2_P256   KeyScheme = "ecdsa-sha2-nistp256"
    36  	KeySchemeRSASSA_PSS_SHA256 KeyScheme = "rsassa-pss-sha256"
    37  
    38  	HashAlgorithmSHA256 HashAlgorithm = "sha256"
    39  	HashAlgorithmSHA512 HashAlgorithm = "sha512"
    40  )
    41  
    42  var (
    43  	HashAlgorithms           = []HashAlgorithm{HashAlgorithmSHA256, HashAlgorithmSHA512}
    44  	ErrPathsAndPathHashesSet = errors.New("tuf: failed validation of delegated target: paths and path_hash_prefixes are both set")
    45  )
    46  
    47  type Signed struct {
    48  	Signed     json.RawMessage `json:"signed"`
    49  	Signatures []Signature     `json:"signatures"`
    50  }
    51  
    52  type Signature struct {
    53  	KeyID     string   `json:"keyid"`
    54  	Signature HexBytes `json:"sig"`
    55  }
    56  
    57  type PublicKey struct {
    58  	Type       KeyType         `json:"keytype"`
    59  	Scheme     KeyScheme       `json:"scheme"`
    60  	Algorithms []HashAlgorithm `json:"keyid_hash_algorithms,omitempty"`
    61  	Value      json.RawMessage `json:"keyval"`
    62  
    63  	ids    []string
    64  	idOnce sync.Once
    65  }
    66  
    67  type PrivateKey struct {
    68  	Type       KeyType         `json:"keytype"`
    69  	Scheme     KeyScheme       `json:"scheme,omitempty"`
    70  	Algorithms []HashAlgorithm `json:"keyid_hash_algorithms,omitempty"`
    71  	Value      json.RawMessage `json:"keyval"`
    72  }
    73  
    74  func (k *PublicKey) IDs() []string {
    75  	k.idOnce.Do(func() {
    76  		data, err := cjson.EncodeCanonical(k)
    77  		if err != nil {
    78  			panic(fmt.Errorf("tuf: error creating key ID: %w", err))
    79  		}
    80  		digest := sha256.Sum256(data)
    81  		k.ids = []string{hex.EncodeToString(digest[:])}
    82  	})
    83  	return k.ids
    84  }
    85  
    86  func (k *PublicKey) ContainsID(id string) bool {
    87  	for _, keyid := range k.IDs() {
    88  		if id == keyid {
    89  			return true
    90  		}
    91  	}
    92  	return false
    93  }
    94  
    95  func DefaultExpires(role string) time.Time {
    96  	var t time.Time
    97  	switch role {
    98  	case "root":
    99  		t = time.Now().AddDate(1, 0, 0)
   100  	case "snapshot":
   101  		t = time.Now().AddDate(0, 0, 7)
   102  	case "timestamp":
   103  		t = time.Now().AddDate(0, 0, 1)
   104  	default:
   105  		// targets and delegated targets
   106  		t = time.Now().AddDate(0, 3, 0)
   107  	}
   108  	return t.UTC().Round(time.Second)
   109  }
   110  
   111  type Root struct {
   112  	Type        string                `json:"_type"`
   113  	SpecVersion string                `json:"spec_version"`
   114  	Version     int64                 `json:"version"`
   115  	Expires     time.Time             `json:"expires"`
   116  	Keys        map[string]*PublicKey `json:"keys"`
   117  	Roles       map[string]*Role      `json:"roles"`
   118  	Custom      *json.RawMessage      `json:"custom,omitempty"`
   119  
   120  	ConsistentSnapshot bool `json:"consistent_snapshot"`
   121  }
   122  
   123  func NewRoot() *Root {
   124  	return &Root{
   125  		Type:               "root",
   126  		SpecVersion:        "1.0",
   127  		Expires:            DefaultExpires("root"),
   128  		Keys:               make(map[string]*PublicKey),
   129  		Roles:              make(map[string]*Role),
   130  		ConsistentSnapshot: true,
   131  	}
   132  }
   133  
   134  func (r *Root) AddKey(key *PublicKey) bool {
   135  	changed := false
   136  	for _, id := range key.IDs() {
   137  		if _, ok := r.Keys[id]; !ok {
   138  			changed = true
   139  			r.Keys[id] = key
   140  		}
   141  	}
   142  	return changed
   143  }
   144  
   145  type Role struct {
   146  	KeyIDs    []string `json:"keyids"`
   147  	Threshold int      `json:"threshold"`
   148  }
   149  
   150  func (r *Role) AddKeyIDs(ids []string) bool {
   151  	roleIDs := make(map[string]struct{})
   152  	for _, id := range r.KeyIDs {
   153  		roleIDs[id] = struct{}{}
   154  	}
   155  	changed := false
   156  	for _, id := range ids {
   157  		if _, ok := roleIDs[id]; !ok {
   158  			changed = true
   159  			r.KeyIDs = append(r.KeyIDs, id)
   160  		}
   161  	}
   162  	return changed
   163  }
   164  
   165  type Files map[string]TargetFileMeta
   166  
   167  type Hashes map[string]HexBytes
   168  
   169  func (f Hashes) HashAlgorithms() []string {
   170  	funcs := make([]string, 0, len(f))
   171  	for name := range f {
   172  		funcs = append(funcs, name)
   173  	}
   174  	return funcs
   175  }
   176  
   177  type metapathFileMeta struct {
   178  	Length  int64            `json:"length,omitempty"`
   179  	Hashes  Hashes           `json:"hashes,omitempty"`
   180  	Version int64            `json:"version"`
   181  	Custom  *json.RawMessage `json:"custom,omitempty"`
   182  }
   183  
   184  // SnapshotFileMeta is the meta field of a snapshot
   185  // Note: Contains a `custom` field
   186  type SnapshotFileMeta metapathFileMeta
   187  
   188  type SnapshotFiles map[string]SnapshotFileMeta
   189  
   190  type Snapshot struct {
   191  	Type        string           `json:"_type"`
   192  	SpecVersion string           `json:"spec_version"`
   193  	Version     int64            `json:"version"`
   194  	Expires     time.Time        `json:"expires"`
   195  	Meta        SnapshotFiles    `json:"meta"`
   196  	Custom      *json.RawMessage `json:"custom,omitempty"`
   197  }
   198  
   199  func NewSnapshot() *Snapshot {
   200  	return &Snapshot{
   201  		Type:        "snapshot",
   202  		SpecVersion: "1.0",
   203  		Expires:     DefaultExpires("snapshot"),
   204  		Meta:        make(SnapshotFiles),
   205  	}
   206  }
   207  
   208  type FileMeta struct {
   209  	Length int64  `json:"length"`
   210  	Hashes Hashes `json:"hashes"`
   211  }
   212  
   213  type TargetFiles map[string]TargetFileMeta
   214  
   215  type TargetFileMeta struct {
   216  	FileMeta
   217  	Custom *json.RawMessage `json:"custom,omitempty"`
   218  }
   219  
   220  func (f TargetFileMeta) HashAlgorithms() []string {
   221  	return f.FileMeta.Hashes.HashAlgorithms()
   222  }
   223  
   224  type Targets struct {
   225  	Type        string           `json:"_type"`
   226  	SpecVersion string           `json:"spec_version"`
   227  	Version     int64            `json:"version"`
   228  	Expires     time.Time        `json:"expires"`
   229  	Targets     TargetFiles      `json:"targets"`
   230  	Delegations *Delegations     `json:"delegations,omitempty"`
   231  	Custom      *json.RawMessage `json:"custom,omitempty"`
   232  }
   233  
   234  // Delegations represents the edges from a parent Targets role to one or more
   235  // delegated target roles. See spec v1.0.19 section 4.5.
   236  type Delegations struct {
   237  	Keys  map[string]*PublicKey `json:"keys"`
   238  	Roles []DelegatedRole       `json:"roles"`
   239  }
   240  
   241  // DelegatedRole describes a delegated role, including what paths it is
   242  // reponsible for. See spec v1.0.19 section 4.5.
   243  type DelegatedRole struct {
   244  	Name             string   `json:"name"`
   245  	KeyIDs           []string `json:"keyids"`
   246  	Threshold        int      `json:"threshold"`
   247  	Terminating      bool     `json:"terminating"`
   248  	PathHashPrefixes []string `json:"path_hash_prefixes,omitempty"`
   249  	Paths            []string `json:"paths"`
   250  }
   251  
   252  // MatchesPath evaluates whether the path patterns or path hash prefixes match
   253  // a given file. This determines whether a delegated role is responsible for
   254  // signing and verifying the file.
   255  func (d *DelegatedRole) MatchesPath(file string) (bool, error) {
   256  	if err := d.validatePaths(); err != nil {
   257  		return false, err
   258  	}
   259  
   260  	for _, pattern := range d.Paths {
   261  		if matched, _ := path.Match(pattern, file); matched {
   262  			return true, nil
   263  		}
   264  	}
   265  
   266  	pathHash := PathHexDigest(file)
   267  	for _, hashPrefix := range d.PathHashPrefixes {
   268  		if strings.HasPrefix(pathHash, hashPrefix) {
   269  			return true, nil
   270  		}
   271  	}
   272  
   273  	return false, nil
   274  }
   275  
   276  // validatePaths enforces the spec
   277  // https://theupdateframework.github.io/specification/v1.0.19/index.html#file-formats-targets
   278  // 'role MUST specify only one of the "path_hash_prefixes" or "paths"'
   279  // Marshalling and unmarshalling JSON will fail and return
   280  // ErrPathsAndPathHashesSet if both fields are set and not empty.
   281  func (d *DelegatedRole) validatePaths() error {
   282  	if len(d.PathHashPrefixes) > 0 && len(d.Paths) > 0 {
   283  		return ErrPathsAndPathHashesSet
   284  	}
   285  
   286  	return nil
   287  }
   288  
   289  // MarshalJSON is called when writing the struct to JSON. We validate prior to
   290  // marshalling to ensure that an invalid delegated role can not be serialized
   291  // to JSON.
   292  func (d *DelegatedRole) MarshalJSON() ([]byte, error) {
   293  	type delegatedRoleAlias DelegatedRole
   294  
   295  	if err := d.validatePaths(); err != nil {
   296  		return nil, err
   297  	}
   298  
   299  	return json.Marshal((*delegatedRoleAlias)(d))
   300  }
   301  
   302  // UnmarshalJSON is called when reading the struct from JSON. We validate once
   303  // unmarshalled to ensure that an error is thrown if an invalid delegated role
   304  // is read.
   305  func (d *DelegatedRole) UnmarshalJSON(b []byte) error {
   306  	type delegatedRoleAlias DelegatedRole
   307  
   308  	// Prepare decoder
   309  	dec := json.NewDecoder(bytes.NewReader(b))
   310  
   311  	// Unmarshal delegated role
   312  	if err := dec.Decode((*delegatedRoleAlias)(d)); err != nil {
   313  		return err
   314  	}
   315  
   316  	return d.validatePaths()
   317  }
   318  
   319  func NewTargets() *Targets {
   320  	return &Targets{
   321  		Type:        "targets",
   322  		SpecVersion: "1.0",
   323  		Expires:     DefaultExpires("targets"),
   324  		Targets:     make(TargetFiles),
   325  	}
   326  }
   327  
   328  type TimestampFileMeta metapathFileMeta
   329  
   330  type TimestampFiles map[string]TimestampFileMeta
   331  
   332  type Timestamp struct {
   333  	Type        string           `json:"_type"`
   334  	SpecVersion string           `json:"spec_version"`
   335  	Version     int64            `json:"version"`
   336  	Expires     time.Time        `json:"expires"`
   337  	Meta        TimestampFiles   `json:"meta"`
   338  	Custom      *json.RawMessage `json:"custom,omitempty"`
   339  }
   340  
   341  func NewTimestamp() *Timestamp {
   342  	return &Timestamp{
   343  		Type:        "timestamp",
   344  		SpecVersion: "1.0",
   345  		Expires:     DefaultExpires("timestamp"),
   346  		Meta:        make(TimestampFiles),
   347  	}
   348  }
   349  

View as plain text