forked from notaryproject/notary
-
Notifications
You must be signed in to change notification settings - Fork 0
/
crypto_service.go
141 lines (126 loc) · 3.94 KB
/
crypto_service.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
package cryptoservice
import (
"crypto/rand"
"fmt"
"path/filepath"
"github.com/Sirupsen/logrus"
"github.com/docker/notary/trustmanager"
"github.com/docker/notary/tuf/data"
)
const (
rsaKeySize = 2048 // Used for snapshots and targets keys
)
// CryptoService implements Sign and Create, holding a specific GUN and keystore to
// operate on
type CryptoService struct {
gun string
keyStores []trustmanager.KeyStore
}
// NewCryptoService returns an instance of CryptoService
func NewCryptoService(gun string, keyStores ...trustmanager.KeyStore) *CryptoService {
return &CryptoService{gun: gun, keyStores: keyStores}
}
// Create is used to generate keys for targets, snapshots and timestamps
func (cs *CryptoService) Create(role, algorithm string) (data.PublicKey, error) {
var privKey data.PrivateKey
var err error
switch algorithm {
case data.RSAKey:
privKey, err = trustmanager.GenerateRSAKey(rand.Reader, rsaKeySize)
if err != nil {
return nil, fmt.Errorf("failed to generate RSA key: %v", err)
}
case data.ECDSAKey:
privKey, err = trustmanager.GenerateECDSAKey(rand.Reader)
if err != nil {
return nil, fmt.Errorf("failed to generate EC key: %v", err)
}
case data.ED25519Key:
privKey, err = trustmanager.GenerateED25519Key(rand.Reader)
if err != nil {
return nil, fmt.Errorf("failed to generate ED25519 key: %v", err)
}
default:
return nil, fmt.Errorf("private key type not supported for key generation: %s", algorithm)
}
logrus.Debugf("generated new %s key for role: %s and keyID: %s", algorithm, role, privKey.ID())
// Store the private key into our keystore with the name being: /GUN/ID.key with an alias of role
var keyPath string
if role == data.CanonicalRootRole {
keyPath = privKey.ID()
} else {
keyPath = filepath.Join(cs.gun, privKey.ID())
}
for _, ks := range cs.keyStores {
err = ks.AddKey(keyPath, role, privKey)
if err == nil {
return data.PublicKeyFromPrivate(privKey), nil
}
}
if err != nil {
return nil, fmt.Errorf("failed to add key to filestore: %v", err)
}
return nil, fmt.Errorf("keystores would not accept new private keys for unknown reasons")
}
// GetPrivateKey returns a private key and role if present by ID.
// It tries to get the key first without a GUN (in which case it's a root key).
// If that fails, try to get the key with the GUN (non-root key).
// If that fails, then we don't have the key.
func (cs *CryptoService) GetPrivateKey(keyID string) (k data.PrivateKey, role string, err error) {
keyPaths := []string{keyID, filepath.Join(cs.gun, keyID)}
for _, ks := range cs.keyStores {
for _, keyPath := range keyPaths {
k, role, err = ks.GetKey(keyPath)
if err == nil {
return
}
switch err.(type) {
case trustmanager.ErrPasswordInvalid, trustmanager.ErrAttemptsExceeded:
return
default:
continue
}
}
}
return // returns whatever the final values were
}
// GetKey returns a key by ID
func (cs *CryptoService) GetKey(keyID string) data.PublicKey {
privKey, _, err := cs.GetPrivateKey(keyID)
if err != nil {
return nil
}
return data.PublicKeyFromPrivate(privKey)
}
// RemoveKey deletes a key by ID
func (cs *CryptoService) RemoveKey(keyID string) (err error) {
keyPaths := []string{keyID, filepath.Join(cs.gun, keyID)}
for _, ks := range cs.keyStores {
for _, keyPath := range keyPaths {
ks.RemoveKey(keyPath)
}
}
return // returns whatever the final values were
}
// ListKeys returns a list of key IDs valid for the given role
func (cs *CryptoService) ListKeys(role string) []string {
var res []string
for _, ks := range cs.keyStores {
for k, r := range ks.ListKeys() {
if r == role {
res = append(res, k)
}
}
}
return res
}
// ListAllKeys returns a map of key IDs to role
func (cs *CryptoService) ListAllKeys() map[string]string {
res := make(map[string]string)
for _, ks := range cs.keyStores {
for k, r := range ks.ListKeys() {
res[k] = r // keys are content addressed so don't care about overwrites
}
}
return res
}