forked from notaryproject/notary
/
signer_trust.go
232 lines (202 loc) · 7.11 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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
// A CryptoService client wrapper around a remote wrapper service.
package client
import (
"crypto"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io"
"net"
"time"
"github.com/docker/notary"
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/credentials"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
)
// 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
healthClient healthpb.HealthClient
}
func healthCheck(d time.Duration, hc healthpb.HealthClient, serviceName string) (*healthpb.HealthCheckResponse, error) {
ctx, _ := context.WithTimeout(context.Background(), d)
req := &healthpb.HealthCheckRequest{
Service: serviceName,
}
return hc.Check(ctx, req)
}
func healthCheckKeyManagement(d time.Duration, hc healthpb.HealthClient) error {
out, err := healthCheck(d, hc, notary.HealthCheckKeyManagement)
if err != nil {
return err
}
if out.Status != healthpb.HealthCheckResponse_SERVING {
return fmt.Errorf("Got the serving status of %s: %s, want %s", "KeyManagement", out.Status, healthpb.HealthCheckResponse_SERVING)
}
return nil
}
func healthCheckSigner(d time.Duration, hc healthpb.HealthClient) error {
out, err := healthCheck(d, hc, notary.HealthCheckSigner)
if err != nil {
return err
}
if out.Status != healthpb.HealthCheckResponse_SERVING {
return fmt.Errorf("Got the serving status of %s: %s, want %s", "Signer", out.Status, healthpb.HealthCheckResponse_SERVING)
}
return nil
}
// CheckHealth are used to probe whether the server is able to handle rpcs.
func (trust *NotarySigner) CheckHealth(d time.Duration, serviceName string) error {
switch serviceName {
case notary.HealthCheckKeyManagement:
return healthCheckKeyManagement(d, trust.healthClient)
case notary.HealthCheckSigner:
return healthCheckSigner(d, trust.healthClient)
case notary.HealthCheckOverall:
if err := healthCheckKeyManagement(d, trust.healthClient); err != nil {
return err
}
return healthCheckSigner(d, trust.healthClient)
default:
return fmt.Errorf("Unknown grpc service %s", serviceName)
}
}
// NewGRPCConnection is a convenience method that returns GRPC Client Connection given a hostname, endpoint, and TLS options
func NewGRPCConnection(hostname string, port string, tlsConfig *tls.Config) (*grpc.ClientConn, error) {
var opts []grpc.DialOption
netAddr := net.JoinHostPort(hostname, port)
creds := credentials.NewTLS(tlsConfig)
opts = append(opts, grpc.WithTransportCredentials(creds))
return grpc.Dial(netAddr, opts...)
}
// NewNotarySigner is a convenience method that returns NotarySigner given a GRPC connection
func NewNotarySigner(conn *grpc.ClientConn) *NotarySigner {
kmClient := pb.NewKeyManagementClient(conn)
sClient := pb.NewSignerClient(conn)
hc := healthpb.NewHealthClient(conn)
return &NotarySigner{
kmClient: kmClient,
sClient: sClient,
healthClient: hc,
}
}
// Create creates a remote key and returns the PublicKey associated with the remote private key
func (trust *NotarySigner) Create(role data.RoleName, gun data.GUN, algorithm string) (data.PublicKey, error) {
publicKey, err := trust.kmClient.CreateKey(context.Background(),
&pb.CreateKeyRequest{Algorithm: algorithm, Role: role.String(), Gun: gun.String()})
if err != nil {
return nil, err
}
public := data.NewPublicKey(publicKey.KeyInfo.Algorithm.Algorithm, publicKey.PublicKey)
return public, nil
}
// AddKey adds a key
func (trust *NotarySigner) AddKey(role data.RoleName, gun data.GUN, k data.PrivateKey) error {
return errors.New("Adding a key to NotarySigner is not supported")
}
// RemoveKey deletes a key by ID - if the key didn't exist, succeed anyway
func (trust *NotarySigner) RemoveKey(keyid string) error {
_, err := trust.kmClient.DeleteKey(context.Background(), &pb.KeyID{ID: keyid})
return err
}
// GetKey retrieves a key by ID - returns nil if the key doesn't exist
func (trust *NotarySigner) GetKey(keyid string) data.PublicKey {
pubKey, _, err := trust.getKeyInfo(keyid)
if err != nil {
return nil
}
return pubKey
}
func (trust *NotarySigner) getKeyInfo(keyid string) (data.PublicKey, data.RoleName, error) {
keyInfo, err := trust.kmClient.GetKeyInfo(context.Background(), &pb.KeyID{ID: keyid})
if err != nil {
return nil, "", err
}
return data.NewPublicKey(keyInfo.KeyInfo.Algorithm.Algorithm, keyInfo.PublicKey), data.RoleName(keyInfo.Role), nil
}
// GetPrivateKey retrieves by ID an object that can be used to sign, but that does
// not contain any private bytes. If the key doesn't exist, returns an error.
func (trust *NotarySigner) GetPrivateKey(keyid string) (data.PrivateKey, data.RoleName, error) {
pubKey, role, err := trust.getKeyInfo(keyid)
if err != nil {
return nil, "", err
}
return NewRemotePrivateKey(pubKey, trust.sClient), role, nil
}
// ListKeys not supported for NotarySigner
func (trust *NotarySigner) ListKeys(role data.RoleName) []string {
return []string{}
}
// ListAllKeys not supported for NotarySigner
func (trust *NotarySigner) ListAllKeys() map[string]data.RoleName {
return map[string]data.RoleName{}
}