main
Raw Download raw file
  1// Copyright 2014 The Go Authors. All rights reserved.
  2// Use of this source code is governed by a BSD-style
  3// license that can be found in the LICENSE file.
  4
  5package agent
  6
  7import (
  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
 19type privKey struct {
 20	signer  ssh.Signer
 21	comment string
 22	expire  *time.Time
 23}
 24
 25type keyring struct {
 26	mu   sync.Mutex
 27	keys []privKey
 28
 29	locked     bool
 30	passphrase []byte
 31}
 32
 33var errLocked = errors.New("agent: locked")
 34
 35// NewKeyring returns an Agent that holds keys in memory.  It is safe
 36// for concurrent use by multiple goroutines.
 37func NewKeyring() Agent {
 38	return &keyring{}
 39}
 40
 41// RemoveAll removes all identities.
 42func (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// removeLocked does the actual key removal. The caller must already be holding the
 54// keyring mutex.
 55func (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// Remove removes all identities with the given public key.
 75func (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// Lock locks the agent. Sign and Remove will fail, and List will return an empty list.
 86func (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// Unlock undoes the effect of Lock
 99func (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// expireKeysLocked removes expired keys from the keyring. If a key was added
115// with a lifetimesecs contraint and seconds >= lifetimesecs seconds have
116// elapsed, it is removed. The caller *must* be holding the keyring mutex.
117func (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// List returns the identities known to the agent.
126func (r *keyring) List() ([]*Key, error) {
127	r.mu.Lock()
128	defer r.mu.Unlock()
129	if r.locked {
130		// section 2.7: locked agents return empty.
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// Insert adds a private key to the keyring. If a certificate
147// is given, that certificate is added as public key. Note that
148// any constraints given are ignored.
149func (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	// If we already have a Signer with the same public key, replace it with the
179	// new one.
180	for idx, k := range r.keys {
181		if bytes.Equal(k.signer.PublicKey().Marshal(), p.signer.PublicKey().Marshal()) {
182			r.keys[idx] = p
183			return nil
184		}
185	}
186
187	r.keys = append(r.keys, p)
188
189	return nil
190}
191
192// Sign returns a signature for the data.
193func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
194	return r.SignWithFlags(key, data, 0)
195}
196
197func (r *keyring) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) {
198	r.mu.Lock()
199	defer r.mu.Unlock()
200	if r.locked {
201		return nil, errLocked
202	}
203
204	r.expireKeysLocked()
205	wanted := key.Marshal()
206	for _, k := range r.keys {
207		if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) {
208			if flags == 0 {
209				return k.signer.Sign(rand.Reader, data)
210			} else {
211				if algorithmSigner, ok := k.signer.(ssh.AlgorithmSigner); !ok {
212					return nil, fmt.Errorf("agent: signature does not support non-default signature algorithm: %T", k.signer)
213				} else {
214					var algorithm string
215					switch flags {
216					case SignatureFlagRsaSha256:
217						algorithm = ssh.KeyAlgoRSASHA256
218					case SignatureFlagRsaSha512:
219						algorithm = ssh.KeyAlgoRSASHA512
220					default:
221						return nil, fmt.Errorf("agent: unsupported signature flags: %d", flags)
222					}
223					return algorithmSigner.SignWithAlgorithm(rand.Reader, data, algorithm)
224				}
225			}
226		}
227	}
228	return nil, errors.New("not found")
229}
230
231// Signers returns signers for all the known keys.
232func (r *keyring) Signers() ([]ssh.Signer, error) {
233	r.mu.Lock()
234	defer r.mu.Unlock()
235	if r.locked {
236		return nil, errLocked
237	}
238
239	r.expireKeysLocked()
240	s := make([]ssh.Signer, 0, len(r.keys))
241	for _, k := range r.keys {
242		s = append(s, k.signer)
243	}
244	return s, nil
245}
246
247// The keyring does not support any extensions
248func (r *keyring) Extension(extensionType string, contents []byte) ([]byte, error) {
249	return nil, ErrExtensionUnsupported
250}