forked from toolshed/abra
chore: vendor
This commit is contained in:
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
|
||||
}
|
Reference in New Issue
Block a user