2017-04-17 18:08:24 -04:00
|
|
|
package data
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto"
|
|
|
|
"crypto/ecdsa"
|
|
|
|
"crypto/rsa"
|
|
|
|
"crypto/sha256"
|
|
|
|
"crypto/x509"
|
|
|
|
"encoding/asn1"
|
|
|
|
"encoding/hex"
|
|
|
|
"errors"
|
|
|
|
"io"
|
|
|
|
"math/big"
|
|
|
|
|
|
|
|
"github.com/agl/ed25519"
|
|
|
|
"github.com/docker/go/canonical/json"
|
2017-09-11 17:07:00 -04:00
|
|
|
"github.com/sirupsen/logrus"
|
2017-04-17 18:08:24 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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:
|
2017-08-24 18:40:24 -04:00
|
|
|
return nil, errors.New("invalid public key type provided to NewECDSAPrivateKey")
|
2017-04-17 18:08:24 -04:00
|
|
|
}
|
|
|
|
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:
|
2017-08-24 18:40:24 -04:00
|
|
|
return nil, errors.New("invalid public key type provided to NewRSAPrivateKey")
|
2017-04-17 18:08:24 -04:00
|
|
|
}
|
|
|
|
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 {
|
2017-08-24 18:40:24 -04:00
|
|
|
return nil, errors.New("signer was based on the wrong key type")
|
2017-04-17 18:08:24 -04:00
|
|
|
}
|
|
|
|
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 := [ed25519.PrivateKeySize]byte{}
|
|
|
|
copy(priv[:], k.private[ed25519.PublicKeySize:])
|
|
|
|
return ed25519.Sign(&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) {
|
2017-08-24 18:40:24 -04:00
|
|
|
return nil, errors.New("unknown key type, cannot sign")
|
2017-04-17 18:08:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|