1
2
3
4
5 package agent
6
7 import (
8 "bytes"
9 "crypto/rand"
10 "crypto/subtle"
11 "errors"
12 "fmt"
13 "sync"
14 "time"
15
16 "golang.org/x/crypto/ssh"
17 )
18
19 type privKey struct {
20 signer ssh.Signer
21 comment string
22 expire *time.Time
23 }
24
25 type keyring struct {
26 mu sync.Mutex
27 keys []privKey
28
29 locked bool
30 passphrase []byte
31 }
32
33 var errLocked = errors.New("agent: locked")
34
35
36
37 func NewKeyring() Agent {
38 return &keyring{}
39 }
40
41
42 func (r *keyring) RemoveAll() error {
43 r.mu.Lock()
44 defer r.mu.Unlock()
45 if r.locked {
46 return errLocked
47 }
48
49 r.keys = nil
50 return nil
51 }
52
53
54
55 func (r *keyring) removeLocked(want []byte) error {
56 found := false
57 for i := 0; i < len(r.keys); {
58 if bytes.Equal(r.keys[i].signer.PublicKey().Marshal(), want) {
59 found = true
60 r.keys[i] = r.keys[len(r.keys)-1]
61 r.keys = r.keys[:len(r.keys)-1]
62 continue
63 } else {
64 i++
65 }
66 }
67
68 if !found {
69 return errors.New("agent: key not found")
70 }
71 return nil
72 }
73
74
75 func (r *keyring) Remove(key ssh.PublicKey) error {
76 r.mu.Lock()
77 defer r.mu.Unlock()
78 if r.locked {
79 return errLocked
80 }
81
82 return r.removeLocked(key.Marshal())
83 }
84
85
86 func (r *keyring) Lock(passphrase []byte) error {
87 r.mu.Lock()
88 defer r.mu.Unlock()
89 if r.locked {
90 return errLocked
91 }
92
93 r.locked = true
94 r.passphrase = passphrase
95 return nil
96 }
97
98
99 func (r *keyring) Unlock(passphrase []byte) error {
100 r.mu.Lock()
101 defer r.mu.Unlock()
102 if !r.locked {
103 return errors.New("agent: not locked")
104 }
105 if 1 != subtle.ConstantTimeCompare(passphrase, r.passphrase) {
106 return fmt.Errorf("agent: incorrect passphrase")
107 }
108
109 r.locked = false
110 r.passphrase = nil
111 return nil
112 }
113
114
115
116
117 func (r *keyring) expireKeysLocked() {
118 for _, k := range r.keys {
119 if k.expire != nil && time.Now().After(*k.expire) {
120 r.removeLocked(k.signer.PublicKey().Marshal())
121 }
122 }
123 }
124
125
126 func (r *keyring) List() ([]*Key, error) {
127 r.mu.Lock()
128 defer r.mu.Unlock()
129 if r.locked {
130
131 return nil, nil
132 }
133
134 r.expireKeysLocked()
135 var ids []*Key
136 for _, k := range r.keys {
137 pub := k.signer.PublicKey()
138 ids = append(ids, &Key{
139 Format: pub.Type(),
140 Blob: pub.Marshal(),
141 Comment: k.comment})
142 }
143 return ids, nil
144 }
145
146
147
148
149 func (r *keyring) Add(key AddedKey) error {
150 r.mu.Lock()
151 defer r.mu.Unlock()
152 if r.locked {
153 return errLocked
154 }
155 signer, err := ssh.NewSignerFromKey(key.PrivateKey)
156
157 if err != nil {
158 return err
159 }
160
161 if cert := key.Certificate; cert != nil {
162 signer, err = ssh.NewCertSigner(cert, signer)
163 if err != nil {
164 return err
165 }
166 }
167
168 p := privKey{
169 signer: signer,
170 comment: key.Comment,
171 }
172
173 if key.LifetimeSecs > 0 {
174 t := time.Now().Add(time.Duration(key.LifetimeSecs) * time.Second)
175 p.expire = &t
176 }
177
178 r.keys = append(r.keys, p)
179
180 return nil
181 }
182
183
184 func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
185 return r.SignWithFlags(key, data, 0)
186 }
187
188 func (r *keyring) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) {
189 r.mu.Lock()
190 defer r.mu.Unlock()
191 if r.locked {
192 return nil, errLocked
193 }
194
195 r.expireKeysLocked()
196 wanted := key.Marshal()
197 for _, k := range r.keys {
198 if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) {
199 if flags == 0 {
200 return k.signer.Sign(rand.Reader, data)
201 } else {
202 if algorithmSigner, ok := k.signer.(ssh.AlgorithmSigner); !ok {
203 return nil, fmt.Errorf("agent: signature does not support non-default signature algorithm: %T", k.signer)
204 } else {
205 var algorithm string
206 switch flags {
207 case SignatureFlagRsaSha256:
208 algorithm = ssh.KeyAlgoRSASHA256
209 case SignatureFlagRsaSha512:
210 algorithm = ssh.KeyAlgoRSASHA512
211 default:
212 return nil, fmt.Errorf("agent: unsupported signature flags: %d", flags)
213 }
214 return algorithmSigner.SignWithAlgorithm(rand.Reader, data, algorithm)
215 }
216 }
217 }
218 }
219 return nil, errors.New("not found")
220 }
221
222
223 func (r *keyring) Signers() ([]ssh.Signer, error) {
224 r.mu.Lock()
225 defer r.mu.Unlock()
226 if r.locked {
227 return nil, errLocked
228 }
229
230 r.expireKeysLocked()
231 s := make([]ssh.Signer, 0, len(r.keys))
232 for _, k := range r.keys {
233 s = append(s, k.signer)
234 }
235 return s, nil
236 }
237
238
239 func (r *keyring) Extension(extensionType string, contents []byte) ([]byte, error) {
240 return nil, ErrExtensionUnsupported
241 }
242
View as plain text