forked from notaryproject/notary
/
signer_trust.go
205 lines (177 loc) · 5.82 KB
/
signer_trust.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// 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 convenience 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,
}
}
// 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 {
var stats string
for k, v := range status.Status {
stats += k + ":" + v + "; "
}
return fmt.Errorf("Trust is not healthy: %s", stats)
}
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")
}