mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-09 21:17:09 +08:00
vendor: initial vendor
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
100
vendor/github.com/theupdateframework/notary/client/changelist/change.go
generated
vendored
Normal file
100
vendor/github.com/theupdateframework/notary/client/changelist/change.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
package changelist
|
||||
|
||||
import (
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
||||
// Scopes for TUFChanges are simply the TUF roles.
|
||||
// Unfortunately because of targets delegations, we can only
|
||||
// cover the base roles.
|
||||
const (
|
||||
ScopeRoot = "root"
|
||||
ScopeTargets = "targets"
|
||||
)
|
||||
|
||||
// Types for TUFChanges are namespaced by the Role they
|
||||
// are relevant for. The Root and Targets roles are the
|
||||
// only ones for which user action can cause a change, as
|
||||
// all changes in Snapshot and Timestamp are programmatically
|
||||
// generated base on Root and Targets changes.
|
||||
const (
|
||||
TypeBaseRole = "role"
|
||||
TypeTargetsTarget = "target"
|
||||
TypeTargetsDelegation = "delegation"
|
||||
TypeWitness = "witness"
|
||||
)
|
||||
|
||||
// TUFChange represents a change to a TUF repo
|
||||
type TUFChange struct {
|
||||
// Abbreviated because Go doesn't permit a field and method of the same name
|
||||
Actn string `json:"action"`
|
||||
Role data.RoleName `json:"role"`
|
||||
ChangeType string `json:"type"`
|
||||
ChangePath string `json:"path"`
|
||||
Data []byte `json:"data"`
|
||||
}
|
||||
|
||||
// TUFRootData represents a modification of the keys associated
|
||||
// with a role that appears in the root.json
|
||||
type TUFRootData struct {
|
||||
Keys data.KeyList `json:"keys"`
|
||||
RoleName data.RoleName `json:"role"`
|
||||
}
|
||||
|
||||
// NewTUFChange initializes a TUFChange object
|
||||
func NewTUFChange(action string, role data.RoleName, changeType, changePath string, content []byte) *TUFChange {
|
||||
return &TUFChange{
|
||||
Actn: action,
|
||||
Role: role,
|
||||
ChangeType: changeType,
|
||||
ChangePath: changePath,
|
||||
Data: content,
|
||||
}
|
||||
}
|
||||
|
||||
// Action return c.Actn
|
||||
func (c TUFChange) Action() string {
|
||||
return c.Actn
|
||||
}
|
||||
|
||||
// Scope returns c.Role
|
||||
func (c TUFChange) Scope() data.RoleName {
|
||||
return c.Role
|
||||
}
|
||||
|
||||
// Type returns c.ChangeType
|
||||
func (c TUFChange) Type() string {
|
||||
return c.ChangeType
|
||||
}
|
||||
|
||||
// Path return c.ChangePath
|
||||
func (c TUFChange) Path() string {
|
||||
return c.ChangePath
|
||||
}
|
||||
|
||||
// Content returns c.Data
|
||||
func (c TUFChange) Content() []byte {
|
||||
return c.Data
|
||||
}
|
||||
|
||||
// TUFDelegation represents a modification to a target delegation
|
||||
// this includes creating a delegations. This format is used to avoid
|
||||
// unexpected race conditions between humans modifying the same delegation
|
||||
type TUFDelegation struct {
|
||||
NewName data.RoleName `json:"new_name,omitempty"`
|
||||
NewThreshold int `json:"threshold,omitempty"`
|
||||
AddKeys data.KeyList `json:"add_keys,omitempty"`
|
||||
RemoveKeys []string `json:"remove_keys,omitempty"`
|
||||
AddPaths []string `json:"add_paths,omitempty"`
|
||||
RemovePaths []string `json:"remove_paths,omitempty"`
|
||||
ClearAllPaths bool `json:"clear_paths,omitempty"`
|
||||
}
|
||||
|
||||
// ToNewRole creates a fresh role object from the TUFDelegation data
|
||||
func (td TUFDelegation) ToNewRole(scope data.RoleName) (*data.Role, error) {
|
||||
name := scope
|
||||
if td.NewName != "" {
|
||||
name = td.NewName
|
||||
}
|
||||
return data.NewRole(name, td.NewThreshold, td.AddKeys.IDs(), td.AddPaths)
|
||||
}
|
82
vendor/github.com/theupdateframework/notary/client/changelist/changelist.go
generated
vendored
Normal file
82
vendor/github.com/theupdateframework/notary/client/changelist/changelist.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
package changelist
|
||||
|
||||
// memChangeList implements a simple in memory change list.
|
||||
type memChangelist struct {
|
||||
changes []Change
|
||||
}
|
||||
|
||||
// NewMemChangelist instantiates a new in-memory changelist
|
||||
func NewMemChangelist() Changelist {
|
||||
return &memChangelist{}
|
||||
}
|
||||
|
||||
// List returns a list of Changes
|
||||
func (cl memChangelist) List() []Change {
|
||||
return cl.changes
|
||||
}
|
||||
|
||||
// Add adds a change to the in-memory change list
|
||||
func (cl *memChangelist) Add(c Change) error {
|
||||
cl.changes = append(cl.changes, c)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Location returns the string "memory"
|
||||
func (cl memChangelist) Location() string {
|
||||
return "memory"
|
||||
}
|
||||
|
||||
// Remove deletes the changes found at the given indices
|
||||
func (cl *memChangelist) Remove(idxs []int) error {
|
||||
remove := make(map[int]struct{})
|
||||
for _, i := range idxs {
|
||||
remove[i] = struct{}{}
|
||||
}
|
||||
var keep []Change
|
||||
|
||||
for i, c := range cl.changes {
|
||||
if _, ok := remove[i]; ok {
|
||||
continue
|
||||
}
|
||||
keep = append(keep, c)
|
||||
}
|
||||
cl.changes = keep
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clear empties the changelist file.
|
||||
func (cl *memChangelist) Clear(archive string) error {
|
||||
// appending to a nil list initializes it.
|
||||
cl.changes = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close is a no-op in this in-memory change-list
|
||||
func (cl *memChangelist) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cl *memChangelist) NewIterator() (ChangeIterator, error) {
|
||||
return &MemChangeListIterator{index: 0, collection: cl.changes}, nil
|
||||
}
|
||||
|
||||
// MemChangeListIterator is a concrete instance of ChangeIterator
|
||||
type MemChangeListIterator struct {
|
||||
index int
|
||||
collection []Change // Same type as memChangeList.changes
|
||||
}
|
||||
|
||||
// Next returns the next Change
|
||||
func (m *MemChangeListIterator) Next() (item Change, err error) {
|
||||
if m.index >= len(m.collection) {
|
||||
return nil, IteratorBoundsError(m.index)
|
||||
}
|
||||
item = m.collection[m.index]
|
||||
m.index++
|
||||
return item, err
|
||||
}
|
||||
|
||||
// HasNext indicates whether the iterator is exhausted
|
||||
func (m *MemChangeListIterator) HasNext() bool {
|
||||
return m.index < len(m.collection)
|
||||
}
|
202
vendor/github.com/theupdateframework/notary/client/changelist/file_changelist.go
generated
vendored
Normal file
202
vendor/github.com/theupdateframework/notary/client/changelist/file_changelist.go
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
package changelist
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// FileChangelist stores all the changes as files
|
||||
type FileChangelist struct {
|
||||
dir string
|
||||
}
|
||||
|
||||
// NewFileChangelist is a convenience method for returning FileChangeLists
|
||||
func NewFileChangelist(dir string) (*FileChangelist, error) {
|
||||
logrus.Debug("Making dir path: ", dir)
|
||||
err := os.MkdirAll(dir, 0700)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &FileChangelist{dir: dir}, nil
|
||||
}
|
||||
|
||||
// getFileNames reads directory, filtering out child directories
|
||||
func getFileNames(dirName string) ([]os.FileInfo, error) {
|
||||
var dirListing, fileInfos []os.FileInfo
|
||||
dir, err := os.Open(dirName)
|
||||
if err != nil {
|
||||
return fileInfos, err
|
||||
}
|
||||
defer dir.Close()
|
||||
dirListing, err = dir.Readdir(0)
|
||||
if err != nil {
|
||||
return fileInfos, err
|
||||
}
|
||||
for _, f := range dirListing {
|
||||
if f.IsDir() {
|
||||
continue
|
||||
}
|
||||
fileInfos = append(fileInfos, f)
|
||||
}
|
||||
sort.Sort(fileChanges(fileInfos))
|
||||
return fileInfos, nil
|
||||
}
|
||||
|
||||
// Read a JSON formatted file from disk; convert to TUFChange struct
|
||||
func unmarshalFile(dirname string, f os.FileInfo) (*TUFChange, error) {
|
||||
c := &TUFChange{}
|
||||
raw, err := ioutil.ReadFile(filepath.Join(dirname, f.Name()))
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
err = json.Unmarshal(raw, c)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// List returns a list of sorted changes
|
||||
func (cl FileChangelist) List() []Change {
|
||||
var changes []Change
|
||||
fileInfos, err := getFileNames(cl.dir)
|
||||
if err != nil {
|
||||
return changes
|
||||
}
|
||||
for _, f := range fileInfos {
|
||||
c, err := unmarshalFile(cl.dir, f)
|
||||
if err != nil {
|
||||
logrus.Warn(err.Error())
|
||||
continue
|
||||
}
|
||||
changes = append(changes, c)
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
// Add adds a change to the file change list
|
||||
func (cl FileChangelist) Add(c Change) error {
|
||||
cJSON, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filename := fmt.Sprintf("%020d_%s.change", time.Now().UnixNano(), uuid.Generate())
|
||||
return ioutil.WriteFile(filepath.Join(cl.dir, filename), cJSON, 0644)
|
||||
}
|
||||
|
||||
// Remove deletes the changes found at the given indices
|
||||
func (cl FileChangelist) Remove(idxs []int) error {
|
||||
fileInfos, err := getFileNames(cl.dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
remove := make(map[int]struct{})
|
||||
for _, i := range idxs {
|
||||
remove[i] = struct{}{}
|
||||
}
|
||||
for i, c := range fileInfos {
|
||||
if _, ok := remove[i]; ok {
|
||||
file := filepath.Join(cl.dir, c.Name())
|
||||
if err := os.Remove(file); err != nil {
|
||||
logrus.Errorf("could not remove change %d: %s", i, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clear clears the change list
|
||||
// N.B. archiving not currently implemented
|
||||
func (cl FileChangelist) Clear(archive string) error {
|
||||
dir, err := os.Open(cl.dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dir.Close()
|
||||
files, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, f := range files {
|
||||
os.Remove(filepath.Join(cl.dir, f.Name()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close is a no-op
|
||||
func (cl FileChangelist) Close() error {
|
||||
// Nothing to do here
|
||||
return nil
|
||||
}
|
||||
|
||||
// Location returns the file path to the changelist
|
||||
func (cl FileChangelist) Location() string {
|
||||
return cl.dir
|
||||
}
|
||||
|
||||
// NewIterator creates an iterator from FileChangelist
|
||||
func (cl FileChangelist) NewIterator() (ChangeIterator, error) {
|
||||
fileInfos, err := getFileNames(cl.dir)
|
||||
if err != nil {
|
||||
return &FileChangeListIterator{}, err
|
||||
}
|
||||
return &FileChangeListIterator{dirname: cl.dir, collection: fileInfos}, nil
|
||||
}
|
||||
|
||||
// IteratorBoundsError is an Error type used by Next()
|
||||
type IteratorBoundsError int
|
||||
|
||||
// Error implements the Error interface
|
||||
func (e IteratorBoundsError) Error() string {
|
||||
return fmt.Sprintf("Iterator index (%d) out of bounds", e)
|
||||
}
|
||||
|
||||
// FileChangeListIterator is a concrete instance of ChangeIterator
|
||||
type FileChangeListIterator struct {
|
||||
index int
|
||||
dirname string
|
||||
collection []os.FileInfo
|
||||
}
|
||||
|
||||
// Next returns the next Change in the FileChangeList
|
||||
func (m *FileChangeListIterator) Next() (item Change, err error) {
|
||||
if m.index >= len(m.collection) {
|
||||
return nil, IteratorBoundsError(m.index)
|
||||
}
|
||||
f := m.collection[m.index]
|
||||
m.index++
|
||||
item, err = unmarshalFile(m.dirname, f)
|
||||
return
|
||||
}
|
||||
|
||||
// HasNext indicates whether iterator is exhausted
|
||||
func (m *FileChangeListIterator) HasNext() bool {
|
||||
return m.index < len(m.collection)
|
||||
}
|
||||
|
||||
type fileChanges []os.FileInfo
|
||||
|
||||
// Len returns the length of a file change list
|
||||
func (cs fileChanges) Len() int {
|
||||
return len(cs)
|
||||
}
|
||||
|
||||
// Less compares the names of two different file changes
|
||||
func (cs fileChanges) Less(i, j int) bool {
|
||||
return cs[i].Name() < cs[j].Name()
|
||||
}
|
||||
|
||||
// Swap swaps the position of two file changes
|
||||
func (cs fileChanges) Swap(i, j int) {
|
||||
tmp := cs[i]
|
||||
cs[i] = cs[j]
|
||||
cs[j] = tmp
|
||||
}
|
78
vendor/github.com/theupdateframework/notary/client/changelist/interface.go
generated
vendored
Normal file
78
vendor/github.com/theupdateframework/notary/client/changelist/interface.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
package changelist
|
||||
|
||||
import "github.com/theupdateframework/notary/tuf/data"
|
||||
|
||||
// Changelist is the interface for all TUF change lists
|
||||
type Changelist interface {
|
||||
// List returns the ordered list of changes
|
||||
// currently stored
|
||||
List() []Change
|
||||
|
||||
// Add change appends the provided change to
|
||||
// the list of changes
|
||||
Add(Change) error
|
||||
|
||||
// Clear empties the current change list.
|
||||
// Archive may be provided as a directory path
|
||||
// to save a copy of the changelist in that location
|
||||
Clear(archive string) error
|
||||
|
||||
// Remove deletes the changes corresponding with the indices given
|
||||
Remove(idxs []int) error
|
||||
|
||||
// Close syncronizes any pending writes to the underlying
|
||||
// storage and closes the file/connection
|
||||
Close() error
|
||||
|
||||
// NewIterator returns an iterator for walking through the list
|
||||
// of changes currently stored
|
||||
NewIterator() (ChangeIterator, error)
|
||||
|
||||
// Location returns the place the changelist is stores
|
||||
Location() string
|
||||
}
|
||||
|
||||
const (
|
||||
// ActionCreate represents a Create action
|
||||
ActionCreate = "create"
|
||||
// ActionUpdate represents an Update action
|
||||
ActionUpdate = "update"
|
||||
// ActionDelete represents a Delete action
|
||||
ActionDelete = "delete"
|
||||
)
|
||||
|
||||
// Change is the interface for a TUF Change
|
||||
type Change interface {
|
||||
// "create","update", or "delete"
|
||||
Action() string
|
||||
|
||||
// Where the change should be made.
|
||||
// For TUF this will be the role
|
||||
Scope() data.RoleName
|
||||
|
||||
// The content type being affected.
|
||||
// For TUF this will be "target", or "delegation".
|
||||
// If the type is "delegation", the Scope will be
|
||||
// used to determine if a root role is being updated
|
||||
// or a target delegation.
|
||||
Type() string
|
||||
|
||||
// Path indicates the entry within a role to be affected by the
|
||||
// change. For targets, this is simply the target's path,
|
||||
// for delegations it's the delegated role name.
|
||||
Path() string
|
||||
|
||||
// Serialized content that the interpreter of a changelist
|
||||
// can use to apply the change.
|
||||
// For TUF this will be the serialized JSON that needs
|
||||
// to be inserted or merged. In the case of a "delete"
|
||||
// action, it will be nil.
|
||||
Content() []byte
|
||||
}
|
||||
|
||||
// ChangeIterator is the interface for iterating across collections of
|
||||
// TUF Change items
|
||||
type ChangeIterator interface {
|
||||
Next() (Change, error)
|
||||
HasNext() bool
|
||||
}
|
1290
vendor/github.com/theupdateframework/notary/client/client.go
generated
vendored
Normal file
1290
vendor/github.com/theupdateframework/notary/client/client.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
262
vendor/github.com/theupdateframework/notary/client/delegations.go
generated
vendored
Normal file
262
vendor/github.com/theupdateframework/notary/client/delegations.go
generated
vendored
Normal file
@ -0,0 +1,262 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary"
|
||||
"github.com/theupdateframework/notary/client/changelist"
|
||||
store "github.com/theupdateframework/notary/storage"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"github.com/theupdateframework/notary/tuf/utils"
|
||||
)
|
||||
|
||||
// AddDelegation creates changelist entries to add provided delegation public keys and paths.
|
||||
// This method composes AddDelegationRoleAndKeys and AddDelegationPaths (each creates one changelist if called).
|
||||
func (r *repository) AddDelegation(name data.RoleName, delegationKeys []data.PublicKey, paths []string) error {
|
||||
if len(delegationKeys) > 0 {
|
||||
err := r.AddDelegationRoleAndKeys(name, delegationKeys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(paths) > 0 {
|
||||
err := r.AddDelegationPaths(name, paths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddDelegationRoleAndKeys creates a changelist entry to add provided delegation public keys.
|
||||
// This method is the simplest way to create a new delegation, because the delegation must have at least
|
||||
// one key upon creation to be valid since we will reject the changelist while validating the threshold.
|
||||
func (r *repository) AddDelegationRoleAndKeys(name data.RoleName, delegationKeys []data.PublicKey) error {
|
||||
|
||||
if !data.IsDelegation(name) {
|
||||
return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"}
|
||||
}
|
||||
|
||||
logrus.Debugf(`Adding delegation "%s" with threshold %d, and %d keys\n`,
|
||||
name, notary.MinThreshold, len(delegationKeys))
|
||||
|
||||
// Defaulting to threshold of 1, since we don't allow for larger thresholds at the moment.
|
||||
tdJSON, err := json.Marshal(&changelist.TUFDelegation{
|
||||
NewThreshold: notary.MinThreshold,
|
||||
AddKeys: data.KeyList(delegationKeys),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template := newCreateDelegationChange(name, tdJSON)
|
||||
return addChange(r.changelist, template, name)
|
||||
}
|
||||
|
||||
// AddDelegationPaths creates a changelist entry to add provided paths to an existing delegation.
|
||||
// This method cannot create a new delegation itself because the role must meet the key threshold upon creation.
|
||||
func (r *repository) AddDelegationPaths(name data.RoleName, paths []string) error {
|
||||
|
||||
if !data.IsDelegation(name) {
|
||||
return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"}
|
||||
}
|
||||
|
||||
logrus.Debugf(`Adding %s paths to delegation %s\n`, paths, name)
|
||||
|
||||
tdJSON, err := json.Marshal(&changelist.TUFDelegation{
|
||||
AddPaths: paths,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template := newCreateDelegationChange(name, tdJSON)
|
||||
return addChange(r.changelist, template, name)
|
||||
}
|
||||
|
||||
// RemoveDelegationKeysAndPaths creates changelist entries to remove provided delegation key IDs and paths.
|
||||
// This method composes RemoveDelegationPaths and RemoveDelegationKeys (each creates one changelist if called).
|
||||
func (r *repository) RemoveDelegationKeysAndPaths(name data.RoleName, keyIDs, paths []string) error {
|
||||
if len(paths) > 0 {
|
||||
err := r.RemoveDelegationPaths(name, paths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(keyIDs) > 0 {
|
||||
err := r.RemoveDelegationKeys(name, keyIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveDelegationRole creates a changelist to remove all paths and keys from a role, and delete the role in its entirety.
|
||||
func (r *repository) RemoveDelegationRole(name data.RoleName) error {
|
||||
|
||||
if !data.IsDelegation(name) {
|
||||
return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"}
|
||||
}
|
||||
|
||||
logrus.Debugf(`Removing delegation "%s"\n`, name)
|
||||
|
||||
template := newDeleteDelegationChange(name, nil)
|
||||
return addChange(r.changelist, template, name)
|
||||
}
|
||||
|
||||
// RemoveDelegationPaths creates a changelist entry to remove provided paths from an existing delegation.
|
||||
func (r *repository) RemoveDelegationPaths(name data.RoleName, paths []string) error {
|
||||
|
||||
if !data.IsDelegation(name) {
|
||||
return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"}
|
||||
}
|
||||
|
||||
logrus.Debugf(`Removing %s paths from delegation "%s"\n`, paths, name)
|
||||
|
||||
tdJSON, err := json.Marshal(&changelist.TUFDelegation{
|
||||
RemovePaths: paths,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template := newUpdateDelegationChange(name, tdJSON)
|
||||
return addChange(r.changelist, template, name)
|
||||
}
|
||||
|
||||
// RemoveDelegationKeys creates a changelist entry to remove provided keys from an existing delegation.
|
||||
// When this changelist is applied, if the specified keys are the only keys left in the role,
|
||||
// the role itself will be deleted in its entirety.
|
||||
// It can also delete a key from all delegations under a parent using a name
|
||||
// with a wildcard at the end.
|
||||
func (r *repository) RemoveDelegationKeys(name data.RoleName, keyIDs []string) error {
|
||||
|
||||
if !data.IsDelegation(name) && !data.IsWildDelegation(name) {
|
||||
return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"}
|
||||
}
|
||||
|
||||
logrus.Debugf(`Removing %s keys from delegation "%s"\n`, keyIDs, name)
|
||||
|
||||
tdJSON, err := json.Marshal(&changelist.TUFDelegation{
|
||||
RemoveKeys: keyIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template := newUpdateDelegationChange(name, tdJSON)
|
||||
return addChange(r.changelist, template, name)
|
||||
}
|
||||
|
||||
// ClearDelegationPaths creates a changelist entry to remove all paths from an existing delegation.
|
||||
func (r *repository) ClearDelegationPaths(name data.RoleName) error {
|
||||
|
||||
if !data.IsDelegation(name) {
|
||||
return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"}
|
||||
}
|
||||
|
||||
logrus.Debugf(`Removing all paths from delegation "%s"\n`, name)
|
||||
|
||||
tdJSON, err := json.Marshal(&changelist.TUFDelegation{
|
||||
ClearAllPaths: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template := newUpdateDelegationChange(name, tdJSON)
|
||||
return addChange(r.changelist, template, name)
|
||||
}
|
||||
|
||||
func newUpdateDelegationChange(name data.RoleName, content []byte) *changelist.TUFChange {
|
||||
return changelist.NewTUFChange(
|
||||
changelist.ActionUpdate,
|
||||
name,
|
||||
changelist.TypeTargetsDelegation,
|
||||
"", // no path for delegations
|
||||
content,
|
||||
)
|
||||
}
|
||||
|
||||
func newCreateDelegationChange(name data.RoleName, content []byte) *changelist.TUFChange {
|
||||
return changelist.NewTUFChange(
|
||||
changelist.ActionCreate,
|
||||
name,
|
||||
changelist.TypeTargetsDelegation,
|
||||
"", // no path for delegations
|
||||
content,
|
||||
)
|
||||
}
|
||||
|
||||
func newDeleteDelegationChange(name data.RoleName, content []byte) *changelist.TUFChange {
|
||||
return changelist.NewTUFChange(
|
||||
changelist.ActionDelete,
|
||||
name,
|
||||
changelist.TypeTargetsDelegation,
|
||||
"", // no path for delegations
|
||||
content,
|
||||
)
|
||||
}
|
||||
|
||||
// GetDelegationRoles returns the keys and roles of the repository's delegations
|
||||
// Also converts key IDs to canonical key IDs to keep consistent with signing prompts
|
||||
func (r *repository) GetDelegationRoles() ([]data.Role, error) {
|
||||
// Update state of the repo to latest
|
||||
if err := r.Update(false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// All top level delegations (ex: targets/level1) are stored exclusively in targets.json
|
||||
_, ok := r.tufRepo.Targets[data.CanonicalTargetsRole]
|
||||
if !ok {
|
||||
return nil, store.ErrMetaNotFound{Resource: data.CanonicalTargetsRole.String()}
|
||||
}
|
||||
|
||||
// make a copy for traversing nested delegations
|
||||
allDelegations := []data.Role{}
|
||||
|
||||
// Define a visitor function to populate the delegations list and translate their key IDs to canonical IDs
|
||||
delegationCanonicalListVisitor := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
|
||||
// For the return list, update with a copy that includes canonicalKeyIDs
|
||||
// These aren't validated by the validRole
|
||||
canonicalDelegations, err := translateDelegationsToCanonicalIDs(tgt.Signed.Delegations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
allDelegations = append(allDelegations, canonicalDelegations...)
|
||||
return nil
|
||||
}
|
||||
err := r.tufRepo.WalkTargets("", "", delegationCanonicalListVisitor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return allDelegations, nil
|
||||
}
|
||||
|
||||
func translateDelegationsToCanonicalIDs(delegationInfo data.Delegations) ([]data.Role, error) {
|
||||
canonicalDelegations := make([]data.Role, len(delegationInfo.Roles))
|
||||
// Do a copy by value to ensure local delegation metadata is untouched
|
||||
for idx, origRole := range delegationInfo.Roles {
|
||||
canonicalDelegations[idx] = *origRole
|
||||
}
|
||||
delegationKeys := delegationInfo.Keys
|
||||
for i, delegation := range canonicalDelegations {
|
||||
canonicalKeyIDs := []string{}
|
||||
for _, keyID := range delegation.KeyIDs {
|
||||
pubKey, ok := delegationKeys[keyID]
|
||||
if !ok {
|
||||
return []data.Role{}, fmt.Errorf("Could not translate canonical key IDs for %s", delegation.Name)
|
||||
}
|
||||
canonicalKeyID, err := utils.CanonicalKeyID(pubKey)
|
||||
if err != nil {
|
||||
return []data.Role{}, fmt.Errorf("Could not translate canonical key IDs for %s: %v", delegation.Name, err)
|
||||
}
|
||||
canonicalKeyIDs = append(canonicalKeyIDs, canonicalKeyID)
|
||||
}
|
||||
canonicalDelegations[i].KeyIDs = canonicalKeyIDs
|
||||
}
|
||||
return canonicalDelegations, nil
|
||||
}
|
48
vendor/github.com/theupdateframework/notary/client/errors.go
generated
vendored
Normal file
48
vendor/github.com/theupdateframework/notary/client/errors.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
||||
// ErrRepoNotInitialized is returned when trying to publish an uninitialized
|
||||
// notary repository
|
||||
type ErrRepoNotInitialized struct{}
|
||||
|
||||
func (err ErrRepoNotInitialized) Error() string {
|
||||
return "repository has not been initialized"
|
||||
}
|
||||
|
||||
// ErrInvalidRemoteRole is returned when the server is requested to manage
|
||||
// a key type that is not permitted
|
||||
type ErrInvalidRemoteRole struct {
|
||||
Role data.RoleName
|
||||
}
|
||||
|
||||
func (err ErrInvalidRemoteRole) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"notary does not permit the server managing the %s key", err.Role.String())
|
||||
}
|
||||
|
||||
// ErrInvalidLocalRole is returned when the client wants to manage
|
||||
// a key type that is not permitted
|
||||
type ErrInvalidLocalRole struct {
|
||||
Role data.RoleName
|
||||
}
|
||||
|
||||
func (err ErrInvalidLocalRole) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"notary does not permit the client managing the %s key", err.Role)
|
||||
}
|
||||
|
||||
// ErrRepositoryNotExist is returned when an action is taken on a remote
|
||||
// repository that doesn't exist
|
||||
type ErrRepositoryNotExist struct {
|
||||
remote string
|
||||
gun data.GUN
|
||||
}
|
||||
|
||||
func (err ErrRepositoryNotExist) Error() string {
|
||||
return fmt.Sprintf("%s does not have trust data for %s", err.remote, err.gun.String())
|
||||
}
|
306
vendor/github.com/theupdateframework/notary/client/helpers.go
generated
vendored
Normal file
306
vendor/github.com/theupdateframework/notary/client/helpers.go
generated
vendored
Normal file
@ -0,0 +1,306 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary/client/changelist"
|
||||
store "github.com/theupdateframework/notary/storage"
|
||||
"github.com/theupdateframework/notary/tuf"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"github.com/theupdateframework/notary/tuf/signed"
|
||||
"github.com/theupdateframework/notary/tuf/utils"
|
||||
)
|
||||
|
||||
// Use this to initialize remote HTTPStores from the config settings
|
||||
func getRemoteStore(baseURL string, gun data.GUN, rt http.RoundTripper) (store.RemoteStore, error) {
|
||||
s, err := store.NewHTTPStore(
|
||||
baseURL+"/v2/"+gun.String()+"/_trust/tuf/",
|
||||
"",
|
||||
"json",
|
||||
"key",
|
||||
rt,
|
||||
)
|
||||
if err != nil {
|
||||
return store.OfflineStore{}, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func applyChangelist(repo *tuf.Repo, invalid *tuf.Repo, cl changelist.Changelist) error {
|
||||
it, err := cl.NewIterator()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
index := 0
|
||||
for it.HasNext() {
|
||||
c, err := it.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isDel := data.IsDelegation(c.Scope()) || data.IsWildDelegation(c.Scope())
|
||||
switch {
|
||||
case c.Scope() == changelist.ScopeTargets || isDel:
|
||||
err = applyTargetsChange(repo, invalid, c)
|
||||
case c.Scope() == changelist.ScopeRoot:
|
||||
err = applyRootChange(repo, c)
|
||||
default:
|
||||
return fmt.Errorf("scope not supported: %s", c.Scope().String())
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Debugf("error attempting to apply change #%d: %s, on scope: %s path: %s type: %s", index, c.Action(), c.Scope(), c.Path(), c.Type())
|
||||
return err
|
||||
}
|
||||
index++
|
||||
}
|
||||
logrus.Debugf("applied %d change(s)", index)
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyTargetsChange(repo *tuf.Repo, invalid *tuf.Repo, c changelist.Change) error {
|
||||
switch c.Type() {
|
||||
case changelist.TypeTargetsTarget:
|
||||
return changeTargetMeta(repo, c)
|
||||
case changelist.TypeTargetsDelegation:
|
||||
return changeTargetsDelegation(repo, c)
|
||||
case changelist.TypeWitness:
|
||||
return witnessTargets(repo, invalid, c.Scope())
|
||||
default:
|
||||
return fmt.Errorf("only target meta and delegations changes supported")
|
||||
}
|
||||
}
|
||||
|
||||
func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error {
|
||||
switch c.Action() {
|
||||
case changelist.ActionCreate:
|
||||
td := changelist.TUFDelegation{}
|
||||
err := json.Unmarshal(c.Content(), &td)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Try to create brand new role or update one
|
||||
// First add the keys, then the paths. We can only add keys and paths in this scenario
|
||||
err = repo.UpdateDelegationKeys(c.Scope(), td.AddKeys, []string{}, td.NewThreshold)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return repo.UpdateDelegationPaths(c.Scope(), td.AddPaths, []string{}, false)
|
||||
case changelist.ActionUpdate:
|
||||
td := changelist.TUFDelegation{}
|
||||
err := json.Unmarshal(c.Content(), &td)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if data.IsWildDelegation(c.Scope()) {
|
||||
return repo.PurgeDelegationKeys(c.Scope(), td.RemoveKeys)
|
||||
}
|
||||
|
||||
delgRole, err := repo.GetDelegationRole(c.Scope())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We need to translate the keys from canonical ID to TUF ID for compatibility
|
||||
canonicalToTUFID := make(map[string]string)
|
||||
for tufID, pubKey := range delgRole.Keys {
|
||||
canonicalID, err := utils.CanonicalKeyID(pubKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
canonicalToTUFID[canonicalID] = tufID
|
||||
}
|
||||
|
||||
removeTUFKeyIDs := []string{}
|
||||
for _, canonID := range td.RemoveKeys {
|
||||
removeTUFKeyIDs = append(removeTUFKeyIDs, canonicalToTUFID[canonID])
|
||||
}
|
||||
|
||||
err = repo.UpdateDelegationKeys(c.Scope(), td.AddKeys, removeTUFKeyIDs, td.NewThreshold)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return repo.UpdateDelegationPaths(c.Scope(), td.AddPaths, td.RemovePaths, td.ClearAllPaths)
|
||||
case changelist.ActionDelete:
|
||||
return repo.DeleteDelegation(c.Scope())
|
||||
default:
|
||||
return fmt.Errorf("unsupported action against delegations: %s", c.Action())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func changeTargetMeta(repo *tuf.Repo, c changelist.Change) error {
|
||||
var err error
|
||||
switch c.Action() {
|
||||
case changelist.ActionCreate:
|
||||
logrus.Debug("changelist add: ", c.Path())
|
||||
meta := &data.FileMeta{}
|
||||
err = json.Unmarshal(c.Content(), meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
files := data.Files{c.Path(): *meta}
|
||||
|
||||
// Attempt to add the target to this role
|
||||
if _, err = repo.AddTargets(c.Scope(), files); err != nil {
|
||||
logrus.Errorf("couldn't add target to %s: %s", c.Scope(), err.Error())
|
||||
}
|
||||
|
||||
case changelist.ActionDelete:
|
||||
logrus.Debug("changelist remove: ", c.Path())
|
||||
|
||||
// Attempt to remove the target from this role
|
||||
if err = repo.RemoveTargets(c.Scope(), c.Path()); err != nil {
|
||||
logrus.Errorf("couldn't remove target from %s: %s", c.Scope(), err.Error())
|
||||
}
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("action not yet supported: %s", c.Action())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func applyRootChange(repo *tuf.Repo, c changelist.Change) error {
|
||||
var err error
|
||||
switch c.Type() {
|
||||
case changelist.TypeBaseRole:
|
||||
err = applyRootRoleChange(repo, c)
|
||||
default:
|
||||
err = fmt.Errorf("type of root change not yet supported: %s", c.Type())
|
||||
}
|
||||
return err // might be nil
|
||||
}
|
||||
|
||||
func applyRootRoleChange(repo *tuf.Repo, c changelist.Change) error {
|
||||
switch c.Action() {
|
||||
case changelist.ActionCreate:
|
||||
// replaces all keys for a role
|
||||
d := &changelist.TUFRootData{}
|
||||
err := json.Unmarshal(c.Content(), d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = repo.ReplaceBaseKeys(d.RoleName, d.Keys...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("action not yet supported for root: %s", c.Action())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func nearExpiry(r data.SignedCommon) bool {
|
||||
plus6mo := time.Now().AddDate(0, 6, 0)
|
||||
return r.Expires.Before(plus6mo)
|
||||
}
|
||||
|
||||
func warnRolesNearExpiry(r *tuf.Repo) {
|
||||
//get every role and its respective signed common and call nearExpiry on it
|
||||
//Root check
|
||||
if nearExpiry(r.Root.Signed.SignedCommon) {
|
||||
logrus.Warn("root is nearing expiry, you should re-sign the role metadata")
|
||||
}
|
||||
//Targets and delegations check
|
||||
for role, signedTOrD := range r.Targets {
|
||||
//signedTOrD is of type *data.SignedTargets
|
||||
if nearExpiry(signedTOrD.Signed.SignedCommon) {
|
||||
logrus.Warn(role, " metadata is nearing expiry, you should re-sign the role metadata")
|
||||
}
|
||||
}
|
||||
//Snapshot check
|
||||
if nearExpiry(r.Snapshot.Signed.SignedCommon) {
|
||||
logrus.Warn("snapshot is nearing expiry, you should re-sign the role metadata")
|
||||
}
|
||||
//do not need to worry about Timestamp, notary signer will re-sign with the timestamp key
|
||||
}
|
||||
|
||||
// Fetches a public key from a remote store, given a gun and role
|
||||
func getRemoteKey(role data.RoleName, remote store.RemoteStore) (data.PublicKey, error) {
|
||||
rawPubKey, err := remote.GetKey(role)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubKey, err := data.UnmarshalPublicKey(rawPubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pubKey, nil
|
||||
}
|
||||
|
||||
// Rotates a private key in a remote store and returns the public key component
|
||||
func rotateRemoteKey(role data.RoleName, remote store.RemoteStore) (data.PublicKey, error) {
|
||||
rawPubKey, err := remote.RotateKey(role)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubKey, err := data.UnmarshalPublicKey(rawPubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pubKey, nil
|
||||
}
|
||||
|
||||
// signs and serializes the metadata for a canonical role in a TUF repo to JSON
|
||||
func serializeCanonicalRole(tufRepo *tuf.Repo, role data.RoleName, extraSigningKeys data.KeyList) (out []byte, err error) {
|
||||
var s *data.Signed
|
||||
switch {
|
||||
case role == data.CanonicalRootRole:
|
||||
s, err = tufRepo.SignRoot(data.DefaultExpires(role), extraSigningKeys)
|
||||
case role == data.CanonicalSnapshotRole:
|
||||
s, err = tufRepo.SignSnapshot(data.DefaultExpires(role))
|
||||
case tufRepo.Targets[role] != nil:
|
||||
s, err = tufRepo.SignTargets(
|
||||
role, data.DefaultExpires(data.CanonicalTargetsRole))
|
||||
default:
|
||||
err = fmt.Errorf("%s not supported role to sign on the client", role)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return json.Marshal(s)
|
||||
}
|
||||
|
||||
func getAllPrivKeys(rootKeyIDs []string, cryptoService signed.CryptoService) ([]data.PrivateKey, error) {
|
||||
if cryptoService == nil {
|
||||
return nil, fmt.Errorf("no crypto service available to get private keys from")
|
||||
}
|
||||
|
||||
privKeys := make([]data.PrivateKey, 0, len(rootKeyIDs))
|
||||
for _, keyID := range rootKeyIDs {
|
||||
privKey, _, err := cryptoService.GetPrivateKey(keyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
privKeys = append(privKeys, privKey)
|
||||
}
|
||||
if len(privKeys) == 0 {
|
||||
var rootKeyID string
|
||||
rootKeyList := cryptoService.ListKeys(data.CanonicalRootRole)
|
||||
if len(rootKeyList) == 0 {
|
||||
rootPublicKey, err := cryptoService.Create(data.CanonicalRootRole, "", data.ECDSAKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rootKeyID = rootPublicKey.ID()
|
||||
} else {
|
||||
rootKeyID = rootKeyList[0]
|
||||
}
|
||||
privKey, _, err := cryptoService.GetPrivateKey(rootKeyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
privKeys = append(privKeys, privKey)
|
||||
}
|
||||
|
||||
return privKeys, nil
|
||||
}
|
47
vendor/github.com/theupdateframework/notary/client/interface.go
generated
vendored
Normal file
47
vendor/github.com/theupdateframework/notary/client/interface.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/theupdateframework/notary/client/changelist"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"github.com/theupdateframework/notary/tuf/signed"
|
||||
)
|
||||
|
||||
// Repository represents the set of options that must be supported over a TUF repo.
|
||||
type Repository interface {
|
||||
// General management operations
|
||||
Initialize(rootKeyIDs []string, serverManagedRoles ...data.RoleName) error
|
||||
InitializeWithCertificate(rootKeyIDs []string, rootCerts []data.PublicKey, serverManagedRoles ...data.RoleName) error
|
||||
Publish() error
|
||||
|
||||
// Target Operations
|
||||
AddTarget(target *Target, roles ...data.RoleName) error
|
||||
RemoveTarget(targetName string, roles ...data.RoleName) error
|
||||
ListTargets(roles ...data.RoleName) ([]*TargetWithRole, error)
|
||||
GetTargetByName(name string, roles ...data.RoleName) (*TargetWithRole, error)
|
||||
GetAllTargetMetadataByName(name string) ([]TargetSignedStruct, error)
|
||||
|
||||
// Changelist operations
|
||||
GetChangelist() (changelist.Changelist, error)
|
||||
|
||||
// Role operations
|
||||
ListRoles() ([]RoleWithSignatures, error)
|
||||
GetDelegationRoles() ([]data.Role, error)
|
||||
AddDelegation(name data.RoleName, delegationKeys []data.PublicKey, paths []string) error
|
||||
AddDelegationRoleAndKeys(name data.RoleName, delegationKeys []data.PublicKey) error
|
||||
AddDelegationPaths(name data.RoleName, paths []string) error
|
||||
RemoveDelegationKeysAndPaths(name data.RoleName, keyIDs, paths []string) error
|
||||
RemoveDelegationRole(name data.RoleName) error
|
||||
RemoveDelegationPaths(name data.RoleName, paths []string) error
|
||||
RemoveDelegationKeys(name data.RoleName, keyIDs []string) error
|
||||
ClearDelegationPaths(name data.RoleName) error
|
||||
|
||||
// Witness and other re-signing operations
|
||||
Witness(roles ...data.RoleName) ([]data.RoleName, error)
|
||||
|
||||
// Key Operations
|
||||
RotateKey(role data.RoleName, serverManagesKey bool, keyList []string) error
|
||||
|
||||
GetCryptoService() signed.CryptoService
|
||||
SetLegacyVersions(int)
|
||||
GetGUN() data.GUN
|
||||
}
|
18
vendor/github.com/theupdateframework/notary/client/repo.go
generated
vendored
Normal file
18
vendor/github.com/theupdateframework/notary/client/repo.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
// +build !pkcs11
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/theupdateframework/notary"
|
||||
"github.com/theupdateframework/notary/trustmanager"
|
||||
)
|
||||
|
||||
func getKeyStores(baseDir string, retriever notary.PassRetriever) ([]trustmanager.KeyStore, error) {
|
||||
fileKeyStore, err := trustmanager.NewKeyFileStore(baseDir, retriever)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create private key store in directory: %s", baseDir)
|
||||
}
|
||||
return []trustmanager.KeyStore{fileKeyStore}, nil
|
||||
}
|
25
vendor/github.com/theupdateframework/notary/client/repo_pkcs11.go
generated
vendored
Normal file
25
vendor/github.com/theupdateframework/notary/client/repo_pkcs11.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// +build pkcs11
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/theupdateframework/notary"
|
||||
"github.com/theupdateframework/notary/trustmanager"
|
||||
"github.com/theupdateframework/notary/trustmanager/yubikey"
|
||||
)
|
||||
|
||||
func getKeyStores(baseDir string, retriever notary.PassRetriever) ([]trustmanager.KeyStore, error) {
|
||||
fileKeyStore, err := trustmanager.NewKeyFileStore(baseDir, retriever)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create private key store in directory: %s", baseDir)
|
||||
}
|
||||
|
||||
keyStores := []trustmanager.KeyStore{fileKeyStore}
|
||||
yubiKeyStore, _ := yubikey.NewYubiStore(fileKeyStore, retriever)
|
||||
if yubiKeyStore != nil {
|
||||
keyStores = []trustmanager.KeyStore{yubiKeyStore, fileKeyStore}
|
||||
}
|
||||
return keyStores, nil
|
||||
}
|
325
vendor/github.com/theupdateframework/notary/client/tufclient.go
generated
vendored
Normal file
325
vendor/github.com/theupdateframework/notary/client/tufclient.go
generated
vendored
Normal file
@ -0,0 +1,325 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary"
|
||||
store "github.com/theupdateframework/notary/storage"
|
||||
"github.com/theupdateframework/notary/trustpinning"
|
||||
"github.com/theupdateframework/notary/tuf"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"github.com/theupdateframework/notary/tuf/signed"
|
||||
)
|
||||
|
||||
// tufClient is a usability wrapper around a raw TUF repo
|
||||
type tufClient struct {
|
||||
remote store.RemoteStore
|
||||
cache store.MetadataStore
|
||||
oldBuilder tuf.RepoBuilder
|
||||
newBuilder tuf.RepoBuilder
|
||||
}
|
||||
|
||||
// newTufClient initialized a tufClient with the given repo, remote source of content, and cache
|
||||
func newTufClient(oldBuilder, newBuilder tuf.RepoBuilder, remote store.RemoteStore, cache store.MetadataStore) *tufClient {
|
||||
return &tufClient{
|
||||
oldBuilder: oldBuilder,
|
||||
newBuilder: newBuilder,
|
||||
remote: remote,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
// Update performs an update to the TUF repo as defined by the TUF spec
|
||||
func (c *tufClient) Update() (*tuf.Repo, *tuf.Repo, error) {
|
||||
// 1. Get timestamp
|
||||
// a. If timestamp error (verification, expired, etc...) download new root and return to 1.
|
||||
// 2. Check if local snapshot is up to date
|
||||
// a. If out of date, get updated snapshot
|
||||
// i. If snapshot error, download new root and return to 1.
|
||||
// 3. Check if root correct against snapshot
|
||||
// a. If incorrect, download new root and return to 1.
|
||||
// 4. Iteratively download and search targets and delegations to find target meta
|
||||
logrus.Debug("updating TUF client")
|
||||
err := c.update()
|
||||
if err != nil {
|
||||
logrus.Debug("Error occurred. Root will be downloaded and another update attempted")
|
||||
logrus.Debug("Resetting the TUF builder...")
|
||||
|
||||
c.newBuilder = c.newBuilder.BootstrapNewBuilder()
|
||||
|
||||
if err := c.updateRoot(); err != nil {
|
||||
logrus.Debug("Client Update (Root): ", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
// If we error again, we now have the latest root and just want to fail
|
||||
// out as there's no expectation the problem can be resolved automatically
|
||||
logrus.Debug("retrying TUF client update")
|
||||
if err := c.update(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
return c.newBuilder.Finish()
|
||||
}
|
||||
|
||||
func (c *tufClient) update() error {
|
||||
if err := c.downloadTimestamp(); err != nil {
|
||||
logrus.Debugf("Client Update (Timestamp): %s", err.Error())
|
||||
return err
|
||||
}
|
||||
if err := c.downloadSnapshot(); err != nil {
|
||||
logrus.Debugf("Client Update (Snapshot): %s", err.Error())
|
||||
return err
|
||||
}
|
||||
// will always need top level targets at a minimum
|
||||
if err := c.downloadTargets(); err != nil {
|
||||
logrus.Debugf("Client Update (Targets): %s", err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateRoot checks if there is a newer version of the root available, and if so
|
||||
// downloads all intermediate root files to allow proper key rotation.
|
||||
func (c *tufClient) updateRoot() error {
|
||||
// Get current root version
|
||||
currentRootConsistentInfo := c.oldBuilder.GetConsistentInfo(data.CanonicalRootRole)
|
||||
currentVersion := c.oldBuilder.GetLoadedVersion(currentRootConsistentInfo.RoleName)
|
||||
|
||||
// Get new root version
|
||||
raw, err := c.downloadRoot()
|
||||
|
||||
switch err.(type) {
|
||||
case *trustpinning.ErrRootRotationFail:
|
||||
// Rotation errors are okay since we haven't yet downloaded
|
||||
// all intermediate root files
|
||||
break
|
||||
case nil:
|
||||
// No error updating root - we were at most 1 version behind
|
||||
return nil
|
||||
default:
|
||||
// Return any non-rotation error.
|
||||
return err
|
||||
}
|
||||
|
||||
// Load current version into newBuilder
|
||||
currentRaw, err := c.cache.GetSized(data.CanonicalRootRole.String(), -1)
|
||||
if err != nil {
|
||||
logrus.Debugf("error loading %d.%s: %s", currentVersion, data.CanonicalRootRole, err)
|
||||
return err
|
||||
}
|
||||
if err := c.newBuilder.LoadRootForUpdate(currentRaw, currentVersion, false); err != nil {
|
||||
logrus.Debugf("%d.%s is invalid: %s", currentVersion, data.CanonicalRootRole, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Extract newest version number
|
||||
signedRoot := &data.Signed{}
|
||||
if err := json.Unmarshal(raw, signedRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
newestRoot, err := data.RootFromSigned(signedRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newestVersion := newestRoot.Signed.SignedCommon.Version
|
||||
|
||||
// Update from current + 1 (current already loaded) to newest - 1 (newest loaded below)
|
||||
if err := c.updateRootVersions(currentVersion+1, newestVersion-1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Already downloaded newest, verify it against newest - 1
|
||||
if err := c.newBuilder.LoadRootForUpdate(raw, newestVersion, true); err != nil {
|
||||
logrus.Debugf("downloaded %d.%s is invalid: %s", newestVersion, data.CanonicalRootRole, err)
|
||||
return err
|
||||
}
|
||||
logrus.Debugf("successfully verified downloaded %d.%s", newestVersion, data.CanonicalRootRole)
|
||||
|
||||
// Write newest to cache
|
||||
if err := c.cache.Set(data.CanonicalRootRole.String(), raw); err != nil {
|
||||
logrus.Debugf("unable to write %s to cache: %d.%s", newestVersion, data.CanonicalRootRole, err)
|
||||
}
|
||||
logrus.Debugf("finished updating root files")
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateRootVersions updates the root from it's current version to a target, rotating keys
|
||||
// as they are found
|
||||
func (c *tufClient) updateRootVersions(fromVersion, toVersion int) error {
|
||||
for v := fromVersion; v <= toVersion; v++ {
|
||||
logrus.Debugf("updating root from version %d to version %d, currently fetching %d", fromVersion, toVersion, v)
|
||||
|
||||
versionedRole := fmt.Sprintf("%d.%s", v, data.CanonicalRootRole)
|
||||
|
||||
raw, err := c.remote.GetSized(versionedRole, -1)
|
||||
if err != nil {
|
||||
logrus.Debugf("error downloading %s: %s", versionedRole, err)
|
||||
return err
|
||||
}
|
||||
if err := c.newBuilder.LoadRootForUpdate(raw, v, false); err != nil {
|
||||
logrus.Debugf("downloaded %s is invalid: %s", versionedRole, err)
|
||||
return err
|
||||
}
|
||||
logrus.Debugf("successfully verified downloaded %s", versionedRole)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// downloadTimestamp is responsible for downloading the timestamp.json
|
||||
// Timestamps are special in that we ALWAYS attempt to download and only
|
||||
// use cache if the download fails (and the cache is still valid).
|
||||
func (c *tufClient) downloadTimestamp() error {
|
||||
logrus.Debug("Loading timestamp...")
|
||||
role := data.CanonicalTimestampRole
|
||||
consistentInfo := c.newBuilder.GetConsistentInfo(role)
|
||||
|
||||
// always get the remote timestamp, since it supersedes the local one
|
||||
cachedTS, cachedErr := c.cache.GetSized(role.String(), notary.MaxTimestampSize)
|
||||
_, remoteErr := c.tryLoadRemote(consistentInfo, cachedTS)
|
||||
|
||||
// check that there was no remote error, or if there was a network problem
|
||||
// If there was a validation error, we should error out so we can download a new root or fail the update
|
||||
switch remoteErr.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case store.ErrMetaNotFound, store.ErrServerUnavailable, store.ErrOffline, store.NetworkError:
|
||||
break
|
||||
default:
|
||||
return remoteErr
|
||||
}
|
||||
|
||||
// since it was a network error: get the cached timestamp, if it exists
|
||||
if cachedErr != nil {
|
||||
logrus.Debug("no cached or remote timestamp available")
|
||||
return remoteErr
|
||||
}
|
||||
|
||||
logrus.Warn("Error while downloading remote metadata, using cached timestamp - this might not be the latest version available remotely")
|
||||
err := c.newBuilder.Load(role, cachedTS, 1, false)
|
||||
if err == nil {
|
||||
logrus.Debug("successfully verified cached timestamp")
|
||||
}
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
// downloadSnapshot is responsible for downloading the snapshot.json
|
||||
func (c *tufClient) downloadSnapshot() error {
|
||||
logrus.Debug("Loading snapshot...")
|
||||
role := data.CanonicalSnapshotRole
|
||||
consistentInfo := c.newBuilder.GetConsistentInfo(role)
|
||||
|
||||
_, err := c.tryLoadCacheThenRemote(consistentInfo)
|
||||
return err
|
||||
}
|
||||
|
||||
// downloadTargets downloads all targets and delegated targets for the repository.
|
||||
// It uses a pre-order tree traversal as it's necessary to download parents first
|
||||
// to obtain the keys to validate children.
|
||||
func (c *tufClient) downloadTargets() error {
|
||||
toDownload := []data.DelegationRole{{
|
||||
BaseRole: data.BaseRole{Name: data.CanonicalTargetsRole},
|
||||
Paths: []string{""},
|
||||
}}
|
||||
|
||||
for len(toDownload) > 0 {
|
||||
role := toDownload[0]
|
||||
toDownload = toDownload[1:]
|
||||
|
||||
consistentInfo := c.newBuilder.GetConsistentInfo(role.Name)
|
||||
if !consistentInfo.ChecksumKnown() {
|
||||
logrus.Debugf("skipping %s because there is no checksum for it", role.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
children, err := c.getTargetsFile(role, consistentInfo)
|
||||
switch err.(type) {
|
||||
case signed.ErrExpired, signed.ErrRoleThreshold:
|
||||
if role.Name == data.CanonicalTargetsRole {
|
||||
return err
|
||||
}
|
||||
logrus.Warnf("Error getting %s: %s", role.Name, err)
|
||||
break
|
||||
case nil:
|
||||
toDownload = append(children, toDownload...)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c tufClient) getTargetsFile(role data.DelegationRole, ci tuf.ConsistentInfo) ([]data.DelegationRole, error) {
|
||||
logrus.Debugf("Loading %s...", role.Name)
|
||||
tgs := &data.SignedTargets{}
|
||||
|
||||
raw, err := c.tryLoadCacheThenRemote(ci)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// we know it unmarshals because if `tryLoadCacheThenRemote` didn't fail, then
|
||||
// the raw has already been loaded into the builder
|
||||
json.Unmarshal(raw, tgs)
|
||||
return tgs.GetValidDelegations(role), nil
|
||||
}
|
||||
|
||||
// downloadRoot is responsible for downloading the root.json
|
||||
func (c *tufClient) downloadRoot() ([]byte, error) {
|
||||
role := data.CanonicalRootRole
|
||||
consistentInfo := c.newBuilder.GetConsistentInfo(role)
|
||||
|
||||
// We can't read an exact size for the root metadata without risking getting stuck in the TUF update cycle
|
||||
// since it's possible that downloading timestamp/snapshot metadata may fail due to a signature mismatch
|
||||
if !consistentInfo.ChecksumKnown() {
|
||||
logrus.Debugf("Loading root with no expected checksum")
|
||||
|
||||
// get the cached root, if it exists, just for version checking
|
||||
cachedRoot, _ := c.cache.GetSized(role.String(), -1)
|
||||
// prefer to download a new root
|
||||
return c.tryLoadRemote(consistentInfo, cachedRoot)
|
||||
}
|
||||
return c.tryLoadCacheThenRemote(consistentInfo)
|
||||
}
|
||||
|
||||
func (c *tufClient) tryLoadCacheThenRemote(consistentInfo tuf.ConsistentInfo) ([]byte, error) {
|
||||
cachedTS, err := c.cache.GetSized(consistentInfo.RoleName.String(), consistentInfo.Length())
|
||||
if err != nil {
|
||||
logrus.Debugf("no %s in cache, must download", consistentInfo.RoleName)
|
||||
return c.tryLoadRemote(consistentInfo, nil)
|
||||
}
|
||||
|
||||
if err = c.newBuilder.Load(consistentInfo.RoleName, cachedTS, 1, false); err == nil {
|
||||
logrus.Debugf("successfully verified cached %s", consistentInfo.RoleName)
|
||||
return cachedTS, nil
|
||||
}
|
||||
|
||||
logrus.Debugf("cached %s is invalid (must download): %s", consistentInfo.RoleName, err)
|
||||
return c.tryLoadRemote(consistentInfo, cachedTS)
|
||||
}
|
||||
|
||||
func (c *tufClient) tryLoadRemote(consistentInfo tuf.ConsistentInfo, old []byte) ([]byte, error) {
|
||||
consistentName := consistentInfo.ConsistentName()
|
||||
raw, err := c.remote.GetSized(consistentName, consistentInfo.Length())
|
||||
if err != nil {
|
||||
logrus.Debugf("error downloading %s: %s", consistentName, err)
|
||||
return old, err
|
||||
}
|
||||
|
||||
// try to load the old data into the old builder - only use it to validate
|
||||
// versions if it loads successfully. If it errors, then the loaded version
|
||||
// will be 1
|
||||
c.oldBuilder.Load(consistentInfo.RoleName, old, 1, true)
|
||||
minVersion := c.oldBuilder.GetLoadedVersion(consistentInfo.RoleName)
|
||||
if err := c.newBuilder.Load(consistentInfo.RoleName, raw, minVersion, false); err != nil {
|
||||
logrus.Debugf("downloaded %s is invalid: %s", consistentName, err)
|
||||
return raw, err
|
||||
}
|
||||
logrus.Debugf("successfully verified downloaded %s", consistentName)
|
||||
if err := c.cache.Set(consistentInfo.RoleName.String(), raw); err != nil {
|
||||
logrus.Debugf("Unable to write %s to cache: %s", consistentInfo.RoleName, err)
|
||||
}
|
||||
return raw, nil
|
||||
}
|
62
vendor/github.com/theupdateframework/notary/client/witness.go
generated
vendored
Normal file
62
vendor/github.com/theupdateframework/notary/client/witness.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/theupdateframework/notary/client/changelist"
|
||||
"github.com/theupdateframework/notary/tuf"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
||||
// Witness creates change objects to witness (i.e. re-sign) the given
|
||||
// roles on the next publish. One change is created per role
|
||||
func (r *repository) Witness(roles ...data.RoleName) ([]data.RoleName, error) {
|
||||
var err error
|
||||
successful := make([]data.RoleName, 0, len(roles))
|
||||
for _, role := range roles {
|
||||
// scope is role
|
||||
c := changelist.NewTUFChange(
|
||||
changelist.ActionUpdate,
|
||||
role,
|
||||
changelist.TypeWitness,
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
err = r.changelist.Add(c)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
successful = append(successful, role)
|
||||
}
|
||||
return successful, err
|
||||
}
|
||||
|
||||
func witnessTargets(repo *tuf.Repo, invalid *tuf.Repo, role data.RoleName) error {
|
||||
if r, ok := repo.Targets[role]; ok {
|
||||
// role is already valid, mark for re-signing/updating
|
||||
r.Dirty = true
|
||||
return nil
|
||||
}
|
||||
|
||||
if roleObj, err := repo.GetDelegationRole(role); err == nil && invalid != nil {
|
||||
// A role with a threshold > len(keys) is technically invalid, but we let it build in the builder because
|
||||
// we want to be able to download the role (which may still have targets on it), add more keys, and then
|
||||
// witness the role, thus bringing it back to valid. However, if no keys have been added before witnessing,
|
||||
// then it is still an invalid role, and can't be witnessed because nothing can bring it back to valid.
|
||||
if roleObj.Threshold > len(roleObj.Keys) {
|
||||
return data.ErrInvalidRole{
|
||||
Role: role,
|
||||
Reason: "role does not specify enough valid signing keys to meet its required threshold",
|
||||
}
|
||||
}
|
||||
if r, ok := invalid.Targets[role]; ok {
|
||||
// role is recognized but invalid, move to valid data and mark for re-signing
|
||||
repo.Targets[role] = r
|
||||
r.Dirty = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// role isn't recognized, even as invalid
|
||||
return data.ErrInvalidRole{
|
||||
Role: role,
|
||||
Reason: "this role is not known",
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user