forked from toolshed/abra
chore: vendor
This commit is contained in:
53
vendor/github.com/theupdateframework/notary/tuf/data/errors.go
generated
vendored
Normal file
53
vendor/github.com/theupdateframework/notary/tuf/data/errors.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
package data
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ErrInvalidMetadata is the error to be returned when metadata is invalid
|
||||
type ErrInvalidMetadata struct {
|
||||
role RoleName
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e ErrInvalidMetadata) Error() string {
|
||||
return fmt.Sprintf("%s type metadata invalid: %s", e.role.String(), e.msg)
|
||||
}
|
||||
|
||||
// ErrMissingMeta - couldn't find the FileMeta object for the given Role, or
|
||||
// the FileMeta object contained no supported checksums
|
||||
type ErrMissingMeta struct {
|
||||
Role string
|
||||
}
|
||||
|
||||
func (e ErrMissingMeta) Error() string {
|
||||
return fmt.Sprintf("no checksums for supported algorithms were provided for %s", e.Role)
|
||||
}
|
||||
|
||||
// ErrInvalidChecksum is the error to be returned when checksum is invalid
|
||||
type ErrInvalidChecksum struct {
|
||||
alg string
|
||||
}
|
||||
|
||||
func (e ErrInvalidChecksum) Error() string {
|
||||
return fmt.Sprintf("%s checksum invalid", e.alg)
|
||||
}
|
||||
|
||||
// ErrMismatchedChecksum is the error to be returned when checksum is mismatched
|
||||
type ErrMismatchedChecksum struct {
|
||||
alg string
|
||||
name string
|
||||
expected string
|
||||
}
|
||||
|
||||
func (e ErrMismatchedChecksum) Error() string {
|
||||
return fmt.Sprintf("%s checksum for %s did not match: expected %s", e.alg, e.name,
|
||||
e.expected)
|
||||
}
|
||||
|
||||
// ErrCertExpired is the error to be returned when a certificate has expired
|
||||
type ErrCertExpired struct {
|
||||
CN string
|
||||
}
|
||||
|
||||
func (e ErrCertExpired) Error() string {
|
||||
return fmt.Sprintf("certificate with CN %s is expired", e.CN)
|
||||
}
|
529
vendor/github.com/theupdateframework/notary/tuf/data/keys.go
generated
vendored
Normal file
529
vendor/github.com/theupdateframework/notary/tuf/data/keys.go
generated
vendored
Normal file
@ -0,0 +1,529 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/docker/go/canonical/json"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
// PublicKey is the necessary interface for public keys
|
||||
type PublicKey interface {
|
||||
ID() string
|
||||
Algorithm() string
|
||||
Public() []byte
|
||||
}
|
||||
|
||||
// PrivateKey adds the ability to access the private key
|
||||
type PrivateKey interface {
|
||||
PublicKey
|
||||
Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error)
|
||||
Private() []byte
|
||||
CryptoSigner() crypto.Signer
|
||||
SignatureAlgorithm() SigAlgorithm
|
||||
}
|
||||
|
||||
// KeyPair holds the public and private key bytes
|
||||
type KeyPair struct {
|
||||
Public []byte `json:"public"`
|
||||
Private []byte `json:"private"`
|
||||
}
|
||||
|
||||
// Keys represents a map of key ID to PublicKey object. It's necessary
|
||||
// to allow us to unmarshal into an interface via the json.Unmarshaller
|
||||
// interface
|
||||
type Keys map[string]PublicKey
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaller interface
|
||||
func (ks *Keys) UnmarshalJSON(data []byte) error {
|
||||
parsed := make(map[string]TUFKey)
|
||||
err := json.Unmarshal(data, &parsed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
final := make(map[string]PublicKey)
|
||||
for k, tk := range parsed {
|
||||
final[k] = typedPublicKey(tk)
|
||||
}
|
||||
*ks = final
|
||||
return nil
|
||||
}
|
||||
|
||||
// KeyList represents a list of keys
|
||||
type KeyList []PublicKey
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaller interface
|
||||
func (ks *KeyList) UnmarshalJSON(data []byte) error {
|
||||
parsed := make([]TUFKey, 0, 1)
|
||||
err := json.Unmarshal(data, &parsed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
final := make([]PublicKey, 0, len(parsed))
|
||||
for _, tk := range parsed {
|
||||
final = append(final, typedPublicKey(tk))
|
||||
}
|
||||
*ks = final
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDs generates a list of the hex encoded key IDs in the KeyList
|
||||
func (ks KeyList) IDs() []string {
|
||||
keyIDs := make([]string, 0, len(ks))
|
||||
for _, k := range ks {
|
||||
keyIDs = append(keyIDs, k.ID())
|
||||
}
|
||||
return keyIDs
|
||||
}
|
||||
|
||||
func typedPublicKey(tk TUFKey) PublicKey {
|
||||
switch tk.Algorithm() {
|
||||
case ECDSAKey:
|
||||
return &ECDSAPublicKey{TUFKey: tk}
|
||||
case ECDSAx509Key:
|
||||
return &ECDSAx509PublicKey{TUFKey: tk}
|
||||
case RSAKey:
|
||||
return &RSAPublicKey{TUFKey: tk}
|
||||
case RSAx509Key:
|
||||
return &RSAx509PublicKey{TUFKey: tk}
|
||||
case ED25519Key:
|
||||
return &ED25519PublicKey{TUFKey: tk}
|
||||
}
|
||||
return &UnknownPublicKey{TUFKey: tk}
|
||||
}
|
||||
|
||||
func typedPrivateKey(tk TUFKey) (PrivateKey, error) {
|
||||
private := tk.Value.Private
|
||||
tk.Value.Private = nil
|
||||
switch tk.Algorithm() {
|
||||
case ECDSAKey:
|
||||
return NewECDSAPrivateKey(
|
||||
&ECDSAPublicKey{
|
||||
TUFKey: tk,
|
||||
},
|
||||
private,
|
||||
)
|
||||
case ECDSAx509Key:
|
||||
return NewECDSAPrivateKey(
|
||||
&ECDSAx509PublicKey{
|
||||
TUFKey: tk,
|
||||
},
|
||||
private,
|
||||
)
|
||||
case RSAKey:
|
||||
return NewRSAPrivateKey(
|
||||
&RSAPublicKey{
|
||||
TUFKey: tk,
|
||||
},
|
||||
private,
|
||||
)
|
||||
case RSAx509Key:
|
||||
return NewRSAPrivateKey(
|
||||
&RSAx509PublicKey{
|
||||
TUFKey: tk,
|
||||
},
|
||||
private,
|
||||
)
|
||||
case ED25519Key:
|
||||
return NewED25519PrivateKey(
|
||||
ED25519PublicKey{
|
||||
TUFKey: tk,
|
||||
},
|
||||
private,
|
||||
)
|
||||
}
|
||||
return &UnknownPrivateKey{
|
||||
TUFKey: tk,
|
||||
privateKey: privateKey{private: private},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewPublicKey creates a new, correctly typed PublicKey, using the
|
||||
// UnknownPublicKey catchall for unsupported ciphers
|
||||
func NewPublicKey(alg string, public []byte) PublicKey {
|
||||
tk := TUFKey{
|
||||
Type: alg,
|
||||
Value: KeyPair{
|
||||
Public: public,
|
||||
},
|
||||
}
|
||||
return typedPublicKey(tk)
|
||||
}
|
||||
|
||||
// NewPrivateKey creates a new, correctly typed PrivateKey, using the
|
||||
// UnknownPrivateKey catchall for unsupported ciphers
|
||||
func NewPrivateKey(pubKey PublicKey, private []byte) (PrivateKey, error) {
|
||||
tk := TUFKey{
|
||||
Type: pubKey.Algorithm(),
|
||||
Value: KeyPair{
|
||||
Public: pubKey.Public(),
|
||||
Private: private, // typedPrivateKey moves this value
|
||||
},
|
||||
}
|
||||
return typedPrivateKey(tk)
|
||||
}
|
||||
|
||||
// UnmarshalPublicKey is used to parse individual public keys in JSON
|
||||
func UnmarshalPublicKey(data []byte) (PublicKey, error) {
|
||||
var parsed TUFKey
|
||||
err := json.Unmarshal(data, &parsed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return typedPublicKey(parsed), nil
|
||||
}
|
||||
|
||||
// UnmarshalPrivateKey is used to parse individual private keys in JSON
|
||||
func UnmarshalPrivateKey(data []byte) (PrivateKey, error) {
|
||||
var parsed TUFKey
|
||||
err := json.Unmarshal(data, &parsed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return typedPrivateKey(parsed)
|
||||
}
|
||||
|
||||
// TUFKey is the structure used for both public and private keys in TUF.
|
||||
// Normally it would make sense to use a different structures for public and
|
||||
// private keys, but that would change the key ID algorithm (since the canonical
|
||||
// JSON would be different). This structure should normally be accessed through
|
||||
// the PublicKey or PrivateKey interfaces.
|
||||
type TUFKey struct {
|
||||
id string
|
||||
Type string `json:"keytype"`
|
||||
Value KeyPair `json:"keyval"`
|
||||
}
|
||||
|
||||
// Algorithm returns the algorithm of the key
|
||||
func (k TUFKey) Algorithm() string {
|
||||
return k.Type
|
||||
}
|
||||
|
||||
// ID efficiently generates if necessary, and caches the ID of the key
|
||||
func (k *TUFKey) ID() string {
|
||||
if k.id == "" {
|
||||
pubK := TUFKey{
|
||||
Type: k.Algorithm(),
|
||||
Value: KeyPair{
|
||||
Public: k.Public(),
|
||||
Private: nil,
|
||||
},
|
||||
}
|
||||
data, err := json.MarshalCanonical(&pubK)
|
||||
if err != nil {
|
||||
logrus.Error("Error generating key ID:", err)
|
||||
}
|
||||
digest := sha256.Sum256(data)
|
||||
k.id = hex.EncodeToString(digest[:])
|
||||
}
|
||||
return k.id
|
||||
}
|
||||
|
||||
// Public returns the public bytes
|
||||
func (k TUFKey) Public() []byte {
|
||||
return k.Value.Public
|
||||
}
|
||||
|
||||
// Public key types
|
||||
|
||||
// ECDSAPublicKey represents an ECDSA key using a raw serialization
|
||||
// of the public key
|
||||
type ECDSAPublicKey struct {
|
||||
TUFKey
|
||||
}
|
||||
|
||||
// ECDSAx509PublicKey represents an ECDSA key using an x509 cert
|
||||
// as the serialized format of the public key
|
||||
type ECDSAx509PublicKey struct {
|
||||
TUFKey
|
||||
}
|
||||
|
||||
// RSAPublicKey represents an RSA key using a raw serialization
|
||||
// of the public key
|
||||
type RSAPublicKey struct {
|
||||
TUFKey
|
||||
}
|
||||
|
||||
// RSAx509PublicKey represents an RSA key using an x509 cert
|
||||
// as the serialized format of the public key
|
||||
type RSAx509PublicKey struct {
|
||||
TUFKey
|
||||
}
|
||||
|
||||
// ED25519PublicKey represents an ED25519 key using a raw serialization
|
||||
// of the public key
|
||||
type ED25519PublicKey struct {
|
||||
TUFKey
|
||||
}
|
||||
|
||||
// UnknownPublicKey is a catchall for key types that are not supported
|
||||
type UnknownPublicKey struct {
|
||||
TUFKey
|
||||
}
|
||||
|
||||
// NewECDSAPublicKey initializes a new public key with the ECDSAKey type
|
||||
func NewECDSAPublicKey(public []byte) *ECDSAPublicKey {
|
||||
return &ECDSAPublicKey{
|
||||
TUFKey: TUFKey{
|
||||
Type: ECDSAKey,
|
||||
Value: KeyPair{
|
||||
Public: public,
|
||||
Private: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewECDSAx509PublicKey initializes a new public key with the ECDSAx509Key type
|
||||
func NewECDSAx509PublicKey(public []byte) *ECDSAx509PublicKey {
|
||||
return &ECDSAx509PublicKey{
|
||||
TUFKey: TUFKey{
|
||||
Type: ECDSAx509Key,
|
||||
Value: KeyPair{
|
||||
Public: public,
|
||||
Private: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewRSAPublicKey initializes a new public key with the RSA type
|
||||
func NewRSAPublicKey(public []byte) *RSAPublicKey {
|
||||
return &RSAPublicKey{
|
||||
TUFKey: TUFKey{
|
||||
Type: RSAKey,
|
||||
Value: KeyPair{
|
||||
Public: public,
|
||||
Private: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewRSAx509PublicKey initializes a new public key with the RSAx509Key type
|
||||
func NewRSAx509PublicKey(public []byte) *RSAx509PublicKey {
|
||||
return &RSAx509PublicKey{
|
||||
TUFKey: TUFKey{
|
||||
Type: RSAx509Key,
|
||||
Value: KeyPair{
|
||||
Public: public,
|
||||
Private: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewED25519PublicKey initializes a new public key with the ED25519Key type
|
||||
func NewED25519PublicKey(public []byte) *ED25519PublicKey {
|
||||
return &ED25519PublicKey{
|
||||
TUFKey: TUFKey{
|
||||
Type: ED25519Key,
|
||||
Value: KeyPair{
|
||||
Public: public,
|
||||
Private: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Private key types
|
||||
type privateKey struct {
|
||||
private []byte
|
||||
}
|
||||
|
||||
type signer struct {
|
||||
signer crypto.Signer
|
||||
}
|
||||
|
||||
// ECDSAPrivateKey represents a private ECDSA key
|
||||
type ECDSAPrivateKey struct {
|
||||
PublicKey
|
||||
privateKey
|
||||
signer
|
||||
}
|
||||
|
||||
// RSAPrivateKey represents a private RSA key
|
||||
type RSAPrivateKey struct {
|
||||
PublicKey
|
||||
privateKey
|
||||
signer
|
||||
}
|
||||
|
||||
// ED25519PrivateKey represents a private ED25519 key
|
||||
type ED25519PrivateKey struct {
|
||||
ED25519PublicKey
|
||||
privateKey
|
||||
}
|
||||
|
||||
// UnknownPrivateKey is a catchall for unsupported key types
|
||||
type UnknownPrivateKey struct {
|
||||
TUFKey
|
||||
privateKey
|
||||
}
|
||||
|
||||
// NewECDSAPrivateKey initializes a new ECDSA private key
|
||||
func NewECDSAPrivateKey(public PublicKey, private []byte) (*ECDSAPrivateKey, error) {
|
||||
switch public.(type) {
|
||||
case *ECDSAPublicKey, *ECDSAx509PublicKey:
|
||||
default:
|
||||
return nil, errors.New("invalid public key type provided to NewECDSAPrivateKey")
|
||||
}
|
||||
ecdsaPrivKey, err := x509.ParseECPrivateKey(private)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ECDSAPrivateKey{
|
||||
PublicKey: public,
|
||||
privateKey: privateKey{private: private},
|
||||
signer: signer{signer: ecdsaPrivKey},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewRSAPrivateKey initialized a new RSA private key
|
||||
func NewRSAPrivateKey(public PublicKey, private []byte) (*RSAPrivateKey, error) {
|
||||
switch public.(type) {
|
||||
case *RSAPublicKey, *RSAx509PublicKey:
|
||||
default:
|
||||
return nil, errors.New("invalid public key type provided to NewRSAPrivateKey")
|
||||
}
|
||||
rsaPrivKey, err := x509.ParsePKCS1PrivateKey(private)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &RSAPrivateKey{
|
||||
PublicKey: public,
|
||||
privateKey: privateKey{private: private},
|
||||
signer: signer{signer: rsaPrivKey},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewED25519PrivateKey initialized a new ED25519 private key
|
||||
func NewED25519PrivateKey(public ED25519PublicKey, private []byte) (*ED25519PrivateKey, error) {
|
||||
return &ED25519PrivateKey{
|
||||
ED25519PublicKey: public,
|
||||
privateKey: privateKey{private: private},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Private return the serialized private bytes of the key
|
||||
func (k privateKey) Private() []byte {
|
||||
return k.private
|
||||
}
|
||||
|
||||
// CryptoSigner returns the underlying crypto.Signer for use cases where we need the default
|
||||
// signature or public key functionality (like when we generate certificates)
|
||||
func (s signer) CryptoSigner() crypto.Signer {
|
||||
return s.signer
|
||||
}
|
||||
|
||||
// CryptoSigner returns the ED25519PrivateKey which already implements crypto.Signer
|
||||
func (k ED25519PrivateKey) CryptoSigner() crypto.Signer {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CryptoSigner returns the UnknownPrivateKey which already implements crypto.Signer
|
||||
func (k UnknownPrivateKey) CryptoSigner() crypto.Signer {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ecdsaSig struct {
|
||||
R *big.Int
|
||||
S *big.Int
|
||||
}
|
||||
|
||||
// Sign creates an ecdsa signature
|
||||
func (k ECDSAPrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||
ecdsaPrivKey, ok := k.CryptoSigner().(*ecdsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, errors.New("signer was based on the wrong key type")
|
||||
}
|
||||
hashed := sha256.Sum256(msg)
|
||||
sigASN1, err := ecdsaPrivKey.Sign(rand, hashed[:], opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig := ecdsaSig{}
|
||||
_, err = asn1.Unmarshal(sigASN1, &sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rBytes, sBytes := sig.R.Bytes(), sig.S.Bytes()
|
||||
octetLength := (ecdsaPrivKey.Params().BitSize + 7) >> 3
|
||||
|
||||
// MUST include leading zeros in the output
|
||||
rBuf := make([]byte, octetLength-len(rBytes), octetLength)
|
||||
sBuf := make([]byte, octetLength-len(sBytes), octetLength)
|
||||
|
||||
rBuf = append(rBuf, rBytes...)
|
||||
sBuf = append(sBuf, sBytes...)
|
||||
return append(rBuf, sBuf...), nil
|
||||
}
|
||||
|
||||
// Sign creates an rsa signature
|
||||
func (k RSAPrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||
hashed := sha256.Sum256(msg)
|
||||
if opts == nil {
|
||||
opts = &rsa.PSSOptions{
|
||||
SaltLength: rsa.PSSSaltLengthEqualsHash,
|
||||
Hash: crypto.SHA256,
|
||||
}
|
||||
}
|
||||
return k.CryptoSigner().Sign(rand, hashed[:], opts)
|
||||
}
|
||||
|
||||
// Sign creates an ed25519 signature
|
||||
func (k ED25519PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||
priv := make([]byte, ed25519.PrivateKeySize)
|
||||
// The ed25519 key is serialized as public key then private key, so just use private key here.
|
||||
copy(priv, k.private[ed25519.PublicKeySize:])
|
||||
return ed25519.Sign(ed25519.PrivateKey(priv), msg)[:], nil
|
||||
}
|
||||
|
||||
// Sign on an UnknownPrivateKey raises an error because the client does not
|
||||
// know how to sign with this key type.
|
||||
func (k UnknownPrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||
return nil, errors.New("unknown key type, cannot sign")
|
||||
}
|
||||
|
||||
// SignatureAlgorithm returns the SigAlgorithm for a ECDSAPrivateKey
|
||||
func (k ECDSAPrivateKey) SignatureAlgorithm() SigAlgorithm {
|
||||
return ECDSASignature
|
||||
}
|
||||
|
||||
// SignatureAlgorithm returns the SigAlgorithm for a RSAPrivateKey
|
||||
func (k RSAPrivateKey) SignatureAlgorithm() SigAlgorithm {
|
||||
return RSAPSSSignature
|
||||
}
|
||||
|
||||
// SignatureAlgorithm returns the SigAlgorithm for a ED25519PrivateKey
|
||||
func (k ED25519PrivateKey) SignatureAlgorithm() SigAlgorithm {
|
||||
return EDDSASignature
|
||||
}
|
||||
|
||||
// SignatureAlgorithm returns the SigAlgorithm for an UnknownPrivateKey
|
||||
func (k UnknownPrivateKey) SignatureAlgorithm() SigAlgorithm {
|
||||
return ""
|
||||
}
|
||||
|
||||
// PublicKeyFromPrivate returns a new TUFKey based on a private key, with
|
||||
// the private key bytes guaranteed to be nil.
|
||||
func PublicKeyFromPrivate(pk PrivateKey) PublicKey {
|
||||
return typedPublicKey(TUFKey{
|
||||
Type: pk.Algorithm(),
|
||||
Value: KeyPair{
|
||||
Public: pk.Public(),
|
||||
Private: nil,
|
||||
},
|
||||
})
|
||||
}
|
339
vendor/github.com/theupdateframework/notary/tuf/data/roles.go
generated
vendored
Normal file
339
vendor/github.com/theupdateframework/notary/tuf/data/roles.go
generated
vendored
Normal file
@ -0,0 +1,339 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Canonical base role names
|
||||
var (
|
||||
CanonicalRootRole RoleName = "root"
|
||||
CanonicalTargetsRole RoleName = "targets"
|
||||
CanonicalSnapshotRole RoleName = "snapshot"
|
||||
CanonicalTimestampRole RoleName = "timestamp"
|
||||
)
|
||||
|
||||
// BaseRoles is an easy to iterate list of the top level
|
||||
// roles.
|
||||
var BaseRoles = []RoleName{
|
||||
CanonicalRootRole,
|
||||
CanonicalTargetsRole,
|
||||
CanonicalSnapshotRole,
|
||||
CanonicalTimestampRole,
|
||||
}
|
||||
|
||||
// Regex for validating delegation names
|
||||
var delegationRegexp = regexp.MustCompile("^[-a-z0-9_/]+$")
|
||||
|
||||
// ErrNoSuchRole indicates the roles doesn't exist
|
||||
type ErrNoSuchRole struct {
|
||||
Role RoleName
|
||||
}
|
||||
|
||||
func (e ErrNoSuchRole) Error() string {
|
||||
return fmt.Sprintf("role does not exist: %s", e.Role)
|
||||
}
|
||||
|
||||
// ErrInvalidRole represents an error regarding a role. Typically
|
||||
// something like a role for which sone of the public keys were
|
||||
// not found in the TUF repo.
|
||||
type ErrInvalidRole struct {
|
||||
Role RoleName
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (e ErrInvalidRole) Error() string {
|
||||
if e.Reason != "" {
|
||||
return fmt.Sprintf("tuf: invalid role %s. %s", e.Role, e.Reason)
|
||||
}
|
||||
return fmt.Sprintf("tuf: invalid role %s.", e.Role)
|
||||
}
|
||||
|
||||
// ValidRole only determines the name is semantically
|
||||
// correct. For target delegated roles, it does NOT check
|
||||
// the appropriate parent roles exist.
|
||||
func ValidRole(name RoleName) bool {
|
||||
if IsDelegation(name) {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, v := range BaseRoles {
|
||||
if name == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsDelegation checks if the role is a delegation or a root role
|
||||
func IsDelegation(role RoleName) bool {
|
||||
strRole := role.String()
|
||||
targetsBase := CanonicalTargetsRole + "/"
|
||||
|
||||
whitelistedChars := delegationRegexp.MatchString(strRole)
|
||||
|
||||
// Limit size of full role string to 255 chars for db column size limit
|
||||
correctLength := len(role) < 256
|
||||
|
||||
// Removes ., .., extra slashes, and trailing slash
|
||||
isClean := path.Clean(strRole) == strRole
|
||||
return strings.HasPrefix(strRole, targetsBase.String()) &&
|
||||
whitelistedChars &&
|
||||
correctLength &&
|
||||
isClean
|
||||
}
|
||||
|
||||
// IsBaseRole checks if the role is a base role
|
||||
func IsBaseRole(role RoleName) bool {
|
||||
for _, baseRole := range BaseRoles {
|
||||
if role == baseRole {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsWildDelegation determines if a role represents a valid wildcard delegation
|
||||
// path, i.e. targets/*, targets/foo/*.
|
||||
// The wildcard may only appear as the final part of the delegation and must
|
||||
// be a whole segment, i.e. targets/foo* is not a valid wildcard delegation.
|
||||
func IsWildDelegation(role RoleName) bool {
|
||||
if path.Clean(role.String()) != role.String() {
|
||||
return false
|
||||
}
|
||||
base := role.Parent()
|
||||
if !(IsDelegation(base) || base == CanonicalTargetsRole) {
|
||||
return false
|
||||
}
|
||||
return role[len(role)-2:] == "/*"
|
||||
}
|
||||
|
||||
// BaseRole is an internal representation of a root/targets/snapshot/timestamp role, with its public keys included
|
||||
type BaseRole struct {
|
||||
Keys map[string]PublicKey
|
||||
Name RoleName
|
||||
Threshold int
|
||||
}
|
||||
|
||||
// NewBaseRole creates a new BaseRole object with the provided parameters
|
||||
func NewBaseRole(name RoleName, threshold int, keys ...PublicKey) BaseRole {
|
||||
r := BaseRole{
|
||||
Name: name,
|
||||
Threshold: threshold,
|
||||
Keys: make(map[string]PublicKey),
|
||||
}
|
||||
for _, k := range keys {
|
||||
r.Keys[k.ID()] = k
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// ListKeys retrieves the public keys valid for this role
|
||||
func (b BaseRole) ListKeys() KeyList {
|
||||
return listKeys(b.Keys)
|
||||
}
|
||||
|
||||
// ListKeyIDs retrieves the list of key IDs valid for this role
|
||||
func (b BaseRole) ListKeyIDs() []string {
|
||||
return listKeyIDs(b.Keys)
|
||||
}
|
||||
|
||||
// Equals returns whether this BaseRole equals another BaseRole
|
||||
func (b BaseRole) Equals(o BaseRole) bool {
|
||||
if b.Threshold != o.Threshold || b.Name != o.Name || len(b.Keys) != len(o.Keys) {
|
||||
return false
|
||||
}
|
||||
|
||||
for keyID, key := range b.Keys {
|
||||
oKey, ok := o.Keys[keyID]
|
||||
if !ok || key.ID() != oKey.ID() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// DelegationRole is an internal representation of a delegation role, with its public keys included
|
||||
type DelegationRole struct {
|
||||
BaseRole
|
||||
Paths []string
|
||||
}
|
||||
|
||||
func listKeys(keyMap map[string]PublicKey) KeyList {
|
||||
keys := KeyList{}
|
||||
for _, key := range keyMap {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func listKeyIDs(keyMap map[string]PublicKey) []string {
|
||||
keyIDs := []string{}
|
||||
for id := range keyMap {
|
||||
keyIDs = append(keyIDs, id)
|
||||
}
|
||||
return keyIDs
|
||||
}
|
||||
|
||||
// Restrict restricts the paths and path hash prefixes for the passed in delegation role,
|
||||
// returning a copy of the role with validated paths as if it was a direct child
|
||||
func (d DelegationRole) Restrict(child DelegationRole) (DelegationRole, error) {
|
||||
if !d.IsParentOf(child) {
|
||||
return DelegationRole{}, fmt.Errorf("%s is not a parent of %s", d.Name, child.Name)
|
||||
}
|
||||
return DelegationRole{
|
||||
BaseRole: BaseRole{
|
||||
Keys: child.Keys,
|
||||
Name: child.Name,
|
||||
Threshold: child.Threshold,
|
||||
},
|
||||
Paths: RestrictDelegationPathPrefixes(d.Paths, child.Paths),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IsParentOf returns whether the passed in delegation role is the direct child of this role,
|
||||
// determined by delegation name.
|
||||
// Ex: targets/a is a direct parent of targets/a/b, but targets/a is not a direct parent of targets/a/b/c
|
||||
func (d DelegationRole) IsParentOf(child DelegationRole) bool {
|
||||
return path.Dir(child.Name.String()) == d.Name.String()
|
||||
}
|
||||
|
||||
// CheckPaths checks if a given path is valid for the role
|
||||
func (d DelegationRole) CheckPaths(path string) bool {
|
||||
return checkPaths(path, d.Paths)
|
||||
}
|
||||
|
||||
func checkPaths(path string, permitted []string) bool {
|
||||
for _, p := range permitted {
|
||||
if strings.HasPrefix(path, p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RestrictDelegationPathPrefixes returns the list of valid delegationPaths that are prefixed by parentPaths
|
||||
func RestrictDelegationPathPrefixes(parentPaths, delegationPaths []string) []string {
|
||||
validPaths := []string{}
|
||||
if len(delegationPaths) == 0 {
|
||||
return validPaths
|
||||
}
|
||||
|
||||
// Validate each individual delegation path
|
||||
for _, delgPath := range delegationPaths {
|
||||
isPrefixed := false
|
||||
for _, parentPath := range parentPaths {
|
||||
if strings.HasPrefix(delgPath, parentPath) {
|
||||
isPrefixed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// If the delegation path did not match prefix against any parent path, it is not valid
|
||||
if isPrefixed {
|
||||
validPaths = append(validPaths, delgPath)
|
||||
}
|
||||
}
|
||||
return validPaths
|
||||
}
|
||||
|
||||
// RootRole is a cut down role as it appears in the root.json
|
||||
// Eventually should only be used for immediately before and after serialization/deserialization
|
||||
type RootRole struct {
|
||||
KeyIDs []string `json:"keyids"`
|
||||
Threshold int `json:"threshold"`
|
||||
}
|
||||
|
||||
// Role is a more verbose role as they appear in targets delegations
|
||||
// Eventually should only be used for immediately before and after serialization/deserialization
|
||||
type Role struct {
|
||||
RootRole
|
||||
Name RoleName `json:"name"`
|
||||
Paths []string `json:"paths,omitempty"`
|
||||
}
|
||||
|
||||
// NewRole creates a new Role object from the given parameters
|
||||
func NewRole(name RoleName, threshold int, keyIDs, paths []string) (*Role, error) {
|
||||
if IsDelegation(name) {
|
||||
if len(paths) == 0 {
|
||||
logrus.Debugf("role %s with no Paths will never be able to publish content until one or more are added", name)
|
||||
}
|
||||
}
|
||||
if threshold < 1 {
|
||||
return nil, ErrInvalidRole{Role: name}
|
||||
}
|
||||
if !ValidRole(name) {
|
||||
return nil, ErrInvalidRole{Role: name}
|
||||
}
|
||||
return &Role{
|
||||
RootRole: RootRole{
|
||||
KeyIDs: keyIDs,
|
||||
Threshold: threshold,
|
||||
},
|
||||
Name: name,
|
||||
Paths: paths,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
// CheckPaths checks if a given path is valid for the role
|
||||
func (r Role) CheckPaths(path string) bool {
|
||||
return checkPaths(path, r.Paths)
|
||||
}
|
||||
|
||||
// AddKeys merges the ids into the current list of role key ids
|
||||
func (r *Role) AddKeys(ids []string) {
|
||||
r.KeyIDs = mergeStrSlices(r.KeyIDs, ids)
|
||||
}
|
||||
|
||||
// AddPaths merges the paths into the current list of role paths
|
||||
func (r *Role) AddPaths(paths []string) error {
|
||||
if len(paths) == 0 {
|
||||
return nil
|
||||
}
|
||||
r.Paths = mergeStrSlices(r.Paths, paths)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveKeys removes the ids from the current list of key ids
|
||||
func (r *Role) RemoveKeys(ids []string) {
|
||||
r.KeyIDs = subtractStrSlices(r.KeyIDs, ids)
|
||||
}
|
||||
|
||||
// RemovePaths removes the paths from the current list of role paths
|
||||
func (r *Role) RemovePaths(paths []string) {
|
||||
r.Paths = subtractStrSlices(r.Paths, paths)
|
||||
}
|
||||
|
||||
func mergeStrSlices(orig, new []string) []string {
|
||||
have := make(map[string]bool)
|
||||
for _, e := range orig {
|
||||
have[e] = true
|
||||
}
|
||||
merged := make([]string, len(orig), len(orig)+len(new))
|
||||
copy(merged, orig)
|
||||
for _, e := range new {
|
||||
if !have[e] {
|
||||
merged = append(merged, e)
|
||||
}
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
func subtractStrSlices(orig, remove []string) []string {
|
||||
kill := make(map[string]bool)
|
||||
for _, e := range remove {
|
||||
kill[e] = true
|
||||
}
|
||||
var keep []string
|
||||
for _, e := range orig {
|
||||
if !kill[e] {
|
||||
keep = append(keep, e)
|
||||
}
|
||||
}
|
||||
return keep
|
||||
}
|
171
vendor/github.com/theupdateframework/notary/tuf/data/root.go
generated
vendored
Normal file
171
vendor/github.com/theupdateframework/notary/tuf/data/root.go
generated
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/go/canonical/json"
|
||||
)
|
||||
|
||||
// SignedRoot is a fully unpacked root.json
|
||||
type SignedRoot struct {
|
||||
Signatures []Signature
|
||||
Signed Root
|
||||
Dirty bool
|
||||
}
|
||||
|
||||
// Root is the Signed component of a root.json
|
||||
type Root struct {
|
||||
SignedCommon
|
||||
Keys Keys `json:"keys"`
|
||||
Roles map[RoleName]*RootRole `json:"roles"`
|
||||
ConsistentSnapshot bool `json:"consistent_snapshot"`
|
||||
}
|
||||
|
||||
// isValidRootStructure returns an error, or nil, depending on whether the content of the struct
|
||||
// is valid for root metadata. This does not check signatures or expiry, just that
|
||||
// the metadata content is valid.
|
||||
func isValidRootStructure(r Root) error {
|
||||
expectedType := TUFTypes[CanonicalRootRole]
|
||||
if r.Type != expectedType {
|
||||
return ErrInvalidMetadata{
|
||||
role: CanonicalRootRole, msg: fmt.Sprintf("expected type %s, not %s", expectedType, r.Type)}
|
||||
}
|
||||
|
||||
if r.Version < 1 {
|
||||
return ErrInvalidMetadata{
|
||||
role: CanonicalRootRole, msg: "version cannot be less than 1"}
|
||||
}
|
||||
|
||||
// all the base roles MUST appear in the root.json - other roles are allowed,
|
||||
// but other than the mirror role (not currently supported) are out of spec
|
||||
for _, roleName := range BaseRoles {
|
||||
roleObj, ok := r.Roles[roleName]
|
||||
if !ok || roleObj == nil {
|
||||
return ErrInvalidMetadata{
|
||||
role: CanonicalRootRole, msg: fmt.Sprintf("missing %s role specification", roleName)}
|
||||
}
|
||||
if err := isValidRootRoleStructure(CanonicalRootRole, roleName, *roleObj, r.Keys); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isValidRootRoleStructure(metaContainingRole, rootRoleName RoleName, r RootRole, validKeys Keys) error {
|
||||
if r.Threshold < 1 {
|
||||
return ErrInvalidMetadata{
|
||||
role: metaContainingRole,
|
||||
msg: fmt.Sprintf("invalid threshold specified for %s: %v ", rootRoleName, r.Threshold),
|
||||
}
|
||||
}
|
||||
for _, keyID := range r.KeyIDs {
|
||||
if _, ok := validKeys[keyID]; !ok {
|
||||
return ErrInvalidMetadata{
|
||||
role: metaContainingRole,
|
||||
msg: fmt.Sprintf("key ID %s specified in %s without corresponding key", keyID, rootRoleName),
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewRoot initializes a new SignedRoot with a set of keys, roles, and the consistent flag
|
||||
func NewRoot(keys map[string]PublicKey, roles map[RoleName]*RootRole, consistent bool) (*SignedRoot, error) {
|
||||
signedRoot := &SignedRoot{
|
||||
Signatures: make([]Signature, 0),
|
||||
Signed: Root{
|
||||
SignedCommon: SignedCommon{
|
||||
Type: TUFTypes[CanonicalRootRole],
|
||||
Version: 0,
|
||||
Expires: DefaultExpires(CanonicalRootRole),
|
||||
},
|
||||
Keys: keys,
|
||||
Roles: roles,
|
||||
ConsistentSnapshot: consistent,
|
||||
},
|
||||
Dirty: true,
|
||||
}
|
||||
|
||||
return signedRoot, nil
|
||||
}
|
||||
|
||||
// BuildBaseRole returns a copy of a BaseRole using the information in this SignedRoot for the specified role name.
|
||||
// Will error for invalid role name or key metadata within this SignedRoot
|
||||
func (r SignedRoot) BuildBaseRole(roleName RoleName) (BaseRole, error) {
|
||||
roleData, ok := r.Signed.Roles[roleName]
|
||||
if !ok {
|
||||
return BaseRole{}, ErrInvalidRole{Role: roleName, Reason: "role not found in root file"}
|
||||
}
|
||||
// Get all public keys for the base role from TUF metadata
|
||||
keyIDs := roleData.KeyIDs
|
||||
pubKeys := make(map[string]PublicKey)
|
||||
for _, keyID := range keyIDs {
|
||||
pubKey, ok := r.Signed.Keys[keyID]
|
||||
if !ok {
|
||||
return BaseRole{}, ErrInvalidRole{
|
||||
Role: roleName,
|
||||
Reason: fmt.Sprintf("key with ID %s was not found in root metadata", keyID),
|
||||
}
|
||||
}
|
||||
pubKeys[keyID] = pubKey
|
||||
}
|
||||
|
||||
return BaseRole{
|
||||
Name: roleName,
|
||||
Keys: pubKeys,
|
||||
Threshold: roleData.Threshold,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ToSigned partially serializes a SignedRoot for further signing
|
||||
func (r SignedRoot) ToSigned() (*Signed, error) {
|
||||
s, err := defaultSerializer.MarshalCanonical(r.Signed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// cast into a json.RawMessage
|
||||
signed := json.RawMessage{}
|
||||
err = signed.UnmarshalJSON(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sigs := make([]Signature, len(r.Signatures))
|
||||
copy(sigs, r.Signatures)
|
||||
return &Signed{
|
||||
Signatures: sigs,
|
||||
Signed: &signed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the serialized form of SignedRoot as bytes
|
||||
func (r SignedRoot) MarshalJSON() ([]byte, error) {
|
||||
signed, err := r.ToSigned()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return defaultSerializer.Marshal(signed)
|
||||
}
|
||||
|
||||
// RootFromSigned fully unpacks a Signed object into a SignedRoot and ensures
|
||||
// that it is a valid SignedRoot
|
||||
func RootFromSigned(s *Signed) (*SignedRoot, error) {
|
||||
r := Root{}
|
||||
if s.Signed == nil {
|
||||
return nil, ErrInvalidMetadata{
|
||||
role: CanonicalRootRole,
|
||||
msg: "root file contained an empty payload",
|
||||
}
|
||||
}
|
||||
if err := defaultSerializer.Unmarshal(*s.Signed, &r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := isValidRootStructure(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sigs := make([]Signature, len(s.Signatures))
|
||||
copy(sigs, s.Signatures)
|
||||
return &SignedRoot{
|
||||
Signatures: sigs,
|
||||
Signed: r,
|
||||
}, nil
|
||||
}
|
36
vendor/github.com/theupdateframework/notary/tuf/data/serializer.go
generated
vendored
Normal file
36
vendor/github.com/theupdateframework/notary/tuf/data/serializer.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package data
|
||||
|
||||
import "github.com/docker/go/canonical/json"
|
||||
|
||||
// Serializer is an interface that can marshal and unmarshal TUF data. This
|
||||
// is expected to be a canonical JSON marshaller
|
||||
type serializer interface {
|
||||
MarshalCanonical(from interface{}) ([]byte, error)
|
||||
Marshal(from interface{}) ([]byte, error)
|
||||
Unmarshal(from []byte, to interface{}) error
|
||||
}
|
||||
|
||||
// CanonicalJSON marshals to and from canonical JSON
|
||||
type canonicalJSON struct{}
|
||||
|
||||
// MarshalCanonical returns the canonical JSON form of a thing
|
||||
func (c canonicalJSON) MarshalCanonical(from interface{}) ([]byte, error) {
|
||||
return json.MarshalCanonical(from)
|
||||
}
|
||||
|
||||
// Marshal returns the regular non-canonical JSON form of a thing
|
||||
func (c canonicalJSON) Marshal(from interface{}) ([]byte, error) {
|
||||
return json.Marshal(from)
|
||||
}
|
||||
|
||||
// Unmarshal unmarshals some JSON bytes
|
||||
func (c canonicalJSON) Unmarshal(from []byte, to interface{}) error {
|
||||
return json.Unmarshal(from, to)
|
||||
}
|
||||
|
||||
// defaultSerializer is a canonical JSON serializer
|
||||
var defaultSerializer serializer = canonicalJSON{}
|
||||
|
||||
func setDefaultSerializer(s serializer) {
|
||||
defaultSerializer = s
|
||||
}
|
169
vendor/github.com/theupdateframework/notary/tuf/data/snapshot.go
generated
vendored
Normal file
169
vendor/github.com/theupdateframework/notary/tuf/data/snapshot.go
generated
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/go/canonical/json"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary"
|
||||
)
|
||||
|
||||
// SignedSnapshot is a fully unpacked snapshot.json
|
||||
type SignedSnapshot struct {
|
||||
Signatures []Signature
|
||||
Signed Snapshot
|
||||
Dirty bool
|
||||
}
|
||||
|
||||
// Snapshot is the Signed component of a snapshot.json
|
||||
type Snapshot struct {
|
||||
SignedCommon
|
||||
Meta Files `json:"meta"`
|
||||
}
|
||||
|
||||
// IsValidSnapshotStructure returns an error, or nil, depending on whether the content of the
|
||||
// struct is valid for snapshot metadata. This does not check signatures or expiry, just that
|
||||
// the metadata content is valid.
|
||||
func IsValidSnapshotStructure(s Snapshot) error {
|
||||
expectedType := TUFTypes[CanonicalSnapshotRole]
|
||||
if s.Type != expectedType {
|
||||
return ErrInvalidMetadata{
|
||||
role: CanonicalSnapshotRole, msg: fmt.Sprintf("expected type %s, not %s", expectedType, s.Type)}
|
||||
}
|
||||
|
||||
if s.Version < 1 {
|
||||
return ErrInvalidMetadata{
|
||||
role: CanonicalSnapshotRole, msg: "version cannot be less than one"}
|
||||
}
|
||||
|
||||
for _, file := range []RoleName{CanonicalRootRole, CanonicalTargetsRole} {
|
||||
// Meta is a map of FileMeta, so if the role isn't in the map it returns
|
||||
// an empty FileMeta, which has an empty map, and you can check on keys
|
||||
// from an empty map.
|
||||
//
|
||||
// For now sha256 is required and sha512 is not.
|
||||
if _, ok := s.Meta[file.String()].Hashes[notary.SHA256]; !ok {
|
||||
return ErrInvalidMetadata{
|
||||
role: CanonicalSnapshotRole,
|
||||
msg: fmt.Sprintf("missing %s sha256 checksum information", file.String()),
|
||||
}
|
||||
}
|
||||
if err := CheckValidHashStructures(s.Meta[file.String()].Hashes); err != nil {
|
||||
return ErrInvalidMetadata{
|
||||
role: CanonicalSnapshotRole,
|
||||
msg: fmt.Sprintf("invalid %s checksum information, %v", file.String(), err),
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSnapshot initializes a SignedSnapshot with a given top level root
|
||||
// and targets objects
|
||||
func NewSnapshot(root *Signed, targets *Signed) (*SignedSnapshot, error) {
|
||||
logrus.Debug("generating new snapshot...")
|
||||
targetsJSON, err := json.Marshal(targets)
|
||||
if err != nil {
|
||||
logrus.Debug("Error Marshalling Targets")
|
||||
return nil, err
|
||||
}
|
||||
rootJSON, err := json.Marshal(root)
|
||||
if err != nil {
|
||||
logrus.Debug("Error Marshalling Root")
|
||||
return nil, err
|
||||
}
|
||||
rootMeta, err := NewFileMeta(bytes.NewReader(rootJSON), NotaryDefaultHashes...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
targetsMeta, err := NewFileMeta(bytes.NewReader(targetsJSON), NotaryDefaultHashes...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SignedSnapshot{
|
||||
Signatures: make([]Signature, 0),
|
||||
Signed: Snapshot{
|
||||
SignedCommon: SignedCommon{
|
||||
Type: TUFTypes[CanonicalSnapshotRole],
|
||||
Version: 0,
|
||||
Expires: DefaultExpires(CanonicalSnapshotRole),
|
||||
},
|
||||
Meta: Files{
|
||||
CanonicalRootRole.String(): rootMeta,
|
||||
CanonicalTargetsRole.String(): targetsMeta,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ToSigned partially serializes a SignedSnapshot for further signing
|
||||
func (sp *SignedSnapshot) ToSigned() (*Signed, error) {
|
||||
s, err := defaultSerializer.MarshalCanonical(sp.Signed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signed := json.RawMessage{}
|
||||
err = signed.UnmarshalJSON(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sigs := make([]Signature, len(sp.Signatures))
|
||||
copy(sigs, sp.Signatures)
|
||||
return &Signed{
|
||||
Signatures: sigs,
|
||||
Signed: &signed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AddMeta updates a role in the snapshot with new meta
|
||||
func (sp *SignedSnapshot) AddMeta(role RoleName, meta FileMeta) {
|
||||
sp.Signed.Meta[role.String()] = meta
|
||||
sp.Dirty = true
|
||||
}
|
||||
|
||||
// GetMeta gets the metadata for a particular role, returning an error if it's
|
||||
// not found
|
||||
func (sp *SignedSnapshot) GetMeta(role RoleName) (*FileMeta, error) {
|
||||
if meta, ok := sp.Signed.Meta[role.String()]; ok {
|
||||
if _, ok := meta.Hashes["sha256"]; ok {
|
||||
return &meta, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrMissingMeta{Role: role.String()}
|
||||
}
|
||||
|
||||
// DeleteMeta removes a role from the snapshot. If the role doesn't
|
||||
// exist in the snapshot, it's a noop.
|
||||
func (sp *SignedSnapshot) DeleteMeta(role RoleName) {
|
||||
if _, ok := sp.Signed.Meta[role.String()]; ok {
|
||||
delete(sp.Signed.Meta, role.String())
|
||||
sp.Dirty = true
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON returns the serialized form of SignedSnapshot as bytes
|
||||
func (sp *SignedSnapshot) MarshalJSON() ([]byte, error) {
|
||||
signed, err := sp.ToSigned()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return defaultSerializer.Marshal(signed)
|
||||
}
|
||||
|
||||
// SnapshotFromSigned fully unpacks a Signed object into a SignedSnapshot
|
||||
func SnapshotFromSigned(s *Signed) (*SignedSnapshot, error) {
|
||||
sp := Snapshot{}
|
||||
if err := defaultSerializer.Unmarshal(*s.Signed, &sp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := IsValidSnapshotStructure(sp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sigs := make([]Signature, len(s.Signatures))
|
||||
copy(sigs, s.Signatures)
|
||||
return &SignedSnapshot{
|
||||
Signatures: sigs,
|
||||
Signed: sp,
|
||||
}, nil
|
||||
}
|
201
vendor/github.com/theupdateframework/notary/tuf/data/targets.go
generated
vendored
Normal file
201
vendor/github.com/theupdateframework/notary/tuf/data/targets.go
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/docker/go/canonical/json"
|
||||
)
|
||||
|
||||
// SignedTargets is a fully unpacked targets.json, or target delegation
|
||||
// json file
|
||||
type SignedTargets struct {
|
||||
Signatures []Signature
|
||||
Signed Targets
|
||||
Dirty bool
|
||||
}
|
||||
|
||||
// Targets is the Signed components of a targets.json or delegation json file
|
||||
type Targets struct {
|
||||
SignedCommon
|
||||
Targets Files `json:"targets"`
|
||||
Delegations Delegations `json:"delegations,omitempty"`
|
||||
}
|
||||
|
||||
// isValidTargetsStructure returns an error, or nil, depending on whether the content of the struct
|
||||
// is valid for targets metadata. This does not check signatures or expiry, just that
|
||||
// the metadata content is valid.
|
||||
func isValidTargetsStructure(t Targets, roleName RoleName) error {
|
||||
if roleName != CanonicalTargetsRole && !IsDelegation(roleName) {
|
||||
return ErrInvalidRole{Role: roleName}
|
||||
}
|
||||
|
||||
// even if it's a delegated role, the metadata type is "Targets"
|
||||
expectedType := TUFTypes[CanonicalTargetsRole]
|
||||
if t.Type != expectedType {
|
||||
return ErrInvalidMetadata{
|
||||
role: roleName, msg: fmt.Sprintf("expected type %s, not %s", expectedType, t.Type)}
|
||||
}
|
||||
|
||||
if t.Version < 1 {
|
||||
return ErrInvalidMetadata{role: roleName, msg: "version cannot be less than one"}
|
||||
}
|
||||
|
||||
for _, roleObj := range t.Delegations.Roles {
|
||||
if !IsDelegation(roleObj.Name) || path.Dir(roleObj.Name.String()) != roleName.String() {
|
||||
return ErrInvalidMetadata{
|
||||
role: roleName, msg: fmt.Sprintf("delegation role %s invalid", roleObj.Name)}
|
||||
}
|
||||
if err := isValidRootRoleStructure(roleName, roleObj.Name, roleObj.RootRole, t.Delegations.Keys); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewTargets initializes a new empty SignedTargets object
|
||||
func NewTargets() *SignedTargets {
|
||||
return &SignedTargets{
|
||||
Signatures: make([]Signature, 0),
|
||||
Signed: Targets{
|
||||
SignedCommon: SignedCommon{
|
||||
Type: TUFTypes["targets"],
|
||||
Version: 0,
|
||||
Expires: DefaultExpires("targets"),
|
||||
},
|
||||
Targets: make(Files),
|
||||
Delegations: *NewDelegations(),
|
||||
},
|
||||
Dirty: true,
|
||||
}
|
||||
}
|
||||
|
||||
// GetMeta attempts to find the targets entry for the path. It
|
||||
// will return nil in the case of the target not being found.
|
||||
func (t SignedTargets) GetMeta(path string) *FileMeta {
|
||||
for p, meta := range t.Signed.Targets {
|
||||
if p == path {
|
||||
return &meta
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetValidDelegations filters the delegation roles specified in the signed targets, and
|
||||
// only returns roles that are direct children and restricts their paths
|
||||
func (t SignedTargets) GetValidDelegations(parent DelegationRole) []DelegationRole {
|
||||
roles := t.buildDelegationRoles()
|
||||
result := []DelegationRole{}
|
||||
for _, r := range roles {
|
||||
validRole, err := parent.Restrict(r)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
result = append(result, validRole)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// BuildDelegationRole returns a copy of a DelegationRole using the information in this SignedTargets for the specified role name.
|
||||
// Will error for invalid role name or key metadata within this SignedTargets. Path data is not validated.
|
||||
func (t *SignedTargets) BuildDelegationRole(roleName RoleName) (DelegationRole, error) {
|
||||
for _, role := range t.Signed.Delegations.Roles {
|
||||
if role.Name == roleName {
|
||||
pubKeys := make(map[string]PublicKey)
|
||||
for _, keyID := range role.KeyIDs {
|
||||
pubKey, ok := t.Signed.Delegations.Keys[keyID]
|
||||
if !ok {
|
||||
// Couldn't retrieve all keys, so stop walking and return invalid role
|
||||
return DelegationRole{}, ErrInvalidRole{
|
||||
Role: roleName,
|
||||
Reason: "role lists unknown key " + keyID + " as a signing key",
|
||||
}
|
||||
}
|
||||
pubKeys[keyID] = pubKey
|
||||
}
|
||||
return DelegationRole{
|
||||
BaseRole: BaseRole{
|
||||
Name: role.Name,
|
||||
Keys: pubKeys,
|
||||
Threshold: role.Threshold,
|
||||
},
|
||||
Paths: role.Paths,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return DelegationRole{}, ErrNoSuchRole{Role: roleName}
|
||||
}
|
||||
|
||||
// helper function to create DelegationRole structures from all delegations in a SignedTargets,
|
||||
// these delegations are read directly from the SignedTargets and not modified or validated
|
||||
func (t SignedTargets) buildDelegationRoles() []DelegationRole {
|
||||
var roles []DelegationRole
|
||||
for _, roleData := range t.Signed.Delegations.Roles {
|
||||
delgRole, err := t.BuildDelegationRole(roleData.Name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
roles = append(roles, delgRole)
|
||||
}
|
||||
return roles
|
||||
}
|
||||
|
||||
// AddTarget adds or updates the meta for the given path
|
||||
func (t *SignedTargets) AddTarget(path string, meta FileMeta) {
|
||||
t.Signed.Targets[path] = meta
|
||||
t.Dirty = true
|
||||
}
|
||||
|
||||
// AddDelegation will add a new delegated role with the given keys,
|
||||
// ensuring the keys either already exist, or are added to the map
|
||||
// of delegation keys
|
||||
func (t *SignedTargets) AddDelegation(role *Role, keys []*PublicKey) error {
|
||||
return errors.New("Not Implemented")
|
||||
}
|
||||
|
||||
// ToSigned partially serializes a SignedTargets for further signing
|
||||
func (t *SignedTargets) ToSigned() (*Signed, error) {
|
||||
s, err := defaultSerializer.MarshalCanonical(t.Signed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signed := json.RawMessage{}
|
||||
err = signed.UnmarshalJSON(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sigs := make([]Signature, len(t.Signatures))
|
||||
copy(sigs, t.Signatures)
|
||||
return &Signed{
|
||||
Signatures: sigs,
|
||||
Signed: &signed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the serialized form of SignedTargets as bytes
|
||||
func (t *SignedTargets) MarshalJSON() ([]byte, error) {
|
||||
signed, err := t.ToSigned()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return defaultSerializer.Marshal(signed)
|
||||
}
|
||||
|
||||
// TargetsFromSigned fully unpacks a Signed object into a SignedTargets, given
|
||||
// a role name (so it can validate the SignedTargets object)
|
||||
func TargetsFromSigned(s *Signed, roleName RoleName) (*SignedTargets, error) {
|
||||
t := Targets{}
|
||||
if err := defaultSerializer.Unmarshal(*s.Signed, &t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := isValidTargetsStructure(t, roleName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sigs := make([]Signature, len(s.Signatures))
|
||||
copy(sigs, s.Signatures)
|
||||
return &SignedTargets{
|
||||
Signatures: sigs,
|
||||
Signed: t,
|
||||
}, nil
|
||||
}
|
136
vendor/github.com/theupdateframework/notary/tuf/data/timestamp.go
generated
vendored
Normal file
136
vendor/github.com/theupdateframework/notary/tuf/data/timestamp.go
generated
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/go/canonical/json"
|
||||
"github.com/theupdateframework/notary"
|
||||
)
|
||||
|
||||
// SignedTimestamp is a fully unpacked timestamp.json
|
||||
type SignedTimestamp struct {
|
||||
Signatures []Signature
|
||||
Signed Timestamp
|
||||
Dirty bool
|
||||
}
|
||||
|
||||
// Timestamp is the Signed component of a timestamp.json
|
||||
type Timestamp struct {
|
||||
SignedCommon
|
||||
Meta Files `json:"meta"`
|
||||
}
|
||||
|
||||
// IsValidTimestampStructure returns an error, or nil, depending on whether the content of the struct
|
||||
// is valid for timestamp metadata. This does not check signatures or expiry, just that
|
||||
// the metadata content is valid.
|
||||
func IsValidTimestampStructure(t Timestamp) error {
|
||||
expectedType := TUFTypes[CanonicalTimestampRole]
|
||||
if t.Type != expectedType {
|
||||
return ErrInvalidMetadata{
|
||||
role: CanonicalTimestampRole, msg: fmt.Sprintf("expected type %s, not %s", expectedType, t.Type)}
|
||||
}
|
||||
|
||||
if t.Version < 1 {
|
||||
return ErrInvalidMetadata{
|
||||
role: CanonicalTimestampRole, msg: "version cannot be less than one"}
|
||||
}
|
||||
|
||||
// Meta is a map of FileMeta, so if the role isn't in the map it returns
|
||||
// an empty FileMeta, which has an empty map, and you can check on keys
|
||||
// from an empty map.
|
||||
//
|
||||
// For now sha256 is required and sha512 is not.
|
||||
if _, ok := t.Meta[CanonicalSnapshotRole.String()].Hashes[notary.SHA256]; !ok {
|
||||
return ErrInvalidMetadata{
|
||||
role: CanonicalTimestampRole, msg: "missing snapshot sha256 checksum information"}
|
||||
}
|
||||
if err := CheckValidHashStructures(t.Meta[CanonicalSnapshotRole.String()].Hashes); err != nil {
|
||||
return ErrInvalidMetadata{
|
||||
role: CanonicalTimestampRole, msg: fmt.Sprintf("invalid snapshot checksum information, %v", err)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewTimestamp initializes a timestamp with an existing snapshot
|
||||
func NewTimestamp(snapshot *Signed) (*SignedTimestamp, error) {
|
||||
snapshotJSON, err := json.Marshal(snapshot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snapshotMeta, err := NewFileMeta(bytes.NewReader(snapshotJSON), NotaryDefaultHashes...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SignedTimestamp{
|
||||
Signatures: make([]Signature, 0),
|
||||
Signed: Timestamp{
|
||||
SignedCommon: SignedCommon{
|
||||
Type: TUFTypes[CanonicalTimestampRole],
|
||||
Version: 0,
|
||||
Expires: DefaultExpires(CanonicalTimestampRole),
|
||||
},
|
||||
Meta: Files{
|
||||
CanonicalSnapshotRole.String(): snapshotMeta,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ToSigned partially serializes a SignedTimestamp such that it can
|
||||
// be signed
|
||||
func (ts *SignedTimestamp) ToSigned() (*Signed, error) {
|
||||
s, err := defaultSerializer.MarshalCanonical(ts.Signed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signed := json.RawMessage{}
|
||||
err = signed.UnmarshalJSON(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sigs := make([]Signature, len(ts.Signatures))
|
||||
copy(sigs, ts.Signatures)
|
||||
return &Signed{
|
||||
Signatures: sigs,
|
||||
Signed: &signed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetSnapshot gets the expected snapshot metadata hashes in the timestamp metadata,
|
||||
// or nil if it doesn't exist
|
||||
func (ts *SignedTimestamp) GetSnapshot() (*FileMeta, error) {
|
||||
snapshotExpected, ok := ts.Signed.Meta[CanonicalSnapshotRole.String()]
|
||||
if !ok {
|
||||
return nil, ErrMissingMeta{Role: CanonicalSnapshotRole.String()}
|
||||
}
|
||||
return &snapshotExpected, nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the serialized form of SignedTimestamp as bytes
|
||||
func (ts *SignedTimestamp) MarshalJSON() ([]byte, error) {
|
||||
signed, err := ts.ToSigned()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return defaultSerializer.Marshal(signed)
|
||||
}
|
||||
|
||||
// TimestampFromSigned parsed a Signed object into a fully unpacked
|
||||
// SignedTimestamp
|
||||
func TimestampFromSigned(s *Signed) (*SignedTimestamp, error) {
|
||||
ts := Timestamp{}
|
||||
if err := defaultSerializer.Unmarshal(*s.Signed, &ts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := IsValidTimestampStructure(ts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sigs := make([]Signature, len(s.Signatures))
|
||||
copy(sigs, s.Signatures)
|
||||
return &SignedTimestamp{
|
||||
Signatures: sigs,
|
||||
Signed: ts,
|
||||
}, nil
|
||||
}
|
390
vendor/github.com/theupdateframework/notary/tuf/data/types.go
generated
vendored
Normal file
390
vendor/github.com/theupdateframework/notary/tuf/data/types.go
generated
vendored
Normal file
@ -0,0 +1,390 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go/canonical/json"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary"
|
||||
)
|
||||
|
||||
// GUN is a Globally Unique Name. It is used to identify trust collections.
|
||||
// An example usage of this is for container image repositories.
|
||||
// For example: myregistry.io/myuser/myimage
|
||||
type GUN string
|
||||
|
||||
func (g GUN) String() string {
|
||||
return string(g)
|
||||
}
|
||||
|
||||
// RoleName type for specifying role
|
||||
type RoleName string
|
||||
|
||||
func (r RoleName) String() string {
|
||||
return string(r)
|
||||
}
|
||||
|
||||
// Parent provides the parent path role from the provided child role
|
||||
func (r RoleName) Parent() RoleName {
|
||||
return RoleName(path.Dir(r.String()))
|
||||
}
|
||||
|
||||
// MetadataRoleMapToStringMap generates a map string of bytes from a map RoleName of bytes
|
||||
func MetadataRoleMapToStringMap(roles map[RoleName][]byte) map[string][]byte {
|
||||
metadata := make(map[string][]byte)
|
||||
for k, v := range roles {
|
||||
metadata[k.String()] = v
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
// NewRoleList generates an array of RoleName objects from a slice of strings
|
||||
func NewRoleList(roles []string) []RoleName {
|
||||
var roleNames []RoleName
|
||||
for _, role := range roles {
|
||||
roleNames = append(roleNames, RoleName(role))
|
||||
}
|
||||
return roleNames
|
||||
}
|
||||
|
||||
// RolesListToStringList generates an array of string objects from a slice of roles
|
||||
func RolesListToStringList(roles []RoleName) []string {
|
||||
var roleNames []string
|
||||
for _, role := range roles {
|
||||
roleNames = append(roleNames, role.String())
|
||||
}
|
||||
return roleNames
|
||||
}
|
||||
|
||||
// SigAlgorithm for types of signatures
|
||||
type SigAlgorithm string
|
||||
|
||||
func (k SigAlgorithm) String() string {
|
||||
return string(k)
|
||||
}
|
||||
|
||||
const defaultHashAlgorithm = "sha256"
|
||||
|
||||
// NotaryDefaultExpiries is the construct used to configure the default expiry times of
|
||||
// the various role files.
|
||||
var NotaryDefaultExpiries = map[RoleName]time.Duration{
|
||||
CanonicalRootRole: notary.NotaryRootExpiry,
|
||||
CanonicalTargetsRole: notary.NotaryTargetsExpiry,
|
||||
CanonicalSnapshotRole: notary.NotarySnapshotExpiry,
|
||||
CanonicalTimestampRole: notary.NotaryTimestampExpiry,
|
||||
}
|
||||
|
||||
// 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[RoleName]string{
|
||||
CanonicalRootRole: "Root",
|
||||
CanonicalTargetsRole: "Targets",
|
||||
CanonicalSnapshotRole: "Snapshot",
|
||||
CanonicalTimestampRole: "Timestamp",
|
||||
}
|
||||
|
||||
// ValidTUFType checks if the given type is valid for the role
|
||||
func ValidTUFType(typ string, role RoleName) bool {
|
||||
if ValidRole(role) {
|
||||
// All targets delegation roles must have
|
||||
// the valid type is for targets.
|
||||
if role == "" {
|
||||
// role is unknown and does not map to
|
||||
// a type
|
||||
return false
|
||||
}
|
||||
if strings.HasPrefix(role.String(), CanonicalTargetsRole.String()+"/") {
|
||||
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"`
|
||||
IsValid bool `json:"-"`
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// NotaryDefaultHashes contains the default supported hash algorithms.
|
||||
var NotaryDefaultHashes = []string{notary.SHA256, notary.SHA512}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// Equals returns true if the other FileMeta object is equivalent to this one
|
||||
func (f FileMeta) Equals(o FileMeta) bool {
|
||||
if o.Length != f.Length || len(o.Hashes) != len(f.Hashes) {
|
||||
return false
|
||||
}
|
||||
if f.Custom == nil && o.Custom != nil || f.Custom != nil && o.Custom == nil {
|
||||
return false
|
||||
}
|
||||
// we don't care if these are valid hashes, just that they are equal
|
||||
for key, val := range f.Hashes {
|
||||
if !bytes.Equal(val, o.Hashes[key]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if f.Custom == nil && o.Custom == nil {
|
||||
return true
|
||||
}
|
||||
fBytes, err := f.Custom.MarshalJSON()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
oBytes, err := o.Custom.MarshalJSON()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(fBytes, oBytes)
|
||||
}
|
||||
|
||||
// CheckHashes verifies all the checksums specified by the "hashes" of the payload.
|
||||
func CheckHashes(payload []byte, name string, hashes Hashes) error {
|
||||
cnt := 0
|
||||
|
||||
// k, v indicate the hash algorithm and the corresponding value
|
||||
for k, v := range hashes {
|
||||
switch k {
|
||||
case notary.SHA256:
|
||||
checksum := sha256.Sum256(payload)
|
||||
if subtle.ConstantTimeCompare(checksum[:], v) == 0 {
|
||||
return ErrMismatchedChecksum{alg: notary.SHA256, name: name, expected: hex.EncodeToString(v)}
|
||||
}
|
||||
cnt++
|
||||
case notary.SHA512:
|
||||
checksum := sha512.Sum512(payload)
|
||||
if subtle.ConstantTimeCompare(checksum[:], v) == 0 {
|
||||
return ErrMismatchedChecksum{alg: notary.SHA512, name: name, expected: hex.EncodeToString(v)}
|
||||
}
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
|
||||
if cnt == 0 {
|
||||
return ErrMissingMeta{Role: name}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CompareMultiHashes verifies that the two Hashes passed in can represent the same data.
|
||||
// This means that both maps must have at least one key defined for which they map, and no conflicts.
|
||||
// Note that we check the intersection of map keys, which adds support for non-default hash algorithms in notary
|
||||
func CompareMultiHashes(hashes1, hashes2 Hashes) error {
|
||||
// First check if the two hash structures are valid
|
||||
if err := CheckValidHashStructures(hashes1); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := CheckValidHashStructures(hashes2); err != nil {
|
||||
return err
|
||||
}
|
||||
// Check if they have at least one matching hash, and no conflicts
|
||||
cnt := 0
|
||||
for hashAlg, hash1 := range hashes1 {
|
||||
|
||||
hash2, ok := hashes2[hashAlg]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if subtle.ConstantTimeCompare(hash1[:], hash2[:]) == 0 {
|
||||
return fmt.Errorf("mismatched %s checksum", hashAlg)
|
||||
}
|
||||
// If we reached here, we had a match
|
||||
cnt++
|
||||
}
|
||||
|
||||
if cnt == 0 {
|
||||
return fmt.Errorf("at least one matching hash needed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckValidHashStructures returns an error, or nil, depending on whether
|
||||
// the content of the hashes is valid or not.
|
||||
func CheckValidHashStructures(hashes Hashes) error {
|
||||
cnt := 0
|
||||
|
||||
for k, v := range hashes {
|
||||
switch k {
|
||||
case notary.SHA256:
|
||||
if len(v) != sha256.Size {
|
||||
return ErrInvalidChecksum{alg: notary.SHA256}
|
||||
}
|
||||
cnt++
|
||||
case notary.SHA512:
|
||||
if len(v) != sha512.Size {
|
||||
return ErrInvalidChecksum{alg: notary.SHA512}
|
||||
}
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
|
||||
if cnt == 0 {
|
||||
return fmt.Errorf("at least one supported hash needed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 notary.SHA256:
|
||||
h = sha256.New()
|
||||
case notary.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),
|
||||
}
|
||||
}
|
||||
|
||||
// These values are recommended TUF expiry times.
|
||||
var defaultExpiryTimes = map[RoleName]time.Duration{
|
||||
CanonicalRootRole: notary.Year,
|
||||
CanonicalTargetsRole: 90 * notary.Day,
|
||||
CanonicalSnapshotRole: 7 * notary.Day,
|
||||
CanonicalTimestampRole: notary.Day,
|
||||
}
|
||||
|
||||
// SetDefaultExpiryTimes allows one to change the default expiries.
|
||||
func SetDefaultExpiryTimes(times map[RoleName]time.Duration) {
|
||||
for key, value := range times {
|
||||
if _, ok := defaultExpiryTimes[key]; !ok {
|
||||
logrus.Errorf("Attempted to set default expiry for an unknown role: %s", key.String())
|
||||
continue
|
||||
}
|
||||
defaultExpiryTimes[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultExpires gets the default expiry time for the given role
|
||||
func DefaultExpires(role RoleName) time.Time {
|
||||
if d, ok := defaultExpiryTimes[role]; ok {
|
||||
return time.Now().Add(d)
|
||||
}
|
||||
var t time.Time
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user