docs/signer/client/signer_trust.go

223 lines
6.3 KiB
Go

// A CryptoService client wrapper around a remote wrapper service.
package client
import (
"crypto"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io"
"net"
"time"
"github.com/Sirupsen/logrus"
pb "github.com/docker/notary/proto"
"github.com/docker/notary/tuf/data"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
)
// The only thing needed from grpc.ClientConn is it's state.
type checkableConnectionState interface {
State() grpc.ConnectivityState
}
// RemotePrivateKey is a key that is on a remote service, so no private
// key bytes are available
type RemotePrivateKey struct {
data.PublicKey
sClient pb.SignerClient
}
// RemoteSigner wraps a RemotePrivateKey and implements the crypto.Signer
// interface
type RemoteSigner struct {
RemotePrivateKey
}
// Public method of a crypto.Signer needs to return a crypto public key.
func (rs *RemoteSigner) Public() crypto.PublicKey {
publicKey, err := x509.ParsePKIXPublicKey(rs.RemotePrivateKey.Public())
if err != nil {
return nil
}
return publicKey
}
// NewRemotePrivateKey returns RemotePrivateKey, a data.PrivateKey that is only
// good for signing. (You can't get the private bytes out for instance.)
func NewRemotePrivateKey(pubKey data.PublicKey, sClient pb.SignerClient) *RemotePrivateKey {
return &RemotePrivateKey{
PublicKey: pubKey,
sClient: sClient,
}
}
// Private returns nil bytes
func (pk *RemotePrivateKey) Private() []byte {
return nil
}
// Sign calls a remote service to sign a message.
func (pk *RemotePrivateKey) Sign(rand io.Reader, msg []byte,
opts crypto.SignerOpts) ([]byte, error) {
keyID := pb.KeyID{ID: pk.ID()}
sr := &pb.SignatureRequest{
Content: msg,
KeyID: &keyID,
}
sig, err := pk.sClient.Sign(context.Background(), sr)
if err != nil {
return nil, err
}
return sig.Content, nil
}
// SignatureAlgorithm returns the signing algorithm based on the type of
// PublicKey algorithm.
func (pk *RemotePrivateKey) SignatureAlgorithm() data.SigAlgorithm {
switch pk.PublicKey.Algorithm() {
case data.ECDSAKey, data.ECDSAx509Key:
return data.ECDSASignature
case data.RSAKey, data.RSAx509Key:
return data.RSAPSSSignature
case data.ED25519Key:
return data.EDDSASignature
default: // unknown
return ""
}
}
// CryptoSigner returns a crypto.Signer tha wraps the RemotePrivateKey. Needed
// for implementing the interface.
func (pk *RemotePrivateKey) CryptoSigner() crypto.Signer {
return &RemoteSigner{RemotePrivateKey: *pk}
}
// NotarySigner implements a RPC based Trust service that calls the Notary-signer Service
type NotarySigner struct {
kmClient pb.KeyManagementClient
sClient pb.SignerClient
clientConn checkableConnectionState
}
// NewNotarySigner is a convinience method that returns NotarySigner
func NewNotarySigner(hostname string, port string, tlsConfig *tls.Config) *NotarySigner {
var opts []grpc.DialOption
netAddr := net.JoinHostPort(hostname, port)
creds := credentials.NewTLS(tlsConfig)
opts = append(opts, grpc.WithTransportCredentials(creds))
conn, err := grpc.Dial(netAddr, opts...)
if err != nil {
logrus.Fatal("fail to dial: ", err)
}
kmClient := pb.NewKeyManagementClient(conn)
sClient := pb.NewSignerClient(conn)
return &NotarySigner{
kmClient: kmClient,
sClient: sClient,
clientConn: conn,
}
}
// Sign signs a byte string with a number of KeyIDs
func (trust *NotarySigner) Sign(keyIDs []string, toSign []byte) ([]data.Signature, error) {
signatures := make([]data.Signature, 0, len(keyIDs))
for _, ID := range keyIDs {
keyID := pb.KeyID{ID: ID}
sr := &pb.SignatureRequest{
Content: toSign,
KeyID: &keyID,
}
sig, err := trust.sClient.Sign(context.Background(), sr)
if err != nil {
return nil, err
}
signatures = append(signatures, data.Signature{
KeyID: sig.KeyInfo.KeyID.ID,
Method: data.SigAlgorithm(sig.Algorithm.Algorithm),
Signature: sig.Content,
})
}
return signatures, nil
}
// Create creates a remote key and returns the PublicKey associated with the remote private key
func (trust *NotarySigner) Create(role, algorithm string) (data.PublicKey, error) {
publicKey, err := trust.kmClient.CreateKey(context.Background(), &pb.Algorithm{Algorithm: algorithm})
if err != nil {
return nil, err
}
public := data.NewPublicKey(publicKey.KeyInfo.Algorithm.Algorithm, publicKey.PublicKey)
return public, nil
}
// RemoveKey deletes a key
func (trust *NotarySigner) RemoveKey(keyid string) error {
_, err := trust.kmClient.DeleteKey(context.Background(), &pb.KeyID{ID: keyid})
return err
}
// GetKey retrieves a key
func (trust *NotarySigner) GetKey(keyid string) data.PublicKey {
publicKey, err := trust.kmClient.GetKeyInfo(context.Background(), &pb.KeyID{ID: keyid})
if err != nil {
return nil
}
return data.NewPublicKey(publicKey.KeyInfo.Algorithm.Algorithm, publicKey.PublicKey)
}
// GetPrivateKey errors in all cases
func (trust *NotarySigner) GetPrivateKey(keyid string) (data.PrivateKey, string, error) {
pubKey := trust.GetKey(keyid)
if pubKey == nil {
return nil, "", nil
}
return NewRemotePrivateKey(pubKey, trust.sClient), "", nil
}
// ListKeys not supported for NotarySigner
func (trust *NotarySigner) ListKeys(role string) []string {
return []string{}
}
// ListAllKeys not supported for NotarySigner
func (trust *NotarySigner) ListAllKeys() map[string]string {
return map[string]string{}
}
// CheckHealth checks the health of one of the clients, since both clients run
// from the same GRPC server.
func (trust *NotarySigner) CheckHealth(timeout time.Duration) error {
// Do not bother starting checking at all if the connection is broken.
if trust.clientConn.State() != grpc.Idle &&
trust.clientConn.State() != grpc.Ready {
return fmt.Errorf("Not currently connected to trust server.")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
status, err := trust.kmClient.CheckHealth(ctx, &pb.Void{})
defer cancel()
if err == nil && len(status.Status) > 0 {
return fmt.Errorf("Trust is not healthy")
} else if err != nil && grpc.Code(err) == codes.DeadlineExceeded {
return fmt.Errorf(
"Timed out reaching trust service after %s.", timeout)
}
return err
}
// ImportRootKey satisfies the CryptoService interface. It should not be implemented
// for a NotarySigner.
func (trust *NotarySigner) ImportRootKey(r io.Reader) error {
return errors.New("Importing a root key to NotarySigner is not supported")
}