forked from toolshed/abra
chore: vendor
This commit is contained in:
30
vendor/github.com/theupdateframework/notary/tuf/LICENSE
generated
vendored
Normal file
30
vendor/github.com/theupdateframework/notary/tuf/LICENSE
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
Copyright (c) 2015, Docker Inc.
|
||||
Copyright (c) 2014-2015 Prime Directive, Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Prime Directive, Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
6
vendor/github.com/theupdateframework/notary/tuf/README.md
generated
vendored
Normal file
6
vendor/github.com/theupdateframework/notary/tuf/README.md
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
## Credits
|
||||
|
||||
This implementation was originally forked from [flynn/go-tuf](https://github.com/flynn/go-tuf)
|
||||
|
||||
This implementation retains the same 3 Clause BSD license present on
|
||||
the original flynn implementation.
|
732
vendor/github.com/theupdateframework/notary/tuf/builder.go
generated
vendored
Normal file
732
vendor/github.com/theupdateframework/notary/tuf/builder.go
generated
vendored
Normal file
@ -0,0 +1,732 @@
|
||||
package tuf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/go/canonical/json"
|
||||
"github.com/theupdateframework/notary"
|
||||
|
||||
"github.com/theupdateframework/notary/trustpinning"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"github.com/theupdateframework/notary/tuf/signed"
|
||||
"github.com/theupdateframework/notary/tuf/utils"
|
||||
)
|
||||
|
||||
// ErrBuildDone is returned when any functions are called on RepoBuilder, and it
|
||||
// is already finished building
|
||||
var ErrBuildDone = fmt.Errorf(
|
||||
"the builder has finished building and cannot accept any more input or produce any more output")
|
||||
|
||||
// ErrInvalidBuilderInput is returned when RepoBuilder.Load is called
|
||||
// with the wrong type of metadata for the state that it's in
|
||||
type ErrInvalidBuilderInput struct{ msg string }
|
||||
|
||||
func (e ErrInvalidBuilderInput) Error() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
// ConsistentInfo is the consistent name and size of a role, or just the name
|
||||
// of the role and a -1 if no file metadata for the role is known
|
||||
type ConsistentInfo struct {
|
||||
RoleName data.RoleName
|
||||
fileMeta data.FileMeta
|
||||
}
|
||||
|
||||
// ChecksumKnown determines whether or not we know enough to provide a size and
|
||||
// consistent name
|
||||
func (c ConsistentInfo) ChecksumKnown() bool {
|
||||
// empty hash, no size : this is the zero value
|
||||
return len(c.fileMeta.Hashes) > 0 || c.fileMeta.Length != 0
|
||||
}
|
||||
|
||||
// ConsistentName returns the consistent name (rolename.sha256) for the role
|
||||
// given this consistent information
|
||||
func (c ConsistentInfo) ConsistentName() string {
|
||||
return utils.ConsistentName(c.RoleName.String(), c.fileMeta.Hashes[notary.SHA256])
|
||||
}
|
||||
|
||||
// Length returns the expected length of the role as per this consistent
|
||||
// information - if no checksum information is known, the size is -1.
|
||||
func (c ConsistentInfo) Length() int64 {
|
||||
if c.ChecksumKnown() {
|
||||
return c.fileMeta.Length
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// RepoBuilder is an interface for an object which builds a tuf.Repo
|
||||
type RepoBuilder interface {
|
||||
Load(roleName data.RoleName, content []byte, minVersion int, allowExpired bool) error
|
||||
LoadRootForUpdate(content []byte, minVersion int, isFinal bool) error
|
||||
GenerateSnapshot(prev *data.SignedSnapshot) ([]byte, int, error)
|
||||
GenerateTimestamp(prev *data.SignedTimestamp) ([]byte, int, error)
|
||||
Finish() (*Repo, *Repo, error)
|
||||
BootstrapNewBuilder() RepoBuilder
|
||||
BootstrapNewBuilderWithNewTrustpin(trustpin trustpinning.TrustPinConfig) RepoBuilder
|
||||
|
||||
// informative functions
|
||||
IsLoaded(roleName data.RoleName) bool
|
||||
GetLoadedVersion(roleName data.RoleName) int
|
||||
GetConsistentInfo(roleName data.RoleName) ConsistentInfo
|
||||
}
|
||||
|
||||
// finishedBuilder refuses any more input or output
|
||||
type finishedBuilder struct{}
|
||||
|
||||
func (f finishedBuilder) Load(roleName data.RoleName, content []byte, minVersion int, allowExpired bool) error {
|
||||
return ErrBuildDone
|
||||
}
|
||||
func (f finishedBuilder) LoadRootForUpdate(content []byte, minVersion int, isFinal bool) error {
|
||||
return ErrBuildDone
|
||||
}
|
||||
func (f finishedBuilder) GenerateSnapshot(prev *data.SignedSnapshot) ([]byte, int, error) {
|
||||
return nil, 0, ErrBuildDone
|
||||
}
|
||||
func (f finishedBuilder) GenerateTimestamp(prev *data.SignedTimestamp) ([]byte, int, error) {
|
||||
return nil, 0, ErrBuildDone
|
||||
}
|
||||
func (f finishedBuilder) Finish() (*Repo, *Repo, error) { return nil, nil, ErrBuildDone }
|
||||
func (f finishedBuilder) BootstrapNewBuilder() RepoBuilder { return f }
|
||||
func (f finishedBuilder) BootstrapNewBuilderWithNewTrustpin(trustpin trustpinning.TrustPinConfig) RepoBuilder {
|
||||
return f
|
||||
}
|
||||
func (f finishedBuilder) IsLoaded(roleName data.RoleName) bool { return false }
|
||||
func (f finishedBuilder) GetLoadedVersion(roleName data.RoleName) int { return 0 }
|
||||
func (f finishedBuilder) GetConsistentInfo(roleName data.RoleName) ConsistentInfo {
|
||||
return ConsistentInfo{RoleName: roleName}
|
||||
}
|
||||
|
||||
// NewRepoBuilder is the only way to get a pre-built RepoBuilder
|
||||
func NewRepoBuilder(gun data.GUN, cs signed.CryptoService, trustpin trustpinning.TrustPinConfig) RepoBuilder {
|
||||
return NewBuilderFromRepo(gun, NewRepo(cs), trustpin)
|
||||
}
|
||||
|
||||
// NewBuilderFromRepo allows us to bootstrap a builder given existing repo data.
|
||||
// YOU PROBABLY SHOULDN'T BE USING THIS OUTSIDE OF TESTING CODE!!!
|
||||
func NewBuilderFromRepo(gun data.GUN, repo *Repo, trustpin trustpinning.TrustPinConfig) RepoBuilder {
|
||||
return &repoBuilderWrapper{
|
||||
RepoBuilder: &repoBuilder{
|
||||
repo: repo,
|
||||
invalidRoles: NewRepo(nil),
|
||||
gun: gun,
|
||||
trustpin: trustpin,
|
||||
loadedNotChecksummed: make(map[data.RoleName][]byte),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// repoBuilderWrapper embeds a repoBuilder, but once Finish is called, swaps
|
||||
// the embed out with a finishedBuilder
|
||||
type repoBuilderWrapper struct {
|
||||
RepoBuilder
|
||||
}
|
||||
|
||||
func (rbw *repoBuilderWrapper) Finish() (*Repo, *Repo, error) {
|
||||
switch rbw.RepoBuilder.(type) {
|
||||
case finishedBuilder:
|
||||
return rbw.RepoBuilder.Finish()
|
||||
default:
|
||||
old := rbw.RepoBuilder
|
||||
rbw.RepoBuilder = finishedBuilder{}
|
||||
return old.Finish()
|
||||
}
|
||||
}
|
||||
|
||||
// repoBuilder actually builds a tuf.Repo
|
||||
type repoBuilder struct {
|
||||
repo *Repo
|
||||
invalidRoles *Repo
|
||||
|
||||
// needed for root trust pininng verification
|
||||
gun data.GUN
|
||||
trustpin trustpinning.TrustPinConfig
|
||||
|
||||
// in case we load root and/or targets before snapshot and timestamp (
|
||||
// or snapshot and not timestamp), so we know what to verify when the
|
||||
// data with checksums come in
|
||||
loadedNotChecksummed map[data.RoleName][]byte
|
||||
|
||||
// bootstrapped values to validate a new root
|
||||
prevRoot *data.SignedRoot
|
||||
bootstrappedRootChecksum *data.FileMeta
|
||||
|
||||
// for bootstrapping the next builder
|
||||
nextRootChecksum *data.FileMeta
|
||||
}
|
||||
|
||||
func (rb *repoBuilder) Finish() (*Repo, *Repo, error) {
|
||||
return rb.repo, rb.invalidRoles, nil
|
||||
}
|
||||
|
||||
func (rb *repoBuilder) BootstrapNewBuilder() RepoBuilder {
|
||||
return &repoBuilderWrapper{RepoBuilder: &repoBuilder{
|
||||
repo: NewRepo(rb.repo.cryptoService),
|
||||
invalidRoles: NewRepo(nil),
|
||||
gun: rb.gun,
|
||||
loadedNotChecksummed: make(map[data.RoleName][]byte),
|
||||
trustpin: rb.trustpin,
|
||||
|
||||
prevRoot: rb.repo.Root,
|
||||
bootstrappedRootChecksum: rb.nextRootChecksum,
|
||||
}}
|
||||
}
|
||||
|
||||
func (rb *repoBuilder) BootstrapNewBuilderWithNewTrustpin(trustpin trustpinning.TrustPinConfig) RepoBuilder {
|
||||
return &repoBuilderWrapper{RepoBuilder: &repoBuilder{
|
||||
repo: NewRepo(rb.repo.cryptoService),
|
||||
gun: rb.gun,
|
||||
loadedNotChecksummed: make(map[data.RoleName][]byte),
|
||||
trustpin: trustpin,
|
||||
|
||||
prevRoot: rb.repo.Root,
|
||||
bootstrappedRootChecksum: rb.nextRootChecksum,
|
||||
}}
|
||||
}
|
||||
|
||||
// IsLoaded returns whether a particular role has already been loaded
|
||||
func (rb *repoBuilder) IsLoaded(roleName data.RoleName) bool {
|
||||
switch roleName {
|
||||
case data.CanonicalRootRole:
|
||||
return rb.repo.Root != nil
|
||||
case data.CanonicalSnapshotRole:
|
||||
return rb.repo.Snapshot != nil
|
||||
case data.CanonicalTimestampRole:
|
||||
return rb.repo.Timestamp != nil
|
||||
default:
|
||||
return rb.repo.Targets[roleName] != nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetLoadedVersion returns the metadata version, if it is loaded, or 1 (the
|
||||
// minimum valid version number) otherwise
|
||||
func (rb *repoBuilder) GetLoadedVersion(roleName data.RoleName) int {
|
||||
switch {
|
||||
case roleName == data.CanonicalRootRole && rb.repo.Root != nil:
|
||||
return rb.repo.Root.Signed.Version
|
||||
case roleName == data.CanonicalSnapshotRole && rb.repo.Snapshot != nil:
|
||||
return rb.repo.Snapshot.Signed.Version
|
||||
case roleName == data.CanonicalTimestampRole && rb.repo.Timestamp != nil:
|
||||
return rb.repo.Timestamp.Signed.Version
|
||||
default:
|
||||
if tgts, ok := rb.repo.Targets[roleName]; ok {
|
||||
return tgts.Signed.Version
|
||||
}
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
// GetConsistentInfo returns the consistent name and size of a role, if it is known,
|
||||
// otherwise just the rolename and a -1 for size (both of which are inside a
|
||||
// ConsistentInfo object)
|
||||
func (rb *repoBuilder) GetConsistentInfo(roleName data.RoleName) ConsistentInfo {
|
||||
info := ConsistentInfo{RoleName: roleName} // starts out with unknown filemeta
|
||||
switch roleName {
|
||||
case data.CanonicalTimestampRole:
|
||||
// we do not want to get a consistent timestamp, but we do want to
|
||||
// limit its size
|
||||
info.fileMeta.Length = notary.MaxTimestampSize
|
||||
case data.CanonicalSnapshotRole:
|
||||
if rb.repo.Timestamp != nil {
|
||||
info.fileMeta = rb.repo.Timestamp.Signed.Meta[roleName.String()]
|
||||
}
|
||||
case data.CanonicalRootRole:
|
||||
switch {
|
||||
case rb.bootstrappedRootChecksum != nil:
|
||||
info.fileMeta = *rb.bootstrappedRootChecksum
|
||||
case rb.repo.Snapshot != nil:
|
||||
info.fileMeta = rb.repo.Snapshot.Signed.Meta[roleName.String()]
|
||||
}
|
||||
default:
|
||||
if rb.repo.Snapshot != nil {
|
||||
info.fileMeta = rb.repo.Snapshot.Signed.Meta[roleName.String()]
|
||||
}
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
func (rb *repoBuilder) Load(roleName data.RoleName, content []byte, minVersion int, allowExpired bool) error {
|
||||
return rb.loadOptions(roleName, content, minVersion, allowExpired, false, false)
|
||||
}
|
||||
|
||||
// LoadRootForUpdate adds additional flags for updating the root.json file
|
||||
func (rb *repoBuilder) LoadRootForUpdate(content []byte, minVersion int, isFinal bool) error {
|
||||
if err := rb.loadOptions(data.CanonicalRootRole, content, minVersion, !isFinal, !isFinal, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if !isFinal {
|
||||
rb.prevRoot = rb.repo.Root
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadOptions adds additional flags that should only be used for updating the root.json
|
||||
func (rb *repoBuilder) loadOptions(roleName data.RoleName, content []byte, minVersion int, allowExpired, skipChecksum, allowLoaded bool) error {
|
||||
if !data.ValidRole(roleName) {
|
||||
return ErrInvalidBuilderInput{msg: fmt.Sprintf("%s is an invalid role", roleName)}
|
||||
}
|
||||
|
||||
if !allowLoaded && rb.IsLoaded(roleName) {
|
||||
return ErrInvalidBuilderInput{msg: fmt.Sprintf("%s has already been loaded", roleName)}
|
||||
}
|
||||
|
||||
var err error
|
||||
switch roleName {
|
||||
case data.CanonicalRootRole:
|
||||
break
|
||||
case data.CanonicalTimestampRole, data.CanonicalSnapshotRole, data.CanonicalTargetsRole:
|
||||
err = rb.checkPrereqsLoaded([]data.RoleName{data.CanonicalRootRole})
|
||||
default: // delegations
|
||||
err = rb.checkPrereqsLoaded([]data.RoleName{data.CanonicalRootRole, data.CanonicalTargetsRole})
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch roleName {
|
||||
case data.CanonicalRootRole:
|
||||
return rb.loadRoot(content, minVersion, allowExpired, skipChecksum)
|
||||
case data.CanonicalSnapshotRole:
|
||||
return rb.loadSnapshot(content, minVersion, allowExpired)
|
||||
case data.CanonicalTimestampRole:
|
||||
return rb.loadTimestamp(content, minVersion, allowExpired)
|
||||
case data.CanonicalTargetsRole:
|
||||
return rb.loadTargets(content, minVersion, allowExpired)
|
||||
default:
|
||||
return rb.loadDelegation(roleName, content, minVersion, allowExpired)
|
||||
}
|
||||
}
|
||||
|
||||
func (rb *repoBuilder) checkPrereqsLoaded(prereqRoles []data.RoleName) error {
|
||||
for _, req := range prereqRoles {
|
||||
if !rb.IsLoaded(req) {
|
||||
return ErrInvalidBuilderInput{msg: fmt.Sprintf("%s must be loaded first", req)}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateSnapshot generates a new snapshot given a previous (optional) snapshot
|
||||
// We can't just load the previous snapshot, because it may have been signed by a different
|
||||
// snapshot key (maybe from a previous root version). Note that we need the root role and
|
||||
// targets role to be loaded, because we need to generate metadata for both (and we need
|
||||
// the root to be loaded so we can get the snapshot role to sign with)
|
||||
func (rb *repoBuilder) GenerateSnapshot(prev *data.SignedSnapshot) ([]byte, int, error) {
|
||||
switch {
|
||||
case rb.repo.cryptoService == nil:
|
||||
return nil, 0, ErrInvalidBuilderInput{msg: "cannot generate snapshot without a cryptoservice"}
|
||||
case rb.IsLoaded(data.CanonicalSnapshotRole):
|
||||
return nil, 0, ErrInvalidBuilderInput{msg: "snapshot has already been loaded"}
|
||||
case rb.IsLoaded(data.CanonicalTimestampRole):
|
||||
return nil, 0, ErrInvalidBuilderInput{msg: "cannot generate snapshot if timestamp has already been loaded"}
|
||||
}
|
||||
|
||||
if err := rb.checkPrereqsLoaded([]data.RoleName{data.CanonicalRootRole}); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// If there is no previous snapshot, we need to generate one, and so the targets must
|
||||
// have already been loaded. Otherwise, so long as the previous snapshot structure is
|
||||
// valid (it has a targets meta), we're good.
|
||||
switch prev {
|
||||
case nil:
|
||||
if err := rb.checkPrereqsLoaded([]data.RoleName{data.CanonicalTargetsRole}); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if err := rb.repo.InitSnapshot(); err != nil {
|
||||
rb.repo.Snapshot = nil
|
||||
return nil, 0, err
|
||||
}
|
||||
default:
|
||||
if err := data.IsValidSnapshotStructure(prev.Signed); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
rb.repo.Snapshot = prev
|
||||
}
|
||||
|
||||
sgnd, err := rb.repo.SignSnapshot(data.DefaultExpires(data.CanonicalSnapshotRole))
|
||||
if err != nil {
|
||||
rb.repo.Snapshot = nil
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
sgndJSON, err := json.Marshal(sgnd)
|
||||
if err != nil {
|
||||
rb.repo.Snapshot = nil
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// loadedNotChecksummed should currently contain the root awaiting checksumming,
|
||||
// since it has to have been loaded. Since the snapshot was generated using
|
||||
// the root and targets data (there may not be any) that have been loaded,
|
||||
// remove all of them from rb.loadedNotChecksummed
|
||||
for tgtName := range rb.repo.Targets {
|
||||
delete(rb.loadedNotChecksummed, data.RoleName(tgtName))
|
||||
}
|
||||
delete(rb.loadedNotChecksummed, data.CanonicalRootRole)
|
||||
|
||||
// The timestamp can't have been loaded yet, so we want to cache the snapshot
|
||||
// bytes so we can validate the checksum when a timestamp gets generated or
|
||||
// loaded later.
|
||||
rb.loadedNotChecksummed[data.CanonicalSnapshotRole] = sgndJSON
|
||||
|
||||
return sgndJSON, rb.repo.Snapshot.Signed.Version, nil
|
||||
}
|
||||
|
||||
// GenerateTimestamp generates a new timestamp given a previous (optional) timestamp
|
||||
// We can't just load the previous timestamp, because it may have been signed by a different
|
||||
// timestamp key (maybe from a previous root version)
|
||||
func (rb *repoBuilder) GenerateTimestamp(prev *data.SignedTimestamp) ([]byte, int, error) {
|
||||
switch {
|
||||
case rb.repo.cryptoService == nil:
|
||||
return nil, 0, ErrInvalidBuilderInput{msg: "cannot generate timestamp without a cryptoservice"}
|
||||
case rb.IsLoaded(data.CanonicalTimestampRole):
|
||||
return nil, 0, ErrInvalidBuilderInput{msg: "timestamp has already been loaded"}
|
||||
}
|
||||
|
||||
// SignTimestamp always serializes the loaded snapshot and signs in the data, so we must always
|
||||
// have the snapshot loaded first
|
||||
if err := rb.checkPrereqsLoaded([]data.RoleName{data.CanonicalRootRole, data.CanonicalSnapshotRole}); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
switch prev {
|
||||
case nil:
|
||||
if err := rb.repo.InitTimestamp(); err != nil {
|
||||
rb.repo.Timestamp = nil
|
||||
return nil, 0, err
|
||||
}
|
||||
default:
|
||||
if err := data.IsValidTimestampStructure(prev.Signed); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
rb.repo.Timestamp = prev
|
||||
}
|
||||
|
||||
sgnd, err := rb.repo.SignTimestamp(data.DefaultExpires(data.CanonicalTimestampRole))
|
||||
if err != nil {
|
||||
rb.repo.Timestamp = nil
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
sgndJSON, err := json.Marshal(sgnd)
|
||||
if err != nil {
|
||||
rb.repo.Timestamp = nil
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// The snapshot should have been loaded (and not checksummed, since a timestamp
|
||||
// cannot have been loaded), so it is awaiting checksumming. Since this
|
||||
// timestamp was generated using the snapshot awaiting checksumming, we can
|
||||
// remove it from rb.loadedNotChecksummed. There should be no other items
|
||||
// awaiting checksumming now since loading/generating a snapshot should have
|
||||
// cleared out everything else in `loadNotChecksummed`.
|
||||
delete(rb.loadedNotChecksummed, data.CanonicalSnapshotRole)
|
||||
|
||||
return sgndJSON, rb.repo.Timestamp.Signed.Version, nil
|
||||
}
|
||||
|
||||
// loadRoot loads a root if one has not been loaded
|
||||
func (rb *repoBuilder) loadRoot(content []byte, minVersion int, allowExpired, skipChecksum bool) error {
|
||||
roleName := data.CanonicalRootRole
|
||||
|
||||
signedObj, err := rb.bytesToSigned(content, data.CanonicalRootRole, skipChecksum)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// ValidateRoot validates against the previous root's role, as well as validates that the root
|
||||
// itself is self-consistent with its own signatures and thresholds.
|
||||
// This assumes that ValidateRoot calls data.RootFromSigned, which validates
|
||||
// the metadata, rather than just unmarshalling signedObject into a SignedRoot object itself.
|
||||
signedRoot, err := trustpinning.ValidateRoot(rb.prevRoot, signedObj, rb.gun, rb.trustpin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := signed.VerifyVersion(&(signedRoot.Signed.SignedCommon), minVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !allowExpired { // check must go at the end because all other validation should pass
|
||||
if err := signed.VerifyExpiry(&(signedRoot.Signed.SignedCommon), roleName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
rootRole, err := signedRoot.BuildBaseRole(data.CanonicalRootRole)
|
||||
if err != nil { // this should never happen since the root has been validated
|
||||
return err
|
||||
}
|
||||
rb.repo.Root = signedRoot
|
||||
rb.repo.originalRootRole = rootRole
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rb *repoBuilder) loadTimestamp(content []byte, minVersion int, allowExpired bool) error {
|
||||
roleName := data.CanonicalTimestampRole
|
||||
|
||||
timestampRole, err := rb.repo.Root.BuildBaseRole(roleName)
|
||||
if err != nil { // this should never happen, since it's already been validated
|
||||
return err
|
||||
}
|
||||
|
||||
signedObj, err := rb.bytesToSignedAndValidateSigs(timestampRole, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signedTimestamp, err := data.TimestampFromSigned(signedObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := signed.VerifyVersion(&(signedTimestamp.Signed.SignedCommon), minVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !allowExpired { // check must go at the end because all other validation should pass
|
||||
if err := signed.VerifyExpiry(&(signedTimestamp.Signed.SignedCommon), roleName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := rb.validateChecksumsFromTimestamp(signedTimestamp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rb.repo.Timestamp = signedTimestamp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rb *repoBuilder) loadSnapshot(content []byte, minVersion int, allowExpired bool) error {
|
||||
roleName := data.CanonicalSnapshotRole
|
||||
|
||||
snapshotRole, err := rb.repo.Root.BuildBaseRole(roleName)
|
||||
if err != nil { // this should never happen, since it's already been validated
|
||||
return err
|
||||
}
|
||||
|
||||
signedObj, err := rb.bytesToSignedAndValidateSigs(snapshotRole, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signedSnapshot, err := data.SnapshotFromSigned(signedObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := signed.VerifyVersion(&(signedSnapshot.Signed.SignedCommon), minVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !allowExpired { // check must go at the end because all other validation should pass
|
||||
if err := signed.VerifyExpiry(&(signedSnapshot.Signed.SignedCommon), roleName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// at this point, the only thing left to validate is existing checksums - we can use
|
||||
// this snapshot to bootstrap the next builder if needed - and we don't need to do
|
||||
// the 2-value assignment since we've already validated the signedSnapshot, which MUST
|
||||
// have root metadata
|
||||
rootMeta := signedSnapshot.Signed.Meta[data.CanonicalRootRole.String()]
|
||||
rb.nextRootChecksum = &rootMeta
|
||||
|
||||
if err := rb.validateChecksumsFromSnapshot(signedSnapshot); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rb.repo.Snapshot = signedSnapshot
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rb *repoBuilder) loadTargets(content []byte, minVersion int, allowExpired bool) error {
|
||||
roleName := data.CanonicalTargetsRole
|
||||
|
||||
targetsRole, err := rb.repo.Root.BuildBaseRole(roleName)
|
||||
if err != nil { // this should never happen, since it's already been validated
|
||||
return err
|
||||
}
|
||||
|
||||
signedObj, err := rb.bytesToSignedAndValidateSigs(targetsRole, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signedTargets, err := data.TargetsFromSigned(signedObj, roleName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := signed.VerifyVersion(&(signedTargets.Signed.SignedCommon), minVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !allowExpired { // check must go at the end because all other validation should pass
|
||||
if err := signed.VerifyExpiry(&(signedTargets.Signed.SignedCommon), roleName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
signedTargets.Signatures = signedObj.Signatures
|
||||
rb.repo.Targets[roleName] = signedTargets
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rb *repoBuilder) loadDelegation(roleName data.RoleName, content []byte, minVersion int, allowExpired bool) error {
|
||||
delegationRole, err := rb.repo.GetDelegationRole(roleName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// bytesToSigned checks checksum
|
||||
signedObj, err := rb.bytesToSigned(content, roleName, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signedTargets, err := data.TargetsFromSigned(signedObj, roleName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := signed.VerifyVersion(&(signedTargets.Signed.SignedCommon), minVersion); err != nil {
|
||||
// don't capture in invalidRoles because the role we received is a rollback
|
||||
return err
|
||||
}
|
||||
|
||||
// verify signature
|
||||
if err := signed.VerifySignatures(signedObj, delegationRole.BaseRole); err != nil {
|
||||
rb.invalidRoles.Targets[roleName] = signedTargets
|
||||
return err
|
||||
}
|
||||
|
||||
if !allowExpired { // check must go at the end because all other validation should pass
|
||||
if err := signed.VerifyExpiry(&(signedTargets.Signed.SignedCommon), roleName); err != nil {
|
||||
rb.invalidRoles.Targets[roleName] = signedTargets
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
signedTargets.Signatures = signedObj.Signatures
|
||||
rb.repo.Targets[roleName] = signedTargets
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rb *repoBuilder) validateChecksumsFromTimestamp(ts *data.SignedTimestamp) error {
|
||||
sn, ok := rb.loadedNotChecksummed[data.CanonicalSnapshotRole]
|
||||
if ok {
|
||||
// by this point, the SignedTimestamp has been validated so it must have a snapshot hash
|
||||
snMeta := ts.Signed.Meta[data.CanonicalSnapshotRole.String()].Hashes
|
||||
if err := data.CheckHashes(sn, data.CanonicalSnapshotRole.String(), snMeta); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(rb.loadedNotChecksummed, data.CanonicalSnapshotRole)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rb *repoBuilder) validateChecksumsFromSnapshot(sn *data.SignedSnapshot) error {
|
||||
var goodRoles []data.RoleName
|
||||
for roleName, loadedBytes := range rb.loadedNotChecksummed {
|
||||
switch roleName {
|
||||
case data.CanonicalSnapshotRole, data.CanonicalTimestampRole:
|
||||
break
|
||||
default:
|
||||
if err := data.CheckHashes(loadedBytes, roleName.String(), sn.Signed.Meta[roleName.String()].Hashes); err != nil {
|
||||
return err
|
||||
}
|
||||
goodRoles = append(goodRoles, roleName)
|
||||
}
|
||||
}
|
||||
for _, roleName := range goodRoles {
|
||||
delete(rb.loadedNotChecksummed, roleName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rb *repoBuilder) validateChecksumFor(content []byte, roleName data.RoleName) error {
|
||||
// validate the bootstrap checksum for root, if provided
|
||||
if roleName == data.CanonicalRootRole && rb.bootstrappedRootChecksum != nil {
|
||||
if err := data.CheckHashes(content, roleName.String(), rb.bootstrappedRootChecksum.Hashes); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// but we also want to cache the root content, so that when the snapshot is
|
||||
// loaded it is validated (to make sure everything in the repo is self-consistent)
|
||||
checksums := rb.getChecksumsFor(roleName)
|
||||
if checksums != nil { // as opposed to empty, in which case hash check should fail
|
||||
if err := data.CheckHashes(content, roleName.String(), *checksums); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if roleName != data.CanonicalTimestampRole {
|
||||
// timestamp is the only role which does not need to be checksummed, but
|
||||
// for everything else, cache the contents in the list of roles that have
|
||||
// not been checksummed by the snapshot/timestamp yet
|
||||
rb.loadedNotChecksummed[roleName] = content
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Checksums the given bytes, and if they validate, convert to a data.Signed object.
|
||||
// If a checksums are nil (as opposed to empty), adds the bytes to the list of roles that
|
||||
// haven't been checksummed (unless it's a timestamp, which has no checksum reference).
|
||||
func (rb *repoBuilder) bytesToSigned(content []byte, roleName data.RoleName, skipChecksum bool) (*data.Signed, error) {
|
||||
if !skipChecksum {
|
||||
if err := rb.validateChecksumFor(content, roleName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// unmarshal to signed
|
||||
signedObj := &data.Signed{}
|
||||
if err := json.Unmarshal(content, signedObj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return signedObj, nil
|
||||
}
|
||||
|
||||
func (rb *repoBuilder) bytesToSignedAndValidateSigs(role data.BaseRole, content []byte) (*data.Signed, error) {
|
||||
|
||||
signedObj, err := rb.bytesToSigned(content, role.Name, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// verify signature
|
||||
if err := signed.VerifySignatures(signedObj, role); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return signedObj, nil
|
||||
}
|
||||
|
||||
// If the checksum reference (the loaded timestamp for the snapshot role, and
|
||||
// the loaded snapshot for every other role except timestamp and snapshot) is nil,
|
||||
// then return nil for the checksums, meaning that the checksum is not yet
|
||||
// available. If the checksum reference *is* loaded, then always returns the
|
||||
// Hashes object for the given role - if it doesn't exist, returns an empty Hash
|
||||
// object (against which any checksum validation would fail).
|
||||
func (rb *repoBuilder) getChecksumsFor(role data.RoleName) *data.Hashes {
|
||||
var hashes data.Hashes
|
||||
switch role {
|
||||
case data.CanonicalTimestampRole:
|
||||
return nil
|
||||
case data.CanonicalSnapshotRole:
|
||||
if rb.repo.Timestamp == nil {
|
||||
return nil
|
||||
}
|
||||
hashes = rb.repo.Timestamp.Signed.Meta[data.CanonicalSnapshotRole.String()].Hashes
|
||||
default:
|
||||
if rb.repo.Snapshot == nil {
|
||||
return nil
|
||||
}
|
||||
hashes = rb.repo.Snapshot.Signed.Meta[role.String()].Hashes
|
||||
}
|
||||
return &hashes
|
||||
}
|
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
|
||||
}
|
111
vendor/github.com/theupdateframework/notary/tuf/signed/ed25519.go
generated
vendored
Normal file
111
vendor/github.com/theupdateframework/notary/tuf/signed/ed25519.go
generated
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
package signed
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
|
||||
"github.com/theupdateframework/notary/trustmanager"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"github.com/theupdateframework/notary/tuf/utils"
|
||||
)
|
||||
|
||||
type edCryptoKey struct {
|
||||
role data.RoleName
|
||||
privKey data.PrivateKey
|
||||
}
|
||||
|
||||
// Ed25519 implements a simple in memory cryptosystem for ED25519 keys
|
||||
type Ed25519 struct {
|
||||
keys map[string]edCryptoKey
|
||||
}
|
||||
|
||||
// NewEd25519 initializes a new empty Ed25519 CryptoService that operates
|
||||
// entirely in memory
|
||||
func NewEd25519() *Ed25519 {
|
||||
return &Ed25519{
|
||||
make(map[string]edCryptoKey),
|
||||
}
|
||||
}
|
||||
|
||||
// AddKey allows you to add a private key
|
||||
func (e *Ed25519) AddKey(role data.RoleName, gun data.GUN, k data.PrivateKey) error {
|
||||
e.addKey(role, k)
|
||||
return nil
|
||||
}
|
||||
|
||||
// addKey allows you to add a private key
|
||||
func (e *Ed25519) addKey(role data.RoleName, k data.PrivateKey) {
|
||||
e.keys[k.ID()] = edCryptoKey{
|
||||
role: role,
|
||||
privKey: k,
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveKey deletes a key from the signer
|
||||
func (e *Ed25519) RemoveKey(keyID string) error {
|
||||
delete(e.keys, keyID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListKeys returns the list of keys IDs for the role
|
||||
func (e *Ed25519) ListKeys(role data.RoleName) []string {
|
||||
keyIDs := make([]string, 0, len(e.keys))
|
||||
for id, edCryptoKey := range e.keys {
|
||||
if edCryptoKey.role == role {
|
||||
keyIDs = append(keyIDs, id)
|
||||
}
|
||||
}
|
||||
return keyIDs
|
||||
}
|
||||
|
||||
// ListAllKeys returns the map of keys IDs to role
|
||||
func (e *Ed25519) ListAllKeys() map[string]data.RoleName {
|
||||
keys := make(map[string]data.RoleName)
|
||||
for id, edKey := range e.keys {
|
||||
keys[id] = edKey.role
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Create generates a new key and returns the public part
|
||||
func (e *Ed25519) Create(role data.RoleName, gun data.GUN, algorithm string) (data.PublicKey, error) {
|
||||
if algorithm != data.ED25519Key {
|
||||
return nil, errors.New("only ED25519 supported by this cryptoservice")
|
||||
}
|
||||
|
||||
private, err := utils.GenerateED25519Key(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e.addKey(role, private)
|
||||
return data.PublicKeyFromPrivate(private), nil
|
||||
}
|
||||
|
||||
// PublicKeys returns a map of public keys for the ids provided, when those IDs are found
|
||||
// in the store.
|
||||
func (e *Ed25519) PublicKeys(keyIDs ...string) (map[string]data.PublicKey, error) {
|
||||
k := make(map[string]data.PublicKey)
|
||||
for _, keyID := range keyIDs {
|
||||
if edKey, ok := e.keys[keyID]; ok {
|
||||
k[keyID] = data.PublicKeyFromPrivate(edKey.privKey)
|
||||
}
|
||||
}
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// GetKey returns a single public key based on the ID
|
||||
func (e *Ed25519) GetKey(keyID string) data.PublicKey {
|
||||
if privKey, _, err := e.GetPrivateKey(keyID); err == nil {
|
||||
return data.PublicKeyFromPrivate(privKey)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPrivateKey returns a single private key and role if present, based on the ID
|
||||
func (e *Ed25519) GetPrivateKey(keyID string) (data.PrivateKey, data.RoleName, error) {
|
||||
if k, ok := e.keys[keyID]; ok {
|
||||
return k.privKey, k.role, nil
|
||||
}
|
||||
return nil, "", trustmanager.ErrKeyNotFound{KeyID: keyID}
|
||||
}
|
98
vendor/github.com/theupdateframework/notary/tuf/signed/errors.go
generated
vendored
Normal file
98
vendor/github.com/theupdateframework/notary/tuf/signed/errors.go
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
package signed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
||||
// ErrInsufficientSignatures - can not create enough signatures on a piece of
|
||||
// metadata
|
||||
type ErrInsufficientSignatures struct {
|
||||
FoundKeys int
|
||||
NeededKeys int
|
||||
MissingKeyIDs []string
|
||||
}
|
||||
|
||||
func (e ErrInsufficientSignatures) Error() string {
|
||||
candidates := ""
|
||||
if len(e.MissingKeyIDs) > 0 {
|
||||
candidates = fmt.Sprintf(" (%s)", strings.Join(e.MissingKeyIDs, ", "))
|
||||
}
|
||||
|
||||
if e.FoundKeys == 0 {
|
||||
return fmt.Sprintf("signing keys not available: need %d keys from %d possible keys%s",
|
||||
e.NeededKeys, len(e.MissingKeyIDs), candidates)
|
||||
}
|
||||
return fmt.Sprintf("not enough signing keys: found %d of %d needed keys - %d other possible keys%s",
|
||||
e.FoundKeys, e.NeededKeys, len(e.MissingKeyIDs), candidates)
|
||||
}
|
||||
|
||||
// ErrExpired indicates a piece of metadata has expired
|
||||
type ErrExpired struct {
|
||||
Role data.RoleName
|
||||
Expired string
|
||||
}
|
||||
|
||||
func (e ErrExpired) Error() string {
|
||||
return fmt.Sprintf("%s expired at %v", e.Role.String(), e.Expired)
|
||||
}
|
||||
|
||||
// ErrLowVersion indicates the piece of metadata has a version number lower than
|
||||
// a version number we're already seen for this role
|
||||
type ErrLowVersion struct {
|
||||
Actual int
|
||||
Current int
|
||||
}
|
||||
|
||||
func (e ErrLowVersion) Error() string {
|
||||
return fmt.Sprintf("version %d is lower than current version %d", e.Actual, e.Current)
|
||||
}
|
||||
|
||||
// ErrRoleThreshold indicates we did not validate enough signatures to meet the threshold
|
||||
type ErrRoleThreshold struct {
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (e ErrRoleThreshold) Error() string {
|
||||
if e.Msg == "" {
|
||||
return "valid signatures did not meet threshold"
|
||||
}
|
||||
return e.Msg
|
||||
}
|
||||
|
||||
// ErrInvalidKeyType indicates the types for the key and signature it's associated with are
|
||||
// mismatched. Probably a sign of malicious behaviour
|
||||
type ErrInvalidKeyType struct{}
|
||||
|
||||
func (e ErrInvalidKeyType) Error() string {
|
||||
return "key type is not valid for signature"
|
||||
}
|
||||
|
||||
// ErrInvalidKeyID indicates the specified key ID was incorrect for its associated data
|
||||
type ErrInvalidKeyID struct{}
|
||||
|
||||
func (e ErrInvalidKeyID) Error() string {
|
||||
return "key ID is not valid for key content"
|
||||
}
|
||||
|
||||
// ErrInvalidKeyLength indicates that while we may support the cipher, the provided
|
||||
// key length is not specifically supported, i.e. we support RSA, but not 1024 bit keys
|
||||
type ErrInvalidKeyLength struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e ErrInvalidKeyLength) Error() string {
|
||||
return fmt.Sprintf("key length is not supported: %s", e.msg)
|
||||
}
|
||||
|
||||
// ErrNoKeys indicates no signing keys were found when trying to sign
|
||||
type ErrNoKeys struct {
|
||||
KeyIDs []string
|
||||
}
|
||||
|
||||
func (e ErrNoKeys) Error() string {
|
||||
return fmt.Sprintf("could not find necessary signing keys, at least one of these keys must be available: %s",
|
||||
strings.Join(e.KeyIDs, ", "))
|
||||
}
|
47
vendor/github.com/theupdateframework/notary/tuf/signed/interface.go
generated
vendored
Normal file
47
vendor/github.com/theupdateframework/notary/tuf/signed/interface.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
package signed
|
||||
|
||||
import "github.com/theupdateframework/notary/tuf/data"
|
||||
|
||||
// KeyService provides management of keys locally. It will never
|
||||
// accept or provide private keys. Communication between the KeyService
|
||||
// and a SigningService happen behind the Create function.
|
||||
type KeyService interface {
|
||||
// Create issues a new key pair and is responsible for loading
|
||||
// the private key into the appropriate signing service.
|
||||
Create(role data.RoleName, gun data.GUN, algorithm string) (data.PublicKey, error)
|
||||
|
||||
// AddKey adds a private key to the specified role and gun
|
||||
AddKey(role data.RoleName, gun data.GUN, key data.PrivateKey) error
|
||||
|
||||
// GetKey retrieves the public key if present, otherwise it returns nil
|
||||
GetKey(keyID string) data.PublicKey
|
||||
|
||||
// GetPrivateKey retrieves the private key and role if present and retrievable,
|
||||
// otherwise it returns nil and an error
|
||||
GetPrivateKey(keyID string) (data.PrivateKey, data.RoleName, error)
|
||||
|
||||
// RemoveKey deletes the specified key, and returns an error only if the key
|
||||
// removal fails. If the key doesn't exist, no error should be returned.
|
||||
RemoveKey(keyID string) error
|
||||
|
||||
// ListKeys returns a list of key IDs for the role, or an empty list or
|
||||
// nil if there are no keys.
|
||||
ListKeys(role data.RoleName) []string
|
||||
|
||||
// ListAllKeys returns a map of all available signing key IDs to role, or
|
||||
// an empty map or nil if there are no keys.
|
||||
ListAllKeys() map[string]data.RoleName
|
||||
}
|
||||
|
||||
// CryptoService is deprecated and all instances of its use should be
|
||||
// replaced with KeyService
|
||||
type CryptoService interface {
|
||||
KeyService
|
||||
}
|
||||
|
||||
// Verifier defines an interface for verifying signatures. An implementer
|
||||
// of this interface should verify signatures for one and only one
|
||||
// signing scheme.
|
||||
type Verifier interface {
|
||||
Verify(key data.PublicKey, sig []byte, msg []byte) error
|
||||
}
|
114
vendor/github.com/theupdateframework/notary/tuf/signed/sign.go
generated
vendored
Normal file
114
vendor/github.com/theupdateframework/notary/tuf/signed/sign.go
generated
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
package signed
|
||||
|
||||
// The Sign function is a choke point for all code paths that do signing.
|
||||
// We use this fact to do key ID translation. There are 2 types of key ID:
|
||||
// - Scoped: the key ID based purely on the data that appears in the TUF
|
||||
// files. This may be wrapped by a certificate that scopes the
|
||||
// key to be used in a specific context.
|
||||
// - Canonical: the key ID based purely on the public key bytes. This is
|
||||
// used by keystores to easily identify keys that may be reused
|
||||
// in many scoped locations.
|
||||
// Currently these types only differ in the context of Root Keys in Notary
|
||||
// for which the root key is wrapped using an x509 certificate.
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary/trustmanager"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"github.com/theupdateframework/notary/tuf/utils"
|
||||
)
|
||||
|
||||
// Sign takes a data.Signed and a cryptoservice containing private keys,
|
||||
// calculates and adds at least minSignature signatures using signingKeys the
|
||||
// data.Signed. It will also clean up any signatures that are not in produced
|
||||
// by either a signingKey or an otherWhitelistedKey.
|
||||
// Note that in most cases, otherWhitelistedKeys should probably be null. They
|
||||
// are for keys you don't want to sign with, but you also don't want to remove
|
||||
// existing signatures by those keys. For instance, if you want to call Sign
|
||||
// multiple times with different sets of signing keys without undoing removing
|
||||
// signatures produced by the previous call to Sign.
|
||||
func Sign(service CryptoService, s *data.Signed, signingKeys []data.PublicKey,
|
||||
minSignatures int, otherWhitelistedKeys []data.PublicKey) error {
|
||||
|
||||
logrus.Debugf("sign called with %d/%d required keys", minSignatures, len(signingKeys))
|
||||
signatures := make([]data.Signature, 0, len(s.Signatures)+1)
|
||||
signingKeyIDs := make(map[string]struct{})
|
||||
tufIDs := make(map[string]data.PublicKey)
|
||||
|
||||
privKeys := make(map[string]data.PrivateKey)
|
||||
|
||||
// Get all the private key objects related to the public keys
|
||||
missingKeyIDs := []string{}
|
||||
for _, key := range signingKeys {
|
||||
canonicalID, err := utils.CanonicalKeyID(key)
|
||||
tufIDs[key.ID()] = key
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k, _, err := service.GetPrivateKey(canonicalID)
|
||||
if err != nil {
|
||||
if _, ok := err.(trustmanager.ErrKeyNotFound); ok {
|
||||
missingKeyIDs = append(missingKeyIDs, canonicalID)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
privKeys[key.ID()] = k
|
||||
}
|
||||
|
||||
// include the list of otherWhitelistedKeys
|
||||
for _, key := range otherWhitelistedKeys {
|
||||
if _, ok := tufIDs[key.ID()]; !ok {
|
||||
tufIDs[key.ID()] = key
|
||||
}
|
||||
}
|
||||
|
||||
// Check to ensure we have enough signing keys
|
||||
if len(privKeys) < minSignatures {
|
||||
return ErrInsufficientSignatures{FoundKeys: len(privKeys),
|
||||
NeededKeys: minSignatures, MissingKeyIDs: missingKeyIDs}
|
||||
}
|
||||
|
||||
emptyStruct := struct{}{}
|
||||
// Do signing and generate list of signatures
|
||||
for keyID, pk := range privKeys {
|
||||
sig, err := pk.Sign(rand.Reader, *s.Signed, nil)
|
||||
if err != nil {
|
||||
logrus.Debugf("Failed to sign with key: %s. Reason: %v", keyID, err)
|
||||
return err
|
||||
}
|
||||
signingKeyIDs[keyID] = emptyStruct
|
||||
signatures = append(signatures, data.Signature{
|
||||
KeyID: keyID,
|
||||
Method: pk.SignatureAlgorithm(),
|
||||
Signature: sig[:],
|
||||
})
|
||||
}
|
||||
|
||||
for i := range s.Signatures {
|
||||
sig := s.Signatures[i]
|
||||
if _, ok := signingKeyIDs[sig.KeyID]; ok {
|
||||
// key is in the set of key IDs for which a signature has been created
|
||||
continue
|
||||
}
|
||||
var (
|
||||
k data.PublicKey
|
||||
ok bool
|
||||
)
|
||||
if k, ok = tufIDs[sig.KeyID]; !ok {
|
||||
// key is no longer a valid signing key
|
||||
continue
|
||||
}
|
||||
if err := VerifySignature(*s.Signed, &sig, k); err != nil {
|
||||
// signature is no longer valid
|
||||
continue
|
||||
}
|
||||
// keep any signatures that still represent valid keys and are
|
||||
// themselves valid
|
||||
signatures = append(signatures, sig)
|
||||
}
|
||||
s.Signatures = signatures
|
||||
return nil
|
||||
}
|
264
vendor/github.com/theupdateframework/notary/tuf/signed/verifiers.go
generated
vendored
Normal file
264
vendor/github.com/theupdateframework/notary/tuf/signed/verifiers.go
generated
vendored
Normal file
@ -0,0 +1,264 @@
|
||||
package signed
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
const (
|
||||
minRSAKeySizeBit = 2048 // 2048 bits = 256 bytes
|
||||
minRSAKeySizeByte = minRSAKeySizeBit / 8
|
||||
)
|
||||
|
||||
// Verifiers serves as a map of all verifiers available on the system and
|
||||
// can be injected into a verificationService. For testing and configuration
|
||||
// purposes, it will not be used by default.
|
||||
var Verifiers = map[data.SigAlgorithm]Verifier{
|
||||
data.RSAPSSSignature: RSAPSSVerifier{},
|
||||
data.RSAPKCS1v15Signature: RSAPKCS1v15Verifier{},
|
||||
data.PyCryptoSignature: RSAPyCryptoVerifier{},
|
||||
data.ECDSASignature: ECDSAVerifier{},
|
||||
data.EDDSASignature: Ed25519Verifier{},
|
||||
}
|
||||
|
||||
// Ed25519Verifier used to verify Ed25519 signatures
|
||||
type Ed25519Verifier struct{}
|
||||
|
||||
// Verify checks that an ed25519 signature is valid
|
||||
func (v Ed25519Verifier) Verify(key data.PublicKey, sig []byte, msg []byte) error {
|
||||
if key.Algorithm() != data.ED25519Key {
|
||||
return ErrInvalidKeyType{}
|
||||
}
|
||||
sigBytes := make([]byte, ed25519.SignatureSize)
|
||||
if len(sig) != ed25519.SignatureSize {
|
||||
logrus.Debugf("signature length is incorrect, must be %d, was %d.", ed25519.SignatureSize, len(sig))
|
||||
return ErrInvalid
|
||||
}
|
||||
copy(sigBytes, sig)
|
||||
|
||||
keyBytes := make([]byte, ed25519.PublicKeySize)
|
||||
pub := key.Public()
|
||||
if len(pub) != ed25519.PublicKeySize {
|
||||
logrus.Errorf("public key is incorrect size, must be %d, was %d.", ed25519.PublicKeySize, len(pub))
|
||||
return ErrInvalidKeyLength{msg: fmt.Sprintf("ed25519 public key must be %d bytes.", ed25519.PublicKeySize)}
|
||||
}
|
||||
n := copy(keyBytes, key.Public())
|
||||
if n < ed25519.PublicKeySize {
|
||||
logrus.Errorf("failed to copy the key, must have %d bytes, copied %d bytes.", ed25519.PublicKeySize, n)
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
if !ed25519.Verify(ed25519.PublicKey(keyBytes), msg, sigBytes) {
|
||||
logrus.Debugf("failed ed25519 verification")
|
||||
return ErrInvalid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyPSS(key interface{}, digest, sig []byte) error {
|
||||
rsaPub, ok := key.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
logrus.Debugf("value was not an RSA public key")
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
if rsaPub.N.BitLen() < minRSAKeySizeBit {
|
||||
logrus.Debugf("RSA keys less than 2048 bits are not acceptable, provided key has length %d.", rsaPub.N.BitLen())
|
||||
return ErrInvalidKeyLength{msg: fmt.Sprintf("RSA key must be at least %d bits.", minRSAKeySizeBit)}
|
||||
}
|
||||
|
||||
if len(sig) < minRSAKeySizeByte {
|
||||
logrus.Debugf("RSA keys less than 2048 bits are not acceptable, provided signature has length %d.", len(sig))
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
opts := rsa.PSSOptions{SaltLength: sha256.Size, Hash: crypto.SHA256}
|
||||
if err := rsa.VerifyPSS(rsaPub, crypto.SHA256, digest[:], sig, &opts); err != nil {
|
||||
logrus.Debugf("failed RSAPSS verification: %s", err)
|
||||
return ErrInvalid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRSAPubKey(key data.PublicKey) (crypto.PublicKey, error) {
|
||||
algorithm := key.Algorithm()
|
||||
var pubKey crypto.PublicKey
|
||||
|
||||
switch algorithm {
|
||||
case data.RSAx509Key:
|
||||
pemCert, _ := pem.Decode([]byte(key.Public()))
|
||||
if pemCert == nil {
|
||||
logrus.Debugf("failed to decode PEM-encoded x509 certificate")
|
||||
return nil, ErrInvalid
|
||||
}
|
||||
cert, err := x509.ParseCertificate(pemCert.Bytes)
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to parse x509 certificate: %s\n", err)
|
||||
return nil, ErrInvalid
|
||||
}
|
||||
pubKey = cert.PublicKey
|
||||
case data.RSAKey:
|
||||
var err error
|
||||
pubKey, err = x509.ParsePKIXPublicKey(key.Public())
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to parse public key: %s\n", err)
|
||||
return nil, ErrInvalid
|
||||
}
|
||||
default:
|
||||
// only accept RSA keys
|
||||
logrus.Debugf("invalid key type for RSAPSS verifier: %s", algorithm)
|
||||
return nil, ErrInvalidKeyType{}
|
||||
}
|
||||
|
||||
return pubKey, nil
|
||||
}
|
||||
|
||||
// RSAPSSVerifier checks RSASSA-PSS signatures
|
||||
type RSAPSSVerifier struct{}
|
||||
|
||||
// Verify does the actual check.
|
||||
func (v RSAPSSVerifier) Verify(key data.PublicKey, sig []byte, msg []byte) error {
|
||||
// will return err if keytype is not a recognized RSA type
|
||||
pubKey, err := getRSAPubKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
digest := sha256.Sum256(msg)
|
||||
|
||||
return verifyPSS(pubKey, digest[:], sig)
|
||||
}
|
||||
|
||||
// RSAPKCS1v15Verifier checks RSA PKCS1v15 signatures
|
||||
type RSAPKCS1v15Verifier struct{}
|
||||
|
||||
// Verify does the actual verification
|
||||
func (v RSAPKCS1v15Verifier) Verify(key data.PublicKey, sig []byte, msg []byte) error {
|
||||
// will return err if keytype is not a recognized RSA type
|
||||
pubKey, err := getRSAPubKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
digest := sha256.Sum256(msg)
|
||||
|
||||
rsaPub, ok := pubKey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
logrus.Debugf("value was not an RSA public key")
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
if rsaPub.N.BitLen() < minRSAKeySizeBit {
|
||||
logrus.Debugf("RSA keys less than 2048 bits are not acceptable, provided key has length %d.", rsaPub.N.BitLen())
|
||||
return ErrInvalidKeyLength{msg: fmt.Sprintf("RSA key must be at least %d bits.", minRSAKeySizeBit)}
|
||||
}
|
||||
|
||||
if len(sig) < minRSAKeySizeByte {
|
||||
logrus.Debugf("RSA keys less than 2048 bits are not acceptable, provided signature has length %d.", len(sig))
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
if err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, digest[:], sig); err != nil {
|
||||
logrus.Errorf("Failed verification: %s", err.Error())
|
||||
return ErrInvalid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RSAPyCryptoVerifier checks RSASSA-PSS signatures
|
||||
type RSAPyCryptoVerifier struct{}
|
||||
|
||||
// Verify does the actual check.
|
||||
// N.B. We have not been able to make this work in a way that is compatible
|
||||
// with PyCrypto.
|
||||
func (v RSAPyCryptoVerifier) Verify(key data.PublicKey, sig []byte, msg []byte) error {
|
||||
digest := sha256.Sum256(msg)
|
||||
if key.Algorithm() != data.RSAKey {
|
||||
return ErrInvalidKeyType{}
|
||||
}
|
||||
|
||||
k, _ := pem.Decode([]byte(key.Public()))
|
||||
if k == nil {
|
||||
logrus.Debugf("failed to decode PEM-encoded x509 certificate")
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
pub, err := x509.ParsePKIXPublicKey(k.Bytes)
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to parse public key: %s\n", err)
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
return verifyPSS(pub, digest[:], sig)
|
||||
}
|
||||
|
||||
// ECDSAVerifier checks ECDSA signatures, decoding the keyType appropriately
|
||||
type ECDSAVerifier struct{}
|
||||
|
||||
// Verify does the actual check.
|
||||
func (v ECDSAVerifier) Verify(key data.PublicKey, sig []byte, msg []byte) error {
|
||||
algorithm := key.Algorithm()
|
||||
var pubKey crypto.PublicKey
|
||||
|
||||
switch algorithm {
|
||||
case data.ECDSAx509Key:
|
||||
pemCert, _ := pem.Decode([]byte(key.Public()))
|
||||
if pemCert == nil {
|
||||
logrus.Debugf("failed to decode PEM-encoded x509 certificate for keyID: %s", key.ID())
|
||||
logrus.Debugf("certificate bytes: %s", string(key.Public()))
|
||||
return ErrInvalid
|
||||
}
|
||||
cert, err := x509.ParseCertificate(pemCert.Bytes)
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to parse x509 certificate: %s\n", err)
|
||||
return ErrInvalid
|
||||
}
|
||||
pubKey = cert.PublicKey
|
||||
case data.ECDSAKey:
|
||||
var err error
|
||||
pubKey, err = x509.ParsePKIXPublicKey(key.Public())
|
||||
if err != nil {
|
||||
logrus.Debugf("Failed to parse private key for keyID: %s, %s\n", key.ID(), err)
|
||||
return ErrInvalid
|
||||
}
|
||||
default:
|
||||
// only accept ECDSA keys.
|
||||
logrus.Debugf("invalid key type for ECDSA verifier: %s", algorithm)
|
||||
return ErrInvalidKeyType{}
|
||||
}
|
||||
|
||||
ecdsaPubKey, ok := pubKey.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
logrus.Debugf("value isn't an ECDSA public key")
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
sigLength := len(sig)
|
||||
expectedOctetLength := 2 * ((ecdsaPubKey.Params().BitSize + 7) >> 3)
|
||||
if sigLength != expectedOctetLength {
|
||||
logrus.Debugf("signature had an unexpected length")
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
rBytes, sBytes := sig[:sigLength/2], sig[sigLength/2:]
|
||||
r := new(big.Int).SetBytes(rBytes)
|
||||
s := new(big.Int).SetBytes(sBytes)
|
||||
|
||||
digest := sha256.Sum256(msg)
|
||||
|
||||
if !ecdsa.Verify(ecdsaPubKey, digest[:], r, s) {
|
||||
logrus.Debugf("failed ECDSA signature validation")
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
123
vendor/github.com/theupdateframework/notary/tuf/signed/verify.go
generated
vendored
Normal file
123
vendor/github.com/theupdateframework/notary/tuf/signed/verify.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
package signed
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go/canonical/json"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"github.com/theupdateframework/notary/tuf/utils"
|
||||
)
|
||||
|
||||
// Various basic signing errors
|
||||
var (
|
||||
ErrNoSignatures = errors.New("tuf: data has no signatures")
|
||||
ErrInvalid = errors.New("tuf: signature verification failed")
|
||||
ErrWrongType = errors.New("tuf: meta file has wrong type")
|
||||
)
|
||||
|
||||
// IsExpired checks if the given time passed before the present time
|
||||
func IsExpired(t time.Time) bool {
|
||||
return t.Before(time.Now())
|
||||
}
|
||||
|
||||
// VerifyExpiry returns ErrExpired if the metadata is expired
|
||||
func VerifyExpiry(s *data.SignedCommon, role data.RoleName) error {
|
||||
if IsExpired(s.Expires) {
|
||||
logrus.Errorf("Metadata for %s expired", role)
|
||||
return ErrExpired{Role: role, Expired: s.Expires.Format("Mon Jan 2 15:04:05 MST 2006")}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyVersion returns ErrLowVersion if the metadata version is lower than the min version
|
||||
func VerifyVersion(s *data.SignedCommon, minVersion int) error {
|
||||
if s.Version < minVersion {
|
||||
return ErrLowVersion{Actual: s.Version, Current: minVersion}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifySignatures checks the we have sufficient valid signatures for the given role
|
||||
func VerifySignatures(s *data.Signed, roleData data.BaseRole) error {
|
||||
if len(s.Signatures) == 0 {
|
||||
return ErrNoSignatures
|
||||
}
|
||||
|
||||
if roleData.Threshold < 1 {
|
||||
return ErrRoleThreshold{}
|
||||
}
|
||||
logrus.Debugf("%s role has key IDs: %s", roleData.Name, strings.Join(roleData.ListKeyIDs(), ","))
|
||||
|
||||
// remarshal the signed part so we can verify the signature, since the signature has
|
||||
// to be of a canonically marshalled signed object
|
||||
var decoded map[string]interface{}
|
||||
if err := json.Unmarshal(*s.Signed, &decoded); err != nil {
|
||||
return err
|
||||
}
|
||||
msg, err := json.MarshalCanonical(decoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
valid := make(map[string]struct{})
|
||||
for i := range s.Signatures {
|
||||
sig := &(s.Signatures[i])
|
||||
logrus.Debug("verifying signature for key ID: ", sig.KeyID)
|
||||
key, ok := roleData.Keys[sig.KeyID]
|
||||
if !ok {
|
||||
logrus.Debugf("continuing b/c keyid lookup was nil: %s\n", sig.KeyID)
|
||||
continue
|
||||
}
|
||||
// Check that the signature key ID actually matches the content ID of the key
|
||||
if key.ID() != sig.KeyID {
|
||||
return ErrInvalidKeyID{}
|
||||
}
|
||||
if err := VerifySignature(msg, sig, key); err != nil {
|
||||
logrus.Debugf("continuing b/c %s", err.Error())
|
||||
continue
|
||||
}
|
||||
valid[sig.KeyID] = struct{}{}
|
||||
}
|
||||
if len(valid) < roleData.Threshold {
|
||||
return ErrRoleThreshold{
|
||||
Msg: fmt.Sprintf("valid signatures did not meet threshold for %s", roleData.Name),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifySignature checks a single signature and public key against a payload
|
||||
// If the signature is verified, the signature's is valid field will actually
|
||||
// be mutated to be equal to the boolean true
|
||||
func VerifySignature(msg []byte, sig *data.Signature, pk data.PublicKey) error {
|
||||
// method lookup is consistent due to Unmarshal JSON doing lower case for us.
|
||||
method := sig.Method
|
||||
verifier, ok := Verifiers[method]
|
||||
if !ok {
|
||||
return fmt.Errorf("signing method is not supported: %s", sig.Method)
|
||||
}
|
||||
|
||||
if err := verifier.Verify(pk, sig.Signature, msg); err != nil {
|
||||
return fmt.Errorf("signature was invalid")
|
||||
}
|
||||
sig.IsValid = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyPublicKeyMatchesPrivateKey checks if the private key and the public keys forms valid key pairs.
|
||||
// Supports both x509 certificate PublicKeys and non-certificate PublicKeys
|
||||
func VerifyPublicKeyMatchesPrivateKey(privKey data.PrivateKey, pubKey data.PublicKey) error {
|
||||
pubKeyID, err := utils.CanonicalKeyID(pubKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not verify key pair: %v", err)
|
||||
}
|
||||
if privKey == nil || pubKeyID != privKey.ID() {
|
||||
return fmt.Errorf("private key is nil or does not match public key")
|
||||
}
|
||||
return nil
|
||||
}
|
1072
vendor/github.com/theupdateframework/notary/tuf/tuf.go
generated
vendored
Normal file
1072
vendor/github.com/theupdateframework/notary/tuf/tuf.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
341
vendor/github.com/theupdateframework/notary/tuf/utils/pkcs8.go
generated
vendored
Normal file
341
vendor/github.com/theupdateframework/notary/tuf/utils/pkcs8.go
generated
vendored
Normal file
@ -0,0 +1,341 @@
|
||||
// Package utils contains tuf related utility functions however this file is hard
|
||||
// forked from https://github.com/youmark/pkcs8 package. It has been further modified
|
||||
// based on the requirements of Notary. For converting keys into PKCS#8 format,
|
||||
// original package expected *crypto.PrivateKey interface, which then type inferred
|
||||
// to either *rsa.PrivateKey or *ecdsa.PrivateKey depending on the need and later
|
||||
// converted to ASN.1 DER encoded form, this whole process was superfluous here as
|
||||
// keys are already being kept in ASN.1 DER format wrapped in data.PrivateKey
|
||||
// structure. With these changes, package has became tightly coupled with notary as
|
||||
// most of the method signatures have been updated. Moreover support for ED25519
|
||||
// keys has been added as well. License for original package is following:
|
||||
//
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// # Copyright (c) 2014 youmark
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1" // #nosec
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
||||
// Copy from crypto/x509
|
||||
var (
|
||||
oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
|
||||
oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
|
||||
oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
|
||||
// crypto/x509 doesn't have support for ED25519
|
||||
// http://www.oid-info.com/get/1.3.6.1.4.1.11591.15.1
|
||||
oidPublicKeyED25519 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11591, 15, 1}
|
||||
)
|
||||
|
||||
// Copy from crypto/x509
|
||||
var (
|
||||
oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33}
|
||||
oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
|
||||
oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
|
||||
oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
|
||||
)
|
||||
|
||||
// Copy from crypto/x509
|
||||
func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) {
|
||||
switch curve {
|
||||
case elliptic.P224():
|
||||
return oidNamedCurveP224, true
|
||||
case elliptic.P256():
|
||||
return oidNamedCurveP256, true
|
||||
case elliptic.P384():
|
||||
return oidNamedCurveP384, true
|
||||
case elliptic.P521():
|
||||
return oidNamedCurveP521, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Unecrypted PKCS8
|
||||
var (
|
||||
oidPKCS5PBKDF2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 12}
|
||||
oidPBES2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 13}
|
||||
oidAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42}
|
||||
)
|
||||
|
||||
type ecPrivateKey struct {
|
||||
Version int
|
||||
PrivateKey []byte
|
||||
NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"`
|
||||
PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"`
|
||||
}
|
||||
|
||||
type privateKeyInfo struct {
|
||||
Version int
|
||||
PrivateKeyAlgorithm []asn1.ObjectIdentifier
|
||||
PrivateKey []byte
|
||||
}
|
||||
|
||||
// Encrypted PKCS8
|
||||
type pbkdf2Params struct {
|
||||
Salt []byte
|
||||
IterationCount int
|
||||
}
|
||||
|
||||
type pbkdf2Algorithms struct {
|
||||
IDPBKDF2 asn1.ObjectIdentifier
|
||||
PBKDF2Params pbkdf2Params
|
||||
}
|
||||
|
||||
type pbkdf2Encs struct {
|
||||
EncryAlgo asn1.ObjectIdentifier
|
||||
IV []byte
|
||||
}
|
||||
|
||||
type pbes2Params struct {
|
||||
KeyDerivationFunc pbkdf2Algorithms
|
||||
EncryptionScheme pbkdf2Encs
|
||||
}
|
||||
|
||||
type pbes2Algorithms struct {
|
||||
IDPBES2 asn1.ObjectIdentifier
|
||||
PBES2Params pbes2Params
|
||||
}
|
||||
|
||||
type encryptedPrivateKeyInfo struct {
|
||||
EncryptionAlgorithm pbes2Algorithms
|
||||
EncryptedData []byte
|
||||
}
|
||||
|
||||
// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey.
|
||||
// copied from https://github.com/golang/go/blob/964639cc338db650ccadeafb7424bc8ebb2c0f6c/src/crypto/x509/pkcs8.go#L17
|
||||
type pkcs8 struct {
|
||||
Version int
|
||||
Algo pkix.AlgorithmIdentifier
|
||||
PrivateKey []byte
|
||||
}
|
||||
|
||||
func parsePKCS8ToTufKey(der []byte) (data.PrivateKey, error) {
|
||||
var key pkcs8
|
||||
|
||||
if _, err := asn1.Unmarshal(der, &key); err != nil {
|
||||
if _, ok := err.(asn1.StructuralError); ok {
|
||||
return nil, errors.New("could not decrypt private key")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if key.Algo.Algorithm.Equal(oidPublicKeyED25519) {
|
||||
tufED25519PrivateKey, err := ED25519ToPrivateKey(key.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not convert ed25519.PrivateKey to data.PrivateKey: %v", err)
|
||||
}
|
||||
|
||||
return tufED25519PrivateKey, nil
|
||||
}
|
||||
|
||||
privKey, err := x509.ParsePKCS8PrivateKey(der)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch priv := privKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
tufRSAPrivateKey, err := RSAToPrivateKey(priv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not convert rsa.PrivateKey to data.PrivateKey: %v", err)
|
||||
}
|
||||
|
||||
return tufRSAPrivateKey, nil
|
||||
case *ecdsa.PrivateKey:
|
||||
tufECDSAPrivateKey, err := ECDSAToPrivateKey(priv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not convert ecdsa.PrivateKey to data.PrivateKey: %v", err)
|
||||
}
|
||||
|
||||
return tufECDSAPrivateKey, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("unsupported key type")
|
||||
}
|
||||
|
||||
// ParsePKCS8ToTufKey requires PKCS#8 key in DER format and returns data.PrivateKey
|
||||
// Password should be provided in case of Encrypted PKCS#8 key, else it should be nil.
|
||||
func ParsePKCS8ToTufKey(der []byte, password []byte) (data.PrivateKey, error) {
|
||||
if password == nil {
|
||||
return parsePKCS8ToTufKey(der)
|
||||
}
|
||||
|
||||
var privKey encryptedPrivateKeyInfo
|
||||
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
|
||||
return nil, errors.New("pkcs8: only PKCS #5 v2.0 supported")
|
||||
}
|
||||
|
||||
if !privKey.EncryptionAlgorithm.IDPBES2.Equal(oidPBES2) {
|
||||
return nil, errors.New("pkcs8: only PBES2 supported")
|
||||
}
|
||||
|
||||
if !privKey.EncryptionAlgorithm.PBES2Params.KeyDerivationFunc.IDPBKDF2.Equal(oidPKCS5PBKDF2) {
|
||||
return nil, errors.New("pkcs8: only PBKDF2 supported")
|
||||
}
|
||||
|
||||
encParam := privKey.EncryptionAlgorithm.PBES2Params.EncryptionScheme
|
||||
kdfParam := privKey.EncryptionAlgorithm.PBES2Params.KeyDerivationFunc.PBKDF2Params
|
||||
|
||||
switch {
|
||||
case encParam.EncryAlgo.Equal(oidAES256CBC):
|
||||
iv := encParam.IV
|
||||
salt := kdfParam.Salt
|
||||
iter := kdfParam.IterationCount
|
||||
|
||||
encryptedKey := privKey.EncryptedData
|
||||
symkey := pbkdf2.Key(password, salt, iter, 32, sha1.New)
|
||||
block, err := aes.NewCipher(symkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(encryptedKey, encryptedKey)
|
||||
|
||||
// no need to explicitly remove padding, as ASN.1 unmarshalling will automatically discard it
|
||||
key, err := parsePKCS8ToTufKey(encryptedKey)
|
||||
if err != nil {
|
||||
return nil, errors.New("pkcs8: incorrect password")
|
||||
}
|
||||
|
||||
return key, nil
|
||||
default:
|
||||
return nil, errors.New("pkcs8: only AES-256-CBC supported")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func convertTUFKeyToPKCS8(priv data.PrivateKey) ([]byte, error) {
|
||||
var pkey privateKeyInfo
|
||||
|
||||
switch priv.Algorithm() {
|
||||
case data.RSAKey, data.RSAx509Key:
|
||||
// Per RFC5958, if publicKey is present, then version is set to v2(1) else version is set to v1(0).
|
||||
// But openssl set to v1 even publicKey is present
|
||||
pkey.Version = 0
|
||||
pkey.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 1)
|
||||
pkey.PrivateKeyAlgorithm[0] = oidPublicKeyRSA
|
||||
pkey.PrivateKey = priv.Private()
|
||||
case data.ECDSAKey, data.ECDSAx509Key:
|
||||
// To extract Curve value, parsing ECDSA key to *ecdsa.PrivateKey
|
||||
eckey, err := x509.ParseECPrivateKey(priv.Private())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oidNamedCurve, ok := oidFromNamedCurve(eckey.Curve)
|
||||
if !ok {
|
||||
return nil, errors.New("pkcs8: unknown elliptic curve")
|
||||
}
|
||||
|
||||
// Per RFC5958, if publicKey is present, then version is set to v2(1) else version is set to v1(0).
|
||||
// But openssl set to v1 even publicKey is present
|
||||
pkey.Version = 1
|
||||
pkey.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 2)
|
||||
pkey.PrivateKeyAlgorithm[0] = oidPublicKeyECDSA
|
||||
pkey.PrivateKeyAlgorithm[1] = oidNamedCurve
|
||||
pkey.PrivateKey = priv.Private()
|
||||
case data.ED25519Key:
|
||||
pkey.Version = 0
|
||||
pkey.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 1)
|
||||
pkey.PrivateKeyAlgorithm[0] = oidPublicKeyED25519
|
||||
pkey.PrivateKey = priv.Private()
|
||||
default:
|
||||
return nil, fmt.Errorf("algorithm %s not supported", priv.Algorithm())
|
||||
}
|
||||
|
||||
return asn1.Marshal(pkey)
|
||||
}
|
||||
|
||||
func convertTUFKeyToPKCS8Encrypted(priv data.PrivateKey, password []byte) ([]byte, error) {
|
||||
// Convert private key into PKCS8 format
|
||||
pkey, err := convertTUFKeyToPKCS8(priv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Calculate key from password based on PKCS5 algorithm
|
||||
// Use 8 byte salt, 16 byte IV, and 2048 iteration
|
||||
iter := 2048
|
||||
salt := make([]byte, 8)
|
||||
iv := make([]byte, 16)
|
||||
_, err = rand.Reader.Read(salt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = rand.Reader.Read(iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key := pbkdf2.Key(password, salt, iter, 32, sha1.New)
|
||||
|
||||
// Use AES256-CBC mode, pad plaintext with PKCS5 padding scheme
|
||||
padding := aes.BlockSize - len(pkey)%aes.BlockSize
|
||||
if padding > 0 {
|
||||
n := len(pkey)
|
||||
pkey = append(pkey, make([]byte, padding)...)
|
||||
for i := 0; i < padding; i++ {
|
||||
pkey[n+i] = byte(padding)
|
||||
}
|
||||
}
|
||||
|
||||
encryptedKey := make([]byte, len(pkey))
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(encryptedKey, pkey)
|
||||
|
||||
pbkdf2algo := pbkdf2Algorithms{oidPKCS5PBKDF2, pbkdf2Params{salt, iter}}
|
||||
pbkdf2encs := pbkdf2Encs{oidAES256CBC, iv}
|
||||
pbes2algo := pbes2Algorithms{oidPBES2, pbes2Params{pbkdf2algo, pbkdf2encs}}
|
||||
|
||||
encryptedPkey := encryptedPrivateKeyInfo{pbes2algo, encryptedKey}
|
||||
return asn1.Marshal(encryptedPkey)
|
||||
}
|
||||
|
||||
// ConvertTUFKeyToPKCS8 converts a private key (data.Private) to PKCS#8 and returns in DER format
|
||||
// if password is not nil, it would convert the Private Key to Encrypted PKCS#8.
|
||||
func ConvertTUFKeyToPKCS8(priv data.PrivateKey, password []byte) ([]byte, error) {
|
||||
if password == nil {
|
||||
return convertTUFKeyToPKCS8(priv)
|
||||
}
|
||||
return convertTUFKeyToPKCS8Encrypted(priv, password)
|
||||
}
|
31
vendor/github.com/theupdateframework/notary/tuf/utils/role_sort.go
generated
vendored
Normal file
31
vendor/github.com/theupdateframework/notary/tuf/utils/role_sort.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RoleList is a list of roles
|
||||
type RoleList []string
|
||||
|
||||
// Len returns the length of the list
|
||||
func (r RoleList) Len() int {
|
||||
return len(r)
|
||||
}
|
||||
|
||||
// Less returns true if the item at i should be sorted
|
||||
// before the item at j. It's an unstable partial ordering
|
||||
// based on the number of segments, separated by "/", in
|
||||
// the role name
|
||||
func (r RoleList) Less(i, j int) bool {
|
||||
segsI := strings.Split(r[i], "/")
|
||||
segsJ := strings.Split(r[j], "/")
|
||||
if len(segsI) == len(segsJ) {
|
||||
return r[i] < r[j]
|
||||
}
|
||||
return len(segsI) < len(segsJ)
|
||||
}
|
||||
|
||||
// Swap the items at 2 locations in the list
|
||||
func (r RoleList) Swap(i, j int) {
|
||||
r[i], r[j] = r[j], r[i]
|
||||
}
|
85
vendor/github.com/theupdateframework/notary/tuf/utils/stack.go
generated
vendored
Normal file
85
vendor/github.com/theupdateframework/notary/tuf/utils/stack.go
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ErrEmptyStack is used when an action that requires some
|
||||
// content is invoked and the stack is empty
|
||||
type ErrEmptyStack struct {
|
||||
action string
|
||||
}
|
||||
|
||||
func (err ErrEmptyStack) Error() string {
|
||||
return fmt.Sprintf("attempted to %s with empty stack", err.action)
|
||||
}
|
||||
|
||||
// ErrBadTypeCast is used by PopX functions when the item
|
||||
// cannot be typed to X
|
||||
type ErrBadTypeCast struct{}
|
||||
|
||||
func (err ErrBadTypeCast) Error() string {
|
||||
return "attempted to do a typed pop and item was not of type"
|
||||
}
|
||||
|
||||
// Stack is a simple type agnostic stack implementation
|
||||
type Stack struct {
|
||||
s []interface{}
|
||||
l sync.Mutex
|
||||
}
|
||||
|
||||
// NewStack create a new stack
|
||||
func NewStack() *Stack {
|
||||
s := &Stack{
|
||||
s: make([]interface{}, 0),
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Push adds an item to the top of the stack.
|
||||
func (s *Stack) Push(item interface{}) {
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
s.s = append(s.s, item)
|
||||
}
|
||||
|
||||
// Pop removes and returns the top item on the stack, or returns
|
||||
// ErrEmptyStack if the stack has no content
|
||||
func (s *Stack) Pop() (interface{}, error) {
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
l := len(s.s)
|
||||
if l > 0 {
|
||||
item := s.s[l-1]
|
||||
s.s = s.s[:l-1]
|
||||
return item, nil
|
||||
}
|
||||
return nil, ErrEmptyStack{action: "Pop"}
|
||||
}
|
||||
|
||||
// PopString attempts to cast the top item on the stack to the string type.
|
||||
// If this succeeds, it removes and returns the top item. If the item
|
||||
// is not of the string type, ErrBadTypeCast is returned. If the stack
|
||||
// is empty, ErrEmptyStack is returned
|
||||
func (s *Stack) PopString() (string, error) {
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
l := len(s.s)
|
||||
if l > 0 {
|
||||
item := s.s[l-1]
|
||||
if item, ok := item.(string); ok {
|
||||
s.s = s.s[:l-1]
|
||||
return item, nil
|
||||
}
|
||||
return "", ErrBadTypeCast{}
|
||||
}
|
||||
return "", ErrEmptyStack{action: "PopString"}
|
||||
}
|
||||
|
||||
// Empty returns true if the stack is empty
|
||||
func (s *Stack) Empty() bool {
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
return len(s.s) == 0
|
||||
}
|
119
vendor/github.com/theupdateframework/notary/tuf/utils/utils.go
generated
vendored
Normal file
119
vendor/github.com/theupdateframework/notary/tuf/utils/utils.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
||||
// StrSliceContains checks if the given string appears in the slice
|
||||
func StrSliceContains(ss []string, s string) bool {
|
||||
for _, v := range ss {
|
||||
if v == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RoleNameSliceContains checks if the given string appears in the slice
|
||||
func RoleNameSliceContains(ss []data.RoleName, s data.RoleName) bool {
|
||||
for _, v := range ss {
|
||||
if v == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RoleNameSliceRemove removes the given RoleName from the slice, returning a new slice
|
||||
func RoleNameSliceRemove(ss []data.RoleName, s data.RoleName) []data.RoleName {
|
||||
res := []data.RoleName{}
|
||||
for _, v := range ss {
|
||||
if v != s {
|
||||
res = append(res, v)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// NoopCloser is a simple Reader wrapper that does nothing when Close is
|
||||
// called
|
||||
type NoopCloser struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
// Close does nothing for a NoopCloser
|
||||
func (nc *NoopCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DoHash returns the digest of d using the hashing algorithm named
|
||||
// in alg
|
||||
func DoHash(alg string, d []byte) []byte {
|
||||
switch alg {
|
||||
case "sha256":
|
||||
digest := sha256.Sum256(d)
|
||||
return digest[:]
|
||||
case "sha512":
|
||||
digest := sha512.Sum512(d)
|
||||
return digest[:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnusedDelegationKeys prunes a list of keys, returning those that are no
|
||||
// longer in use for a given targets file
|
||||
func UnusedDelegationKeys(t data.SignedTargets) []string {
|
||||
// compare ids to all still active key ids in all active roles
|
||||
// with the targets file
|
||||
found := make(map[string]bool)
|
||||
for _, r := range t.Signed.Delegations.Roles {
|
||||
for _, id := range r.KeyIDs {
|
||||
found[id] = true
|
||||
}
|
||||
}
|
||||
var discard []string
|
||||
for id := range t.Signed.Delegations.Keys {
|
||||
if !found[id] {
|
||||
discard = append(discard, id)
|
||||
}
|
||||
}
|
||||
return discard
|
||||
}
|
||||
|
||||
// RemoveUnusedKeys determines which keys in the slice of IDs are no longer
|
||||
// used in the given targets file and removes them from the delegated keys
|
||||
// map
|
||||
func RemoveUnusedKeys(t *data.SignedTargets) {
|
||||
unusedIDs := UnusedDelegationKeys(*t)
|
||||
for _, id := range unusedIDs {
|
||||
delete(t.Signed.Delegations.Keys, id)
|
||||
}
|
||||
}
|
||||
|
||||
// FindRoleIndex returns the index of the role named <name> or -1 if no
|
||||
// matching role is found.
|
||||
func FindRoleIndex(rs []*data.Role, name data.RoleName) int {
|
||||
for i, r := range rs {
|
||||
if r.Name == name {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// ConsistentName generates the appropriate HTTP URL path for the role,
|
||||
// based on whether the repo is marked as consistent. The RemoteStore
|
||||
// is responsible for adding file extensions.
|
||||
func ConsistentName(role string, hashSHA256 []byte) string {
|
||||
if len(hashSHA256) > 0 {
|
||||
hash := hex.EncodeToString(hashSHA256)
|
||||
return fmt.Sprintf("%s.%s", role, hash)
|
||||
}
|
||||
return role
|
||||
}
|
564
vendor/github.com/theupdateframework/notary/tuf/utils/x509.go
generated
vendored
Normal file
564
vendor/github.com/theupdateframework/notary/tuf/utils/x509.go
generated
vendored
Normal file
@ -0,0 +1,564 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
// CanonicalKeyID returns the ID of the public bytes version of a TUF key.
|
||||
// On regular RSA/ECDSA TUF keys, this is just the key ID. On X509 RSA/ECDSA
|
||||
// TUF keys, this is the key ID of the public key part of the key in the leaf cert
|
||||
func CanonicalKeyID(k data.PublicKey) (string, error) {
|
||||
if k == nil {
|
||||
return "", errors.New("public key is nil")
|
||||
}
|
||||
switch k.Algorithm() {
|
||||
case data.ECDSAx509Key, data.RSAx509Key:
|
||||
return X509PublicKeyID(k)
|
||||
default:
|
||||
return k.ID(), nil
|
||||
}
|
||||
}
|
||||
|
||||
// LoadCertFromPEM returns the first certificate found in a bunch of bytes or error
|
||||
// if nothing is found. Taken from https://golang.org/src/crypto/x509/cert_pool.go#L85.
|
||||
func LoadCertFromPEM(pemBytes []byte) (*x509.Certificate, error) {
|
||||
for len(pemBytes) > 0 {
|
||||
var block *pem.Block
|
||||
block, pemBytes = pem.Decode(pemBytes)
|
||||
if block == nil {
|
||||
return nil, errors.New("no certificates found in PEM data")
|
||||
}
|
||||
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("no certificates found in PEM data")
|
||||
}
|
||||
|
||||
// X509PublicKeyID returns a public key ID as a string, given a
|
||||
// data.PublicKey that contains an X509 Certificate
|
||||
func X509PublicKeyID(certPubKey data.PublicKey) (string, error) {
|
||||
// Note that this only loads the first certificate from the public key
|
||||
cert, err := LoadCertFromPEM(certPubKey.Public())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pubKeyBytes, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var key data.PublicKey
|
||||
switch certPubKey.Algorithm() {
|
||||
case data.ECDSAx509Key:
|
||||
key = data.NewECDSAPublicKey(pubKeyBytes)
|
||||
case data.RSAx509Key:
|
||||
key = data.NewRSAPublicKey(pubKeyBytes)
|
||||
}
|
||||
|
||||
return key.ID(), nil
|
||||
}
|
||||
|
||||
func parseLegacyPrivateKey(block *pem.Block, passphrase string) (data.PrivateKey, error) {
|
||||
var privKeyBytes []byte
|
||||
var err error
|
||||
if x509.IsEncryptedPEMBlock(block) {
|
||||
privKeyBytes, err = x509.DecryptPEMBlock(block, []byte(passphrase))
|
||||
if err != nil {
|
||||
return nil, errors.New("could not decrypt private key")
|
||||
}
|
||||
} else {
|
||||
privKeyBytes = block.Bytes
|
||||
}
|
||||
|
||||
switch block.Type {
|
||||
case "RSA PRIVATE KEY":
|
||||
rsaPrivKey, err := x509.ParsePKCS1PrivateKey(privKeyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse DER encoded key: %v", err)
|
||||
}
|
||||
|
||||
tufRSAPrivateKey, err := RSAToPrivateKey(rsaPrivKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not convert rsa.PrivateKey to data.PrivateKey: %v", err)
|
||||
}
|
||||
|
||||
return tufRSAPrivateKey, nil
|
||||
case "EC PRIVATE KEY":
|
||||
ecdsaPrivKey, err := x509.ParseECPrivateKey(privKeyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse DER encoded private key: %v", err)
|
||||
}
|
||||
|
||||
tufECDSAPrivateKey, err := ECDSAToPrivateKey(ecdsaPrivKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not convert ecdsa.PrivateKey to data.PrivateKey: %v", err)
|
||||
}
|
||||
|
||||
return tufECDSAPrivateKey, nil
|
||||
case "ED25519 PRIVATE KEY":
|
||||
// We serialize ED25519 keys by concatenating the private key
|
||||
// to the public key and encoding with PEM. See the
|
||||
// ED25519ToPrivateKey function.
|
||||
tufECDSAPrivateKey, err := ED25519ToPrivateKey(privKeyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not convert ecdsa.PrivateKey to data.PrivateKey: %v", err)
|
||||
}
|
||||
|
||||
return tufECDSAPrivateKey, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported key type %q", block.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// ParsePEMPrivateKey returns a data.PrivateKey from a PEM encoded private key. It
|
||||
// supports PKCS#8 as well as RSA/ECDSA (PKCS#1) only in non-FIPS mode and
|
||||
// attempts to decrypt using the passphrase, if encrypted.
|
||||
func ParsePEMPrivateKey(pemBytes []byte, passphrase string) (data.PrivateKey, error) {
|
||||
return parsePEMPrivateKey(pemBytes, passphrase, notary.FIPSEnabled())
|
||||
}
|
||||
|
||||
func parsePEMPrivateKey(pemBytes []byte, passphrase string, fips bool) (data.PrivateKey, error) {
|
||||
block, _ := pem.Decode(pemBytes)
|
||||
if block == nil {
|
||||
return nil, errors.New("no valid private key found")
|
||||
}
|
||||
|
||||
switch block.Type {
|
||||
case "RSA PRIVATE KEY", "EC PRIVATE KEY", "ED25519 PRIVATE KEY":
|
||||
if fips {
|
||||
return nil, fmt.Errorf("%s not supported in FIPS mode", block.Type)
|
||||
}
|
||||
return parseLegacyPrivateKey(block, passphrase)
|
||||
case "ENCRYPTED PRIVATE KEY", "PRIVATE KEY":
|
||||
if passphrase == "" {
|
||||
return ParsePKCS8ToTufKey(block.Bytes, nil)
|
||||
}
|
||||
return ParsePKCS8ToTufKey(block.Bytes, []byte(passphrase))
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported key type %q", block.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// CertToPEM is a utility function returns a PEM encoded x509 Certificate
|
||||
func CertToPEM(cert *x509.Certificate) []byte {
|
||||
pemCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
|
||||
|
||||
return pemCert
|
||||
}
|
||||
|
||||
// CertChainToPEM is a utility function returns a PEM encoded chain of x509 Certificates, in the order they are passed
|
||||
func CertChainToPEM(certChain []*x509.Certificate) ([]byte, error) {
|
||||
var pemBytes bytes.Buffer
|
||||
for _, cert := range certChain {
|
||||
if err := pem.Encode(&pemBytes, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return pemBytes.Bytes(), nil
|
||||
}
|
||||
|
||||
// LoadCertFromFile loads the first certificate from the file provided. The
|
||||
// data is expected to be PEM Encoded and contain one of more certificates
|
||||
// with PEM type "CERTIFICATE"
|
||||
func LoadCertFromFile(filename string) (*x509.Certificate, error) {
|
||||
certs, err := LoadCertBundleFromFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return certs[0], nil
|
||||
}
|
||||
|
||||
// LoadCertBundleFromFile loads certificates from the []byte provided. The
|
||||
// data is expected to be PEM Encoded and contain one of more certificates
|
||||
// with PEM type "CERTIFICATE"
|
||||
func LoadCertBundleFromFile(filename string) ([]*x509.Certificate, error) {
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return LoadCertBundleFromPEM(b)
|
||||
}
|
||||
|
||||
// LoadCertBundleFromPEM loads certificates from the []byte provided. The
|
||||
// data is expected to be PEM Encoded and contain one of more certificates
|
||||
// with PEM type "CERTIFICATE"
|
||||
func LoadCertBundleFromPEM(pemBytes []byte) ([]*x509.Certificate, error) {
|
||||
certificates := []*x509.Certificate{}
|
||||
var block *pem.Block
|
||||
block, pemBytes = pem.Decode(pemBytes)
|
||||
for ; block != nil; block, pemBytes = pem.Decode(pemBytes) {
|
||||
if block.Type == "CERTIFICATE" {
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certificates = append(certificates, cert)
|
||||
} else {
|
||||
return nil, fmt.Errorf("invalid pem block type: %s", block.Type)
|
||||
}
|
||||
}
|
||||
|
||||
if len(certificates) == 0 {
|
||||
return nil, fmt.Errorf("no valid certificates found")
|
||||
}
|
||||
|
||||
return certificates, nil
|
||||
}
|
||||
|
||||
// GetLeafCerts parses a list of x509 Certificates and returns all of them
|
||||
// that aren't CA
|
||||
func GetLeafCerts(certs []*x509.Certificate) []*x509.Certificate {
|
||||
var leafCerts []*x509.Certificate
|
||||
for _, cert := range certs {
|
||||
if cert.IsCA {
|
||||
continue
|
||||
}
|
||||
leafCerts = append(leafCerts, cert)
|
||||
}
|
||||
return leafCerts
|
||||
}
|
||||
|
||||
// GetIntermediateCerts parses a list of x509 Certificates and returns all of the
|
||||
// ones marked as a CA, to be used as intermediates
|
||||
func GetIntermediateCerts(certs []*x509.Certificate) []*x509.Certificate {
|
||||
var intCerts []*x509.Certificate
|
||||
for _, cert := range certs {
|
||||
if cert.IsCA {
|
||||
intCerts = append(intCerts, cert)
|
||||
}
|
||||
}
|
||||
return intCerts
|
||||
}
|
||||
|
||||
// ParsePEMPublicKey returns a data.PublicKey from a PEM encoded public key or certificate.
|
||||
func ParsePEMPublicKey(pubKeyBytes []byte) (data.PublicKey, error) {
|
||||
pemBlock, _ := pem.Decode(pubKeyBytes)
|
||||
if pemBlock == nil {
|
||||
return nil, errors.New("no valid public key found")
|
||||
}
|
||||
|
||||
switch pemBlock.Type {
|
||||
case "CERTIFICATE":
|
||||
cert, err := x509.ParseCertificate(pemBlock.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse provided certificate: %v", err)
|
||||
}
|
||||
err = ValidateCertificate(cert, true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid certificate: %v", err)
|
||||
}
|
||||
return CertToKey(cert), nil
|
||||
case "PUBLIC KEY":
|
||||
keyType, err := keyTypeForPublicKey(pemBlock.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data.NewPublicKey(keyType, pemBlock.Bytes), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported PEM block type %q, expected CERTIFICATE or PUBLIC KEY", pemBlock.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func keyTypeForPublicKey(pubKeyBytes []byte) (string, error) {
|
||||
pub, err := x509.ParsePKIXPublicKey(pubKeyBytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to parse pem encoded public key: %v", err)
|
||||
}
|
||||
switch pub.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
return data.ECDSAKey, nil
|
||||
case *rsa.PublicKey:
|
||||
return data.RSAKey, nil
|
||||
}
|
||||
return "", fmt.Errorf("unknown public key format")
|
||||
}
|
||||
|
||||
// ValidateCertificate returns an error if the certificate is not valid for notary
|
||||
// Currently this is only ensuring the public key has a large enough modulus if RSA,
|
||||
// using a non SHA1 signature algorithm, and an optional time expiry check
|
||||
func ValidateCertificate(c *x509.Certificate, checkExpiry bool) error {
|
||||
if (c.NotBefore).After(c.NotAfter) {
|
||||
return fmt.Errorf("certificate validity window is invalid")
|
||||
}
|
||||
// Can't have SHA1 sig algorithm
|
||||
if c.SignatureAlgorithm == x509.SHA1WithRSA || c.SignatureAlgorithm == x509.DSAWithSHA1 || c.SignatureAlgorithm == x509.ECDSAWithSHA1 {
|
||||
return fmt.Errorf("certificate with CN %s uses invalid SHA1 signature algorithm", c.Subject.CommonName)
|
||||
}
|
||||
// If we have an RSA key, make sure it's long enough
|
||||
if c.PublicKeyAlgorithm == x509.RSA {
|
||||
rsaKey, ok := c.PublicKey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("unable to parse RSA public key")
|
||||
}
|
||||
if rsaKey.N.BitLen() < notary.MinRSABitSize {
|
||||
return fmt.Errorf("RSA bit length is too short")
|
||||
}
|
||||
}
|
||||
if checkExpiry {
|
||||
now := time.Now()
|
||||
tomorrow := now.AddDate(0, 0, 1)
|
||||
// Give one day leeway on creation "before" time, check "after" against today
|
||||
if (tomorrow).Before(c.NotBefore) || now.After(c.NotAfter) {
|
||||
return data.ErrCertExpired{CN: c.Subject.CommonName}
|
||||
}
|
||||
// If this certificate is expiring within 6 months, put out a warning
|
||||
if (c.NotAfter).Before(time.Now().AddDate(0, 6, 0)) {
|
||||
logrus.Warnf("certificate with CN %s is near expiry", c.Subject.CommonName)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateKey returns a new private key using the provided algorithm or an
|
||||
// error detailing why the key could not be generated
|
||||
func GenerateKey(algorithm string) (data.PrivateKey, error) {
|
||||
switch algorithm {
|
||||
case data.ECDSAKey:
|
||||
return GenerateECDSAKey(rand.Reader)
|
||||
case data.ED25519Key:
|
||||
return GenerateED25519Key(rand.Reader)
|
||||
}
|
||||
return nil, fmt.Errorf("private key type not supported for key generation: %s", algorithm)
|
||||
}
|
||||
|
||||
// RSAToPrivateKey converts an rsa.Private key to a TUF data.PrivateKey type
|
||||
func RSAToPrivateKey(rsaPrivKey *rsa.PrivateKey) (data.PrivateKey, error) {
|
||||
// Get a DER-encoded representation of the PublicKey
|
||||
rsaPubBytes, err := x509.MarshalPKIXPublicKey(&rsaPrivKey.PublicKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal public key: %v", err)
|
||||
}
|
||||
|
||||
// Get a DER-encoded representation of the PrivateKey
|
||||
rsaPrivBytes := x509.MarshalPKCS1PrivateKey(rsaPrivKey)
|
||||
|
||||
pubKey := data.NewRSAPublicKey(rsaPubBytes)
|
||||
return data.NewRSAPrivateKey(pubKey, rsaPrivBytes)
|
||||
}
|
||||
|
||||
// GenerateECDSAKey generates an ECDSA Private key and returns a TUF PrivateKey
|
||||
func GenerateECDSAKey(random io.Reader) (data.PrivateKey, error) {
|
||||
ecdsaPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), random)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tufPrivKey, err := ECDSAToPrivateKey(ecdsaPrivKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logrus.Debugf("generated ECDSA key with keyID: %s", tufPrivKey.ID())
|
||||
|
||||
return tufPrivKey, nil
|
||||
}
|
||||
|
||||
// GenerateED25519Key generates an ED25519 private key and returns a TUF
|
||||
// PrivateKey. The serialization format we use is just the public key bytes
|
||||
// followed by the private key bytes
|
||||
func GenerateED25519Key(random io.Reader) (data.PrivateKey, error) {
|
||||
pub, priv, err := ed25519.GenerateKey(random)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var serialized [ed25519.PublicKeySize + ed25519.PrivateKeySize]byte
|
||||
copy(serialized[:], pub[:])
|
||||
copy(serialized[ed25519.PublicKeySize:], priv[:])
|
||||
|
||||
tufPrivKey, err := ED25519ToPrivateKey(serialized[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logrus.Debugf("generated ED25519 key with keyID: %s", tufPrivKey.ID())
|
||||
|
||||
return tufPrivKey, nil
|
||||
}
|
||||
|
||||
// ECDSAToPrivateKey converts an ecdsa.Private key to a TUF data.PrivateKey type
|
||||
func ECDSAToPrivateKey(ecdsaPrivKey *ecdsa.PrivateKey) (data.PrivateKey, error) {
|
||||
// Get a DER-encoded representation of the PublicKey
|
||||
ecdsaPubBytes, err := x509.MarshalPKIXPublicKey(&ecdsaPrivKey.PublicKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal public key: %v", err)
|
||||
}
|
||||
|
||||
// Get a DER-encoded representation of the PrivateKey
|
||||
ecdsaPrivKeyBytes, err := x509.MarshalECPrivateKey(ecdsaPrivKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal private key: %v", err)
|
||||
}
|
||||
|
||||
pubKey := data.NewECDSAPublicKey(ecdsaPubBytes)
|
||||
return data.NewECDSAPrivateKey(pubKey, ecdsaPrivKeyBytes)
|
||||
}
|
||||
|
||||
// ED25519ToPrivateKey converts a serialized ED25519 key to a TUF
|
||||
// data.PrivateKey type
|
||||
func ED25519ToPrivateKey(privKeyBytes []byte) (data.PrivateKey, error) {
|
||||
if len(privKeyBytes) != ed25519.PublicKeySize+ed25519.PrivateKeySize {
|
||||
return nil, errors.New("malformed ed25519 private key")
|
||||
}
|
||||
|
||||
pubKey := data.NewED25519PublicKey(privKeyBytes[:ed25519.PublicKeySize])
|
||||
return data.NewED25519PrivateKey(*pubKey, privKeyBytes)
|
||||
}
|
||||
|
||||
// ExtractPrivateKeyAttributes extracts role and gun values from private key bytes
|
||||
func ExtractPrivateKeyAttributes(pemBytes []byte) (data.RoleName, data.GUN, error) {
|
||||
return extractPrivateKeyAttributes(pemBytes, notary.FIPSEnabled())
|
||||
}
|
||||
|
||||
func extractPrivateKeyAttributes(pemBytes []byte, fips bool) (data.RoleName, data.GUN, error) {
|
||||
block, _ := pem.Decode(pemBytes)
|
||||
if block == nil {
|
||||
return "", "", errors.New("PEM block is empty")
|
||||
}
|
||||
|
||||
switch block.Type {
|
||||
case "RSA PRIVATE KEY", "EC PRIVATE KEY", "ED25519 PRIVATE KEY":
|
||||
if fips {
|
||||
return "", "", fmt.Errorf("%s not supported in FIPS mode", block.Type)
|
||||
}
|
||||
case "PRIVATE KEY", "ENCRYPTED PRIVATE KEY":
|
||||
// do nothing for PKCS#8 keys
|
||||
default:
|
||||
return "", "", errors.New("unknown key format")
|
||||
}
|
||||
return data.RoleName(block.Headers["role"]), data.GUN(block.Headers["gun"]), nil
|
||||
}
|
||||
|
||||
// ConvertPrivateKeyToPKCS8 converts a data.PrivateKey to PKCS#8 Format
|
||||
func ConvertPrivateKeyToPKCS8(key data.PrivateKey, role data.RoleName, gun data.GUN, passphrase string) ([]byte, error) {
|
||||
var (
|
||||
err error
|
||||
der []byte
|
||||
blockType = "PRIVATE KEY"
|
||||
)
|
||||
|
||||
if passphrase == "" {
|
||||
der, err = ConvertTUFKeyToPKCS8(key, nil)
|
||||
} else {
|
||||
blockType = "ENCRYPTED PRIVATE KEY"
|
||||
der, err = ConvertTUFKeyToPKCS8(key, []byte(passphrase))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to convert to PKCS8 key")
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
if role != "" {
|
||||
headers["role"] = role.String()
|
||||
}
|
||||
|
||||
if gun != "" {
|
||||
headers["gun"] = gun.String()
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(&pem.Block{Bytes: der, Type: blockType, Headers: headers}), nil
|
||||
}
|
||||
|
||||
// CertToKey transforms a single input certificate into its corresponding
|
||||
// PublicKey
|
||||
func CertToKey(cert *x509.Certificate) data.PublicKey {
|
||||
block := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
|
||||
pemdata := pem.EncodeToMemory(&block)
|
||||
|
||||
switch cert.PublicKeyAlgorithm {
|
||||
case x509.RSA:
|
||||
return data.NewRSAx509PublicKey(pemdata)
|
||||
case x509.ECDSA:
|
||||
return data.NewECDSAx509PublicKey(pemdata)
|
||||
default:
|
||||
logrus.Debugf("Unknown key type parsed from certificate: %v", cert.PublicKeyAlgorithm)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// CertsToKeys transforms each of the input certificate chains into its corresponding
|
||||
// PublicKey
|
||||
func CertsToKeys(leafCerts map[string]*x509.Certificate, intCerts map[string][]*x509.Certificate) map[string]data.PublicKey {
|
||||
keys := make(map[string]data.PublicKey)
|
||||
for id, leafCert := range leafCerts {
|
||||
if key, err := CertBundleToKey(leafCert, intCerts[id]); err == nil {
|
||||
keys[key.ID()] = key
|
||||
}
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// CertBundleToKey creates a TUF key from a leaf certs and a list of
|
||||
// intermediates
|
||||
func CertBundleToKey(leafCert *x509.Certificate, intCerts []*x509.Certificate) (data.PublicKey, error) {
|
||||
certBundle := []*x509.Certificate{leafCert}
|
||||
certBundle = append(certBundle, intCerts...)
|
||||
certChainPEM, err := CertChainToPEM(certBundle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var newKey data.PublicKey
|
||||
// Use the leaf cert's public key algorithm for typing
|
||||
switch leafCert.PublicKeyAlgorithm {
|
||||
case x509.RSA:
|
||||
newKey = data.NewRSAx509PublicKey(certChainPEM)
|
||||
case x509.ECDSA:
|
||||
newKey = data.NewECDSAx509PublicKey(certChainPEM)
|
||||
default:
|
||||
logrus.Debugf("Unknown key type parsed from certificate: %v", leafCert.PublicKeyAlgorithm)
|
||||
return nil, x509.ErrUnsupportedAlgorithm
|
||||
}
|
||||
return newKey, nil
|
||||
}
|
||||
|
||||
// NewCertificate returns an X509 Certificate following a template, given a Common Name and validity interval.
|
||||
func NewCertificate(commonName string, startTime, endTime time.Time) (*x509.Certificate, error) {
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate new certificate: %v", err)
|
||||
}
|
||||
|
||||
return &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
CommonName: commonName,
|
||||
},
|
||||
NotBefore: startTime,
|
||||
NotAfter: endTime,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning},
|
||||
BasicConstraintsValid: true,
|
||||
}, nil
|
||||
}
|
126
vendor/github.com/theupdateframework/notary/tuf/validation/errors.go
generated
vendored
Normal file
126
vendor/github.com/theupdateframework/notary/tuf/validation/errors.go
generated
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
package validation
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// VALIDATION ERRORS
|
||||
|
||||
// ErrValidation represents a general validation error
|
||||
type ErrValidation struct {
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (err ErrValidation) Error() string {
|
||||
return fmt.Sprintf("An error occurred during validation: %s", err.Msg)
|
||||
}
|
||||
|
||||
// ErrBadHierarchy represents missing metadata. Currently: a missing snapshot
|
||||
// at this current time. When delegations are implemented it will also
|
||||
// represent a missing delegation parent
|
||||
type ErrBadHierarchy struct {
|
||||
Missing string
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (err ErrBadHierarchy) Error() string {
|
||||
return fmt.Sprintf("Metadata hierarchy is incomplete: %s", err.Msg)
|
||||
}
|
||||
|
||||
// ErrBadRoot represents a failure validating the root
|
||||
type ErrBadRoot struct {
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (err ErrBadRoot) Error() string {
|
||||
return fmt.Sprintf("The root metadata is invalid: %s", err.Msg)
|
||||
}
|
||||
|
||||
// ErrBadTargets represents a failure to validate a targets (incl delegations)
|
||||
type ErrBadTargets struct {
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (err ErrBadTargets) Error() string {
|
||||
return fmt.Sprintf("The targets metadata is invalid: %s", err.Msg)
|
||||
}
|
||||
|
||||
// ErrBadSnapshot represents a failure to validate the snapshot
|
||||
type ErrBadSnapshot struct {
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (err ErrBadSnapshot) Error() string {
|
||||
return fmt.Sprintf("The snapshot metadata is invalid: %s", err.Msg)
|
||||
}
|
||||
|
||||
// END VALIDATION ERRORS
|
||||
|
||||
// SerializableError is a struct that can be used to serialize an error as JSON
|
||||
type SerializableError struct {
|
||||
Name string
|
||||
Error error
|
||||
}
|
||||
|
||||
// UnmarshalJSON attempts to unmarshal the error into the right type
|
||||
func (s *SerializableError) UnmarshalJSON(text []byte) (err error) {
|
||||
var x struct{ Name string }
|
||||
err = json.Unmarshal(text, &x)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var theError error
|
||||
switch x.Name {
|
||||
case "ErrValidation":
|
||||
var e struct{ Error ErrValidation }
|
||||
err = json.Unmarshal(text, &e)
|
||||
theError = e.Error
|
||||
case "ErrBadHierarchy":
|
||||
var e struct{ Error ErrBadHierarchy }
|
||||
err = json.Unmarshal(text, &e)
|
||||
theError = e.Error
|
||||
case "ErrBadRoot":
|
||||
var e struct{ Error ErrBadRoot }
|
||||
err = json.Unmarshal(text, &e)
|
||||
theError = e.Error
|
||||
case "ErrBadTargets":
|
||||
var e struct{ Error ErrBadTargets }
|
||||
err = json.Unmarshal(text, &e)
|
||||
theError = e.Error
|
||||
case "ErrBadSnapshot":
|
||||
var e struct{ Error ErrBadSnapshot }
|
||||
err = json.Unmarshal(text, &e)
|
||||
theError = e.Error
|
||||
default:
|
||||
err = fmt.Errorf("do not know how to unmarshal %s", x.Name)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.Name = x.Name
|
||||
s.Error = theError
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSerializableError serializes one of the above errors into JSON
|
||||
func NewSerializableError(err error) (*SerializableError, error) {
|
||||
// make sure it's one of our errors
|
||||
var name string
|
||||
switch err.(type) {
|
||||
case ErrValidation:
|
||||
name = "ErrValidation"
|
||||
case ErrBadHierarchy:
|
||||
name = "ErrBadHierarchy"
|
||||
case ErrBadRoot:
|
||||
name = "ErrBadRoot"
|
||||
case ErrBadTargets:
|
||||
name = "ErrBadTargets"
|
||||
case ErrBadSnapshot:
|
||||
name = "ErrBadSnapshot"
|
||||
default:
|
||||
return nil, fmt.Errorf("does not support serializing non-validation errors")
|
||||
}
|
||||
return &SerializableError{Name: name, Error: err}, nil
|
||||
}
|
Reference in New Issue
Block a user