1// Copyright 2012 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
5// Package agent implements the ssh-agent protocol, and provides both
6// a client and a server. The client can talk to a standard ssh-agent
7// that uses UNIX sockets, and one could implement an alternative
8// ssh-agent process using the sample server.
9//
10// References:
11//  [PROTOCOL.agent]:    http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD
12package agent // import "golang.org/x/crypto/ssh/agent"
13
14import (
15	"bytes"
16	"crypto/dsa"
17	"crypto/ecdsa"
18	"crypto/elliptic"
19	"crypto/rsa"
20	"encoding/base64"
21	"encoding/binary"
22	"errors"
23	"fmt"
24	"io"
25	"math/big"
26	"sync"
27
28	"golang.org/x/crypto/ed25519"
29	"golang.org/x/crypto/ssh"
30)
31
32// Agent represents the capabilities of an ssh-agent.
33type Agent interface {
34	// List returns the identities known to the agent.
35	List() ([]*Key, error)
36
37	// Sign has the agent sign the data using a protocol 2 key as defined
38	// in [PROTOCOL.agent] section 2.6.2.
39	Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error)
40
41	// Add adds a private key to the agent.
42	Add(key AddedKey) error
43
44	// Remove removes all identities with the given public key.
45	Remove(key ssh.PublicKey) error
46
47	// RemoveAll removes all identities.
48	RemoveAll() error
49
50	// Lock locks the agent. Sign and Remove will fail, and List will empty an empty list.
51	Lock(passphrase []byte) error
52
53	// Unlock undoes the effect of Lock
54	Unlock(passphrase []byte) error
55
56	// Signers returns signers for all the known keys.
57	Signers() ([]ssh.Signer, error)
58}
59
60// AddedKey describes an SSH key to be added to an Agent.
61type AddedKey struct {
62	// PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey or
63	// *ecdsa.PrivateKey, which will be inserted into the agent.
64	PrivateKey interface{}
65	// Certificate, if not nil, is communicated to the agent and will be
66	// stored with the key.
67	Certificate *ssh.Certificate
68	// Comment is an optional, free-form string.
69	Comment string
70	// LifetimeSecs, if not zero, is the number of seconds that the
71	// agent will store the key for.
72	LifetimeSecs uint32
73	// ConfirmBeforeUse, if true, requests that the agent confirm with the
74	// user before each use of this key.
75	ConfirmBeforeUse bool
76}
77
78// See [PROTOCOL.agent], section 3.
79const (
80	agentRequestV1Identities   = 1
81	agentRemoveAllV1Identities = 9
82
83	// 3.2 Requests from client to agent for protocol 2 key operations
84	agentAddIdentity         = 17
85	agentRemoveIdentity      = 18
86	agentRemoveAllIdentities = 19
87	agentAddIdConstrained    = 25
88
89	// 3.3 Key-type independent requests from client to agent
90	agentAddSmartcardKey            = 20
91	agentRemoveSmartcardKey         = 21
92	agentLock                       = 22
93	agentUnlock                     = 23
94	agentAddSmartcardKeyConstrained = 26
95
96	// 3.7 Key constraint identifiers
97	agentConstrainLifetime = 1
98	agentConstrainConfirm  = 2
99)
100
101// maxAgentResponseBytes is the maximum agent reply size that is accepted. This
102// is a sanity check, not a limit in the spec.
103const maxAgentResponseBytes = 16 << 20
104
105// Agent messages:
106// These structures mirror the wire format of the corresponding ssh agent
107// messages found in [PROTOCOL.agent].
108
109// 3.4 Generic replies from agent to client
110const agentFailure = 5
111
112type failureAgentMsg struct{}
113
114const agentSuccess = 6
115
116type successAgentMsg struct{}
117
118// See [PROTOCOL.agent], section 2.5.2.
119const agentRequestIdentities = 11
120
121type requestIdentitiesAgentMsg struct{}
122
123// See [PROTOCOL.agent], section 2.5.2.
124const agentIdentitiesAnswer = 12
125
126type identitiesAnswerAgentMsg struct {
127	NumKeys uint32 `sshtype:"12"`
128	Keys    []byte `ssh:"rest"`
129}
130
131// See [PROTOCOL.agent], section 2.6.2.
132const agentSignRequest = 13
133
134type signRequestAgentMsg struct {
135	KeyBlob []byte `sshtype:"13"`
136	Data    []byte
137	Flags   uint32
138}
139
140// See [PROTOCOL.agent], section 2.6.2.
141
142// 3.6 Replies from agent to client for protocol 2 key operations
143const agentSignResponse = 14
144
145type signResponseAgentMsg struct {
146	SigBlob []byte `sshtype:"14"`
147}
148
149type publicKey struct {
150	Format string
151	Rest   []byte `ssh:"rest"`
152}
153
154// Key represents a protocol 2 public key as defined in
155// [PROTOCOL.agent], section 2.5.2.
156type Key struct {
157	Format  string
158	Blob    []byte
159	Comment string
160}
161
162func clientErr(err error) error {
163	return fmt.Errorf("agent: client error: %v", err)
164}
165
166// String returns the storage form of an agent key with the format, base64
167// encoded serialized key, and the comment if it is not empty.
168func (k *Key) String() string {
169	s := string(k.Format) + " " + base64.StdEncoding.EncodeToString(k.Blob)
170
171	if k.Comment != "" {
172		s += " " + k.Comment
173	}
174
175	return s
176}
177
178// Type returns the public key type.
179func (k *Key) Type() string {
180	return k.Format
181}
182
183// Marshal returns key blob to satisfy the ssh.PublicKey interface.
184func (k *Key) Marshal() []byte {
185	return k.Blob
186}
187
188// Verify satisfies the ssh.PublicKey interface.
189func (k *Key) Verify(data []byte, sig *ssh.Signature) error {
190	pubKey, err := ssh.ParsePublicKey(k.Blob)
191	if err != nil {
192		return fmt.Errorf("agent: bad public key: %v", err)
193	}
194	return pubKey.Verify(data, sig)
195}
196
197type wireKey struct {
198	Format string
199	Rest   []byte `ssh:"rest"`
200}
201
202func parseKey(in []byte) (out *Key, rest []byte, err error) {
203	var record struct {
204		Blob    []byte
205		Comment string
206		Rest    []byte `ssh:"rest"`
207	}
208
209	if err := ssh.Unmarshal(in, &record); err != nil {
210		return nil, nil, err
211	}
212
213	var wk wireKey
214	if err := ssh.Unmarshal(record.Blob, &wk); err != nil {
215		return nil, nil, err
216	}
217
218	return &Key{
219		Format:  wk.Format,
220		Blob:    record.Blob,
221		Comment: record.Comment,
222	}, record.Rest, nil
223}
224
225// client is a client for an ssh-agent process.
226type client struct {
227	// conn is typically a *net.UnixConn
228	conn io.ReadWriter
229	// mu is used to prevent concurrent access to the agent
230	mu sync.Mutex
231}
232
233// NewClient returns an Agent that talks to an ssh-agent process over
234// the given connection.
235func NewClient(rw io.ReadWriter) Agent {
236	return &client{conn: rw}
237}
238
239// call sends an RPC to the agent. On success, the reply is
240// unmarshaled into reply and replyType is set to the first byte of
241// the reply, which contains the type of the message.
242func (c *client) call(req []byte) (reply interface{}, err error) {
243	c.mu.Lock()
244	defer c.mu.Unlock()
245
246	msg := make([]byte, 4+len(req))
247	binary.BigEndian.PutUint32(msg, uint32(len(req)))
248	copy(msg[4:], req)
249	if _, err = c.conn.Write(msg); err != nil {
250		return nil, clientErr(err)
251	}
252
253	var respSizeBuf [4]byte
254	if _, err = io.ReadFull(c.conn, respSizeBuf[:]); err != nil {
255		return nil, clientErr(err)
256	}
257	respSize := binary.BigEndian.Uint32(respSizeBuf[:])
258	if respSize > maxAgentResponseBytes {
259		return nil, clientErr(err)
260	}
261
262	buf := make([]byte, respSize)
263	if _, err = io.ReadFull(c.conn, buf); err != nil {
264		return nil, clientErr(err)
265	}
266	reply, err = unmarshal(buf)
267	if err != nil {
268		return nil, clientErr(err)
269	}
270	return reply, err
271}
272
273func (c *client) simpleCall(req []byte) error {
274	resp, err := c.call(req)
275	if err != nil {
276		return err
277	}
278	if _, ok := resp.(*successAgentMsg); ok {
279		return nil
280	}
281	return errors.New("agent: failure")
282}
283
284func (c *client) RemoveAll() error {
285	return c.simpleCall([]byte{agentRemoveAllIdentities})
286}
287
288func (c *client) Remove(key ssh.PublicKey) error {
289	req := ssh.Marshal(&agentRemoveIdentityMsg{
290		KeyBlob: key.Marshal(),
291	})
292	return c.simpleCall(req)
293}
294
295func (c *client) Lock(passphrase []byte) error {
296	req := ssh.Marshal(&agentLockMsg{
297		Passphrase: passphrase,
298	})
299	return c.simpleCall(req)
300}
301
302func (c *client) Unlock(passphrase []byte) error {
303	req := ssh.Marshal(&agentUnlockMsg{
304		Passphrase: passphrase,
305	})
306	return c.simpleCall(req)
307}
308
309// List returns the identities known to the agent.
310func (c *client) List() ([]*Key, error) {
311	// see [PROTOCOL.agent] section 2.5.2.
312	req := []byte{agentRequestIdentities}
313
314	msg, err := c.call(req)
315	if err != nil {
316		return nil, err
317	}
318
319	switch msg := msg.(type) {
320	case *identitiesAnswerAgentMsg:
321		if msg.NumKeys > maxAgentResponseBytes/8 {
322			return nil, errors.New("agent: too many keys in agent reply")
323		}
324		keys := make([]*Key, msg.NumKeys)
325		data := msg.Keys
326		for i := uint32(0); i < msg.NumKeys; i++ {
327			var key *Key
328			var err error
329			if key, data, err = parseKey(data); err != nil {
330				return nil, err
331			}
332			keys[i] = key
333		}
334		return keys, nil
335	case *failureAgentMsg:
336		return nil, errors.New("agent: failed to list keys")
337	}
338	panic("unreachable")
339}
340
341// Sign has the agent sign the data using a protocol 2 key as defined
342// in [PROTOCOL.agent] section 2.6.2.
343func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
344	req := ssh.Marshal(signRequestAgentMsg{
345		KeyBlob: key.Marshal(),
346		Data:    data,
347	})
348
349	msg, err := c.call(req)
350	if err != nil {
351		return nil, err
352	}
353
354	switch msg := msg.(type) {
355	case *signResponseAgentMsg:
356		var sig ssh.Signature
357		if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil {
358			return nil, err
359		}
360
361		return &sig, nil
362	case *failureAgentMsg:
363		return nil, errors.New("agent: failed to sign challenge")
364	}
365	panic("unreachable")
366}
367
368// unmarshal parses an agent message in packet, returning the parsed
369// form and the message type of packet.
370func unmarshal(packet []byte) (interface{}, error) {
371	if len(packet) < 1 {
372		return nil, errors.New("agent: empty packet")
373	}
374	var msg interface{}
375	switch packet[0] {
376	case agentFailure:
377		return new(failureAgentMsg), nil
378	case agentSuccess:
379		return new(successAgentMsg), nil
380	case agentIdentitiesAnswer:
381		msg = new(identitiesAnswerAgentMsg)
382	case agentSignResponse:
383		msg = new(signResponseAgentMsg)
384	case agentV1IdentitiesAnswer:
385		msg = new(agentV1IdentityMsg)
386	default:
387		return nil, fmt.Errorf("agent: unknown type tag %d", packet[0])
388	}
389	if err := ssh.Unmarshal(packet, msg); err != nil {
390		return nil, err
391	}
392	return msg, nil
393}
394
395type rsaKeyMsg struct {
396	Type        string `sshtype:"17|25"`
397	N           *big.Int
398	E           *big.Int
399	D           *big.Int
400	Iqmp        *big.Int // IQMP = Inverse Q Mod P
401	P           *big.Int
402	Q           *big.Int
403	Comments    string
404	Constraints []byte `ssh:"rest"`
405}
406
407type dsaKeyMsg struct {
408	Type        string `sshtype:"17|25"`
409	P           *big.Int
410	Q           *big.Int
411	G           *big.Int
412	Y           *big.Int
413	X           *big.Int
414	Comments    string
415	Constraints []byte `ssh:"rest"`
416}
417
418type ecdsaKeyMsg struct {
419	Type        string `sshtype:"17|25"`
420	Curve       string
421	KeyBytes    []byte
422	D           *big.Int
423	Comments    string
424	Constraints []byte `ssh:"rest"`
425}
426
427type ed25519KeyMsg struct {
428	Type        string `sshtype:"17|25"`
429	Pub         []byte
430	Priv        []byte
431	Comments    string
432	Constraints []byte `ssh:"rest"`
433}
434
435// Insert adds a private key to the agent.
436func (c *client) insertKey(s interface{}, comment string, constraints []byte) error {
437	var req []byte
438	switch k := s.(type) {
439	case *rsa.PrivateKey:
440		if len(k.Primes) != 2 {
441			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
442		}
443		k.Precompute()
444		req = ssh.Marshal(rsaKeyMsg{
445			Type:        ssh.KeyAlgoRSA,
446			N:           k.N,
447			E:           big.NewInt(int64(k.E)),
448			D:           k.D,
449			Iqmp:        k.Precomputed.Qinv,
450			P:           k.Primes[0],
451			Q:           k.Primes[1],
452			Comments:    comment,
453			Constraints: constraints,
454		})
455	case *dsa.PrivateKey:
456		req = ssh.Marshal(dsaKeyMsg{
457			Type:        ssh.KeyAlgoDSA,
458			P:           k.P,
459			Q:           k.Q,
460			G:           k.G,
461			Y:           k.Y,
462			X:           k.X,
463			Comments:    comment,
464			Constraints: constraints,
465		})
466	case *ecdsa.PrivateKey:
467		nistID := fmt.Sprintf("nistp%d", k.Params().BitSize)
468		req = ssh.Marshal(ecdsaKeyMsg{
469			Type:        "ecdsa-sha2-" + nistID,
470			Curve:       nistID,
471			KeyBytes:    elliptic.Marshal(k.Curve, k.X, k.Y),
472			D:           k.D,
473			Comments:    comment,
474			Constraints: constraints,
475		})
476	case *ed25519.PrivateKey:
477		req = ssh.Marshal(ed25519KeyMsg{
478			Type:        ssh.KeyAlgoED25519,
479			Pub:         []byte(*k)[32:],
480			Priv:        []byte(*k),
481			Comments:    comment,
482			Constraints: constraints,
483		})
484	default:
485		return fmt.Errorf("agent: unsupported key type %T", s)
486	}
487
488	// if constraints are present then the message type needs to be changed.
489	if len(constraints) != 0 {
490		req[0] = agentAddIdConstrained
491	}
492
493	resp, err := c.call(req)
494	if err != nil {
495		return err
496	}
497	if _, ok := resp.(*successAgentMsg); ok {
498		return nil
499	}
500	return errors.New("agent: failure")
501}
502
503type rsaCertMsg struct {
504	Type        string `sshtype:"17|25"`
505	CertBytes   []byte
506	D           *big.Int
507	Iqmp        *big.Int // IQMP = Inverse Q Mod P
508	P           *big.Int
509	Q           *big.Int
510	Comments    string
511	Constraints []byte `ssh:"rest"`
512}
513
514type dsaCertMsg struct {
515	Type        string `sshtype:"17|25"`
516	CertBytes   []byte
517	X           *big.Int
518	Comments    string
519	Constraints []byte `ssh:"rest"`
520}
521
522type ecdsaCertMsg struct {
523	Type        string `sshtype:"17|25"`
524	CertBytes   []byte
525	D           *big.Int
526	Comments    string
527	Constraints []byte `ssh:"rest"`
528}
529
530type ed25519CertMsg struct {
531	Type        string `sshtype:"17|25"`
532	CertBytes   []byte
533	Pub         []byte
534	Priv        []byte
535	Comments    string
536	Constraints []byte `ssh:"rest"`
537}
538
539// Insert adds a private key to the agent. If a certificate is given,
540// that certificate is added instead as public key.
541func (c *client) Add(key AddedKey) error {
542	var constraints []byte
543
544	if secs := key.LifetimeSecs; secs != 0 {
545		constraints = append(constraints, agentConstrainLifetime)
546
547		var secsBytes [4]byte
548		binary.BigEndian.PutUint32(secsBytes[:], secs)
549		constraints = append(constraints, secsBytes[:]...)
550	}
551
552	if key.ConfirmBeforeUse {
553		constraints = append(constraints, agentConstrainConfirm)
554	}
555
556	if cert := key.Certificate; cert == nil {
557		return c.insertKey(key.PrivateKey, key.Comment, constraints)
558	} else {
559		return c.insertCert(key.PrivateKey, cert, key.Comment, constraints)
560	}
561}
562
563func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error {
564	var req []byte
565	switch k := s.(type) {
566	case *rsa.PrivateKey:
567		if len(k.Primes) != 2 {
568			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
569		}
570		k.Precompute()
571		req = ssh.Marshal(rsaCertMsg{
572			Type:        cert.Type(),
573			CertBytes:   cert.Marshal(),
574			D:           k.D,
575			Iqmp:        k.Precomputed.Qinv,
576			P:           k.Primes[0],
577			Q:           k.Primes[1],
578			Comments:    comment,
579			Constraints: constraints,
580		})
581	case *dsa.PrivateKey:
582		req = ssh.Marshal(dsaCertMsg{
583			Type:        cert.Type(),
584			CertBytes:   cert.Marshal(),
585			X:           k.X,
586			Comments:    comment,
587			Constraints: constraints,
588		})
589	case *ecdsa.PrivateKey:
590		req = ssh.Marshal(ecdsaCertMsg{
591			Type:        cert.Type(),
592			CertBytes:   cert.Marshal(),
593			D:           k.D,
594			Comments:    comment,
595			Constraints: constraints,
596		})
597	case ed25519.PrivateKey:
598		req = ssh.Marshal(ed25519CertMsg{
599			Type:        cert.Type(),
600			CertBytes:   cert.Marshal(),
601			Pub:         []byte(k)[32:],
602			Priv:        []byte(k),
603			Comments:    comment,
604			Constraints: constraints,
605		})
606	default:
607		return fmt.Errorf("agent: unsupported key type %T", s)
608	}
609
610	// if constraints are present then the message type needs to be changed.
611	if len(constraints) != 0 {
612		req[0] = agentAddIdConstrained
613	}
614
615	signer, err := ssh.NewSignerFromKey(s)
616	if err != nil {
617		return err
618	}
619	if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 {
620		return errors.New("agent: signer and cert have different public key")
621	}
622
623	resp, err := c.call(req)
624	if err != nil {
625		return err
626	}
627	if _, ok := resp.(*successAgentMsg); ok {
628		return nil
629	}
630	return errors.New("agent: failure")
631}
632
633// Signers provides a callback for client authentication.
634func (c *client) Signers() ([]ssh.Signer, error) {
635	keys, err := c.List()
636	if err != nil {
637		return nil, err
638	}
639
640	var result []ssh.Signer
641	for _, k := range keys {
642		result = append(result, &agentKeyringSigner{c, k})
643	}
644	return result, nil
645}
646
647type agentKeyringSigner struct {
648	agent *client
649	pub   ssh.PublicKey
650}
651
652func (s *agentKeyringSigner) PublicKey() ssh.PublicKey {
653	return s.pub
654}
655
656func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) {
657	// The agent has its own entropy source, so the rand argument is ignored.
658	return s.agent.Sign(s.pub, data)
659}
660