docs/tuf/data/types.go

216 lines
5.6 KiB
Go

package data
import (
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash"
"io"
"io/ioutil"
"strings"
"time"
"github.com/Sirupsen/logrus"
"github.com/jfrazelle/go/canonical/json"
)
// SigAlgorithm for types of signatures
type SigAlgorithm string
func (k SigAlgorithm) String() string {
return string(k)
}
const defaultHashAlgorithm = "sha256"
// Signature types
const (
EDDSASignature SigAlgorithm = "eddsa"
RSAPSSSignature SigAlgorithm = "rsapss"
RSAPKCS1v15Signature SigAlgorithm = "rsapkcs1v15"
ECDSASignature SigAlgorithm = "ecdsa"
PyCryptoSignature SigAlgorithm = "pycrypto-pkcs#1 pss"
)
// Key types
const (
ED25519Key = "ed25519"
RSAKey = "rsa"
RSAx509Key = "rsa-x509"
ECDSAKey = "ecdsa"
ECDSAx509Key = "ecdsa-x509"
)
// TUFTypes is the set of metadata types
var TUFTypes = map[string]string{
CanonicalRootRole: "Root",
CanonicalTargetsRole: "Targets",
CanonicalSnapshotRole: "Snapshot",
CanonicalTimestampRole: "Timestamp",
}
// SetTUFTypes allows one to override some or all of the default
// type names in TUF.
func SetTUFTypes(ts map[string]string) {
for k, v := range ts {
TUFTypes[k] = v
}
}
// ValidTUFType checks if the given type is valid for the role
func ValidTUFType(typ, role string) bool {
if ValidRole(role) {
// All targets delegation roles must have
// the valid type is for targets.
role = CanonicalRole(role)
if role == "" {
// role is unknown and does not map to
// a type
return false
}
if strings.HasPrefix(role, CanonicalTargetsRole+"/") {
role = CanonicalTargetsRole
}
}
// most people will just use the defaults so have this optimal check
// first. Do comparison just in case there is some unknown vulnerability
// if a key and value in the map differ.
if v, ok := TUFTypes[role]; ok {
return typ == v
}
return false
}
// Signed is the high level, partially deserialized metadata object
// used to verify signatures before fully unpacking, or to add signatures
// before fully packing
type Signed struct {
Signed json.RawMessage `json:"signed"`
Signatures []Signature `json:"signatures"`
}
// SignedCommon contains the fields common to the Signed component of all
// TUF metadata files
type SignedCommon struct {
Type string `json:"_type"`
Expires time.Time `json:"expires"`
Version int `json:"version"`
}
// SignedMeta is used in server validation where we only need signatures
// and common fields
type SignedMeta struct {
Signed SignedCommon `json:"signed"`
Signatures []Signature `json:"signatures"`
}
// Signature is a signature on a piece of metadata
type Signature struct {
KeyID string `json:"keyid"`
Method SigAlgorithm `json:"method"`
Signature []byte `json:"sig"`
}
// Files is the map of paths to file meta container in targets and delegations
// metadata files
type Files map[string]FileMeta
// Hashes is the map of hash type to digest created for each metadata
// and target file
type Hashes map[string][]byte
// FileMeta contains the size and hashes for a metadata or target file. Custom
// data can be optionally added.
type FileMeta struct {
Length int64 `json:"length"`
Hashes Hashes `json:"hashes"`
Custom json.RawMessage `json:"custom,omitempty"`
}
// NewFileMeta generates a FileMeta object from the reader, using the
// hash algorithms provided
func NewFileMeta(r io.Reader, hashAlgorithms ...string) (FileMeta, error) {
if len(hashAlgorithms) == 0 {
hashAlgorithms = []string{defaultHashAlgorithm}
}
hashes := make(map[string]hash.Hash, len(hashAlgorithms))
for _, hashAlgorithm := range hashAlgorithms {
var h hash.Hash
switch hashAlgorithm {
case "sha256":
h = sha256.New()
case "sha512":
h = sha512.New()
default:
return FileMeta{}, fmt.Errorf("Unknown Hash Algorithm: %s", hashAlgorithm)
}
hashes[hashAlgorithm] = h
r = io.TeeReader(r, h)
}
n, err := io.Copy(ioutil.Discard, r)
if err != nil {
return FileMeta{}, err
}
m := FileMeta{Length: n, Hashes: make(Hashes, len(hashes))}
for hashAlgorithm, h := range hashes {
m.Hashes[hashAlgorithm] = h.Sum(nil)
}
return m, nil
}
// Delegations holds a tier of targets delegations
type Delegations struct {
Keys Keys `json:"keys"`
Roles []*Role `json:"roles"`
}
// NewDelegations initializes an empty Delegations object
func NewDelegations() *Delegations {
return &Delegations{
Keys: make(map[string]PublicKey),
Roles: make([]*Role, 0),
}
}
// defines number of days in which something should expire
var defaultExpiryTimes = map[string]int{
CanonicalRootRole: 365,
CanonicalTargetsRole: 90,
CanonicalSnapshotRole: 7,
CanonicalTimestampRole: 1,
}
// SetDefaultExpiryTimes allows one to change the default expiries.
func SetDefaultExpiryTimes(times map[string]int) {
for key, value := range times {
if _, ok := defaultExpiryTimes[key]; !ok {
logrus.Errorf("Attempted to set default expiry for an unknown role: %s", key)
continue
}
defaultExpiryTimes[key] = value
}
}
// DefaultExpires gets the default expiry time for the given role
func DefaultExpires(role string) time.Time {
var t time.Time
if t, ok := defaultExpiryTimes[role]; ok {
return time.Now().AddDate(0, 0, t)
}
return t.UTC().Round(time.Second)
}
type unmarshalledSignature Signature
// UnmarshalJSON does a custom unmarshalling of the signature JSON
func (s *Signature) UnmarshalJSON(data []byte) error {
uSignature := unmarshalledSignature{}
err := json.Unmarshal(data, &uSignature)
if err != nil {
return err
}
uSignature.Method = SigAlgorithm(strings.ToLower(string(uSignature.Method)))
*s = Signature(uSignature)
return nil
}