main
Raw Download raw file
  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//
 12//	[PROTOCOL.agent]: https://tools.ietf.org/html/draft-miller-ssh-agent-00
 13package agent
 14
 15import (
 16	"bytes"
 17	"crypto/dsa"
 18	"crypto/ecdsa"
 19	"crypto/ed25519"
 20	"crypto/elliptic"
 21	"crypto/rsa"
 22	"encoding/base64"
 23	"encoding/binary"
 24	"errors"
 25	"fmt"
 26	"io"
 27	"math/big"
 28	"sync"
 29
 30	"golang.org/x/crypto/ssh"
 31)
 32
 33// SignatureFlags represent additional flags that can be passed to the signature
 34// requests an defined in [PROTOCOL.agent] section 4.5.1.
 35type SignatureFlags uint32
 36
 37// SignatureFlag values as defined in [PROTOCOL.agent] section 5.3.
 38const (
 39	SignatureFlagReserved SignatureFlags = 1 << iota
 40	SignatureFlagRsaSha256
 41	SignatureFlagRsaSha512
 42)
 43
 44// Agent represents the capabilities of an ssh-agent.
 45type Agent interface {
 46	// List returns the identities known to the agent.
 47	List() ([]*Key, error)
 48
 49	// Sign has the agent sign the data using a protocol 2 key as defined
 50	// in [PROTOCOL.agent] section 2.6.2.
 51	Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error)
 52
 53	// Add adds a private key to the agent.
 54	Add(key AddedKey) error
 55
 56	// Remove removes all identities with the given public key.
 57	Remove(key ssh.PublicKey) error
 58
 59	// RemoveAll removes all identities.
 60	RemoveAll() error
 61
 62	// Lock locks the agent. Sign and Remove will fail, and List will empty an empty list.
 63	Lock(passphrase []byte) error
 64
 65	// Unlock undoes the effect of Lock
 66	Unlock(passphrase []byte) error
 67
 68	// Signers returns signers for all the known keys.
 69	Signers() ([]ssh.Signer, error)
 70}
 71
 72type ExtendedAgent interface {
 73	Agent
 74
 75	// SignWithFlags signs like Sign, but allows for additional flags to be sent/received
 76	SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error)
 77
 78	// Extension processes a custom extension request. Standard-compliant agents are not
 79	// required to support any extensions, but this method allows agents to implement
 80	// vendor-specific methods or add experimental features. See [PROTOCOL.agent] section 4.7.
 81	// If agent extensions are unsupported entirely this method MUST return an
 82	// ErrExtensionUnsupported error. Similarly, if just the specific extensionType in
 83	// the request is unsupported by the agent then ErrExtensionUnsupported MUST be
 84	// returned.
 85	//
 86	// In the case of success, since [PROTOCOL.agent] section 4.7 specifies that the contents
 87	// of the response are unspecified (including the type of the message), the complete
 88	// response will be returned as a []byte slice, including the "type" byte of the message.
 89	Extension(extensionType string, contents []byte) ([]byte, error)
 90}
 91
 92// ConstraintExtension describes an optional constraint defined by users.
 93type ConstraintExtension struct {
 94	// ExtensionName consist of a UTF-8 string suffixed by the
 95	// implementation domain following the naming scheme defined
 96	// in Section 4.2 of RFC 4251, e.g.  "foo@example.com".
 97	ExtensionName string
 98	// ExtensionDetails contains the actual content of the extended
 99	// constraint.
100	ExtensionDetails []byte
101}
102
103// AddedKey describes an SSH key to be added to an Agent.
104type AddedKey struct {
105	// PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey,
106	// ed25519.PrivateKey or *ecdsa.PrivateKey, which will be inserted into the
107	// agent.
108	PrivateKey interface{}
109	// Certificate, if not nil, is communicated to the agent and will be
110	// stored with the key.
111	Certificate *ssh.Certificate
112	// Comment is an optional, free-form string.
113	Comment string
114	// LifetimeSecs, if not zero, is the number of seconds that the
115	// agent will store the key for.
116	LifetimeSecs uint32
117	// ConfirmBeforeUse, if true, requests that the agent confirm with the
118	// user before each use of this key.
119	ConfirmBeforeUse bool
120	// ConstraintExtensions are the experimental or private-use constraints
121	// defined by users.
122	ConstraintExtensions []ConstraintExtension
123}
124
125// See [PROTOCOL.agent], section 3.
126const (
127	agentRequestV1Identities   = 1
128	agentRemoveAllV1Identities = 9
129
130	// 3.2 Requests from client to agent for protocol 2 key operations
131	agentAddIdentity         = 17
132	agentRemoveIdentity      = 18
133	agentRemoveAllIdentities = 19
134	agentAddIDConstrained    = 25
135
136	// 3.3 Key-type independent requests from client to agent
137	agentAddSmartcardKey            = 20
138	agentRemoveSmartcardKey         = 21
139	agentLock                       = 22
140	agentUnlock                     = 23
141	agentAddSmartcardKeyConstrained = 26
142
143	// 3.7 Key constraint identifiers
144	agentConstrainLifetime = 1
145	agentConstrainConfirm  = 2
146	// Constraint extension identifier up to version 2 of the protocol. A
147	// backward incompatible change will be required if we want to add support
148	// for SSH_AGENT_CONSTRAIN_MAXSIGN which uses the same ID.
149	agentConstrainExtensionV00 = 3
150	// Constraint extension identifier in version 3 and later of the protocol.
151	agentConstrainExtension = 255
152)
153
154// maxAgentResponseBytes is the maximum agent reply size that is accepted. This
155// is a sanity check, not a limit in the spec.
156const maxAgentResponseBytes = 16 << 20
157
158// Agent messages:
159// These structures mirror the wire format of the corresponding ssh agent
160// messages found in [PROTOCOL.agent].
161
162// 3.4 Generic replies from agent to client
163const agentFailure = 5
164
165type failureAgentMsg struct{}
166
167const agentSuccess = 6
168
169type successAgentMsg struct{}
170
171// See [PROTOCOL.agent], section 2.5.2.
172const agentRequestIdentities = 11
173
174type requestIdentitiesAgentMsg struct{}
175
176// See [PROTOCOL.agent], section 2.5.2.
177const agentIdentitiesAnswer = 12
178
179type identitiesAnswerAgentMsg struct {
180	NumKeys uint32 `sshtype:"12"`
181	Keys    []byte `ssh:"rest"`
182}
183
184// See [PROTOCOL.agent], section 2.6.2.
185const agentSignRequest = 13
186
187type signRequestAgentMsg struct {
188	KeyBlob []byte `sshtype:"13"`
189	Data    []byte
190	Flags   uint32
191}
192
193// See [PROTOCOL.agent], section 2.6.2.
194
195// 3.6 Replies from agent to client for protocol 2 key operations
196const agentSignResponse = 14
197
198type signResponseAgentMsg struct {
199	SigBlob []byte `sshtype:"14"`
200}
201
202type publicKey struct {
203	Format string
204	Rest   []byte `ssh:"rest"`
205}
206
207// 3.7 Key constraint identifiers
208type constrainLifetimeAgentMsg struct {
209	LifetimeSecs uint32 `sshtype:"1"`
210}
211
212type constrainExtensionAgentMsg struct {
213	ExtensionName    string `sshtype:"255|3"`
214	ExtensionDetails []byte
215
216	// Rest is a field used for parsing, not part of message
217	Rest []byte `ssh:"rest"`
218}
219
220// See [PROTOCOL.agent], section 4.7
221const agentExtension = 27
222const agentExtensionFailure = 28
223
224// ErrExtensionUnsupported indicates that an extension defined in
225// [PROTOCOL.agent] section 4.7 is unsupported by the agent. Specifically this
226// error indicates that the agent returned a standard SSH_AGENT_FAILURE message
227// as the result of a SSH_AGENTC_EXTENSION request. Note that the protocol
228// specification (and therefore this error) does not distinguish between a
229// specific extension being unsupported and extensions being unsupported entirely.
230var ErrExtensionUnsupported = errors.New("agent: extension unsupported")
231
232type extensionAgentMsg struct {
233	ExtensionType string `sshtype:"27"`
234	// NOTE: this matches OpenSSH's PROTOCOL.agent, not the IETF draft [PROTOCOL.agent],
235	// so that it matches what OpenSSH actually implements in the wild.
236	Contents []byte `ssh:"rest"`
237}
238
239// Key represents a protocol 2 public key as defined in
240// [PROTOCOL.agent], section 2.5.2.
241type Key struct {
242	Format  string
243	Blob    []byte
244	Comment string
245}
246
247func clientErr(err error) error {
248	return fmt.Errorf("agent: client error: %v", err)
249}
250
251// String returns the storage form of an agent key with the format, base64
252// encoded serialized key, and the comment if it is not empty.
253func (k *Key) String() string {
254	s := string(k.Format) + " " + base64.StdEncoding.EncodeToString(k.Blob)
255
256	if k.Comment != "" {
257		s += " " + k.Comment
258	}
259
260	return s
261}
262
263// Type returns the public key type.
264func (k *Key) Type() string {
265	return k.Format
266}
267
268// Marshal returns key blob to satisfy the ssh.PublicKey interface.
269func (k *Key) Marshal() []byte {
270	return k.Blob
271}
272
273// Verify satisfies the ssh.PublicKey interface.
274func (k *Key) Verify(data []byte, sig *ssh.Signature) error {
275	pubKey, err := ssh.ParsePublicKey(k.Blob)
276	if err != nil {
277		return fmt.Errorf("agent: bad public key: %v", err)
278	}
279	return pubKey.Verify(data, sig)
280}
281
282type wireKey struct {
283	Format string
284	Rest   []byte `ssh:"rest"`
285}
286
287func parseKey(in []byte) (out *Key, rest []byte, err error) {
288	var record struct {
289		Blob    []byte
290		Comment string
291		Rest    []byte `ssh:"rest"`
292	}
293
294	if err := ssh.Unmarshal(in, &record); err != nil {
295		return nil, nil, err
296	}
297
298	var wk wireKey
299	if err := ssh.Unmarshal(record.Blob, &wk); err != nil {
300		return nil, nil, err
301	}
302
303	return &Key{
304		Format:  wk.Format,
305		Blob:    record.Blob,
306		Comment: record.Comment,
307	}, record.Rest, nil
308}
309
310// client is a client for an ssh-agent process.
311type client struct {
312	// conn is typically a *net.UnixConn
313	conn io.ReadWriter
314	// mu is used to prevent concurrent access to the agent
315	mu sync.Mutex
316}
317
318// NewClient returns an Agent that talks to an ssh-agent process over
319// the given connection.
320func NewClient(rw io.ReadWriter) ExtendedAgent {
321	return &client{conn: rw}
322}
323
324// call sends an RPC to the agent. On success, the reply is
325// unmarshaled into reply and replyType is set to the first byte of
326// the reply, which contains the type of the message.
327func (c *client) call(req []byte) (reply interface{}, err error) {
328	buf, err := c.callRaw(req)
329	if err != nil {
330		return nil, err
331	}
332	reply, err = unmarshal(buf)
333	if err != nil {
334		return nil, clientErr(err)
335	}
336	return reply, nil
337}
338
339// callRaw sends an RPC to the agent. On success, the raw
340// bytes of the response are returned; no unmarshalling is
341// performed on the response.
342func (c *client) callRaw(req []byte) (reply []byte, err error) {
343	c.mu.Lock()
344	defer c.mu.Unlock()
345
346	msg := make([]byte, 4+len(req))
347	binary.BigEndian.PutUint32(msg, uint32(len(req)))
348	copy(msg[4:], req)
349	if _, err = c.conn.Write(msg); err != nil {
350		return nil, clientErr(err)
351	}
352
353	var respSizeBuf [4]byte
354	if _, err = io.ReadFull(c.conn, respSizeBuf[:]); err != nil {
355		return nil, clientErr(err)
356	}
357	respSize := binary.BigEndian.Uint32(respSizeBuf[:])
358	if respSize > maxAgentResponseBytes {
359		return nil, clientErr(errors.New("response too large"))
360	}
361
362	buf := make([]byte, respSize)
363	if _, err = io.ReadFull(c.conn, buf); err != nil {
364		return nil, clientErr(err)
365	}
366	return buf, nil
367}
368
369func (c *client) simpleCall(req []byte) error {
370	resp, err := c.call(req)
371	if err != nil {
372		return err
373	}
374	if _, ok := resp.(*successAgentMsg); ok {
375		return nil
376	}
377	return errors.New("agent: failure")
378}
379
380func (c *client) RemoveAll() error {
381	return c.simpleCall([]byte{agentRemoveAllIdentities})
382}
383
384func (c *client) Remove(key ssh.PublicKey) error {
385	req := ssh.Marshal(&agentRemoveIdentityMsg{
386		KeyBlob: key.Marshal(),
387	})
388	return c.simpleCall(req)
389}
390
391func (c *client) Lock(passphrase []byte) error {
392	req := ssh.Marshal(&agentLockMsg{
393		Passphrase: passphrase,
394	})
395	return c.simpleCall(req)
396}
397
398func (c *client) Unlock(passphrase []byte) error {
399	req := ssh.Marshal(&agentUnlockMsg{
400		Passphrase: passphrase,
401	})
402	return c.simpleCall(req)
403}
404
405// List returns the identities known to the agent.
406func (c *client) List() ([]*Key, error) {
407	// see [PROTOCOL.agent] section 2.5.2.
408	req := []byte{agentRequestIdentities}
409
410	msg, err := c.call(req)
411	if err != nil {
412		return nil, err
413	}
414
415	switch msg := msg.(type) {
416	case *identitiesAnswerAgentMsg:
417		if msg.NumKeys > maxAgentResponseBytes/8 {
418			return nil, errors.New("agent: too many keys in agent reply")
419		}
420		keys := make([]*Key, msg.NumKeys)
421		data := msg.Keys
422		for i := uint32(0); i < msg.NumKeys; i++ {
423			var key *Key
424			var err error
425			if key, data, err = parseKey(data); err != nil {
426				return nil, err
427			}
428			keys[i] = key
429		}
430		return keys, nil
431	case *failureAgentMsg:
432		return nil, errors.New("agent: failed to list keys")
433	}
434	panic("unreachable")
435}
436
437// Sign has the agent sign the data using a protocol 2 key as defined
438// in [PROTOCOL.agent] section 2.6.2.
439func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
440	return c.SignWithFlags(key, data, 0)
441}
442
443func (c *client) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) {
444	req := ssh.Marshal(signRequestAgentMsg{
445		KeyBlob: key.Marshal(),
446		Data:    data,
447		Flags:   uint32(flags),
448	})
449
450	msg, err := c.call(req)
451	if err != nil {
452		return nil, err
453	}
454
455	switch msg := msg.(type) {
456	case *signResponseAgentMsg:
457		var sig ssh.Signature
458		if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil {
459			return nil, err
460		}
461
462		return &sig, nil
463	case *failureAgentMsg:
464		return nil, errors.New("agent: failed to sign challenge")
465	}
466	panic("unreachable")
467}
468
469// unmarshal parses an agent message in packet, returning the parsed
470// form and the message type of packet.
471func unmarshal(packet []byte) (interface{}, error) {
472	if len(packet) < 1 {
473		return nil, errors.New("agent: empty packet")
474	}
475	var msg interface{}
476	switch packet[0] {
477	case agentFailure:
478		return new(failureAgentMsg), nil
479	case agentSuccess:
480		return new(successAgentMsg), nil
481	case agentIdentitiesAnswer:
482		msg = new(identitiesAnswerAgentMsg)
483	case agentSignResponse:
484		msg = new(signResponseAgentMsg)
485	case agentV1IdentitiesAnswer:
486		msg = new(agentV1IdentityMsg)
487	default:
488		return nil, fmt.Errorf("agent: unknown type tag %d", packet[0])
489	}
490	if err := ssh.Unmarshal(packet, msg); err != nil {
491		return nil, err
492	}
493	return msg, nil
494}
495
496type rsaKeyMsg struct {
497	Type        string `sshtype:"17|25"`
498	N           *big.Int
499	E           *big.Int
500	D           *big.Int
501	Iqmp        *big.Int // IQMP = Inverse Q Mod P
502	P           *big.Int
503	Q           *big.Int
504	Comments    string
505	Constraints []byte `ssh:"rest"`
506}
507
508type dsaKeyMsg struct {
509	Type        string `sshtype:"17|25"`
510	P           *big.Int
511	Q           *big.Int
512	G           *big.Int
513	Y           *big.Int
514	X           *big.Int
515	Comments    string
516	Constraints []byte `ssh:"rest"`
517}
518
519type ecdsaKeyMsg struct {
520	Type        string `sshtype:"17|25"`
521	Curve       string
522	KeyBytes    []byte
523	D           *big.Int
524	Comments    string
525	Constraints []byte `ssh:"rest"`
526}
527
528type ed25519KeyMsg struct {
529	Type        string `sshtype:"17|25"`
530	Pub         []byte
531	Priv        []byte
532	Comments    string
533	Constraints []byte `ssh:"rest"`
534}
535
536// Insert adds a private key to the agent.
537func (c *client) insertKey(s interface{}, comment string, constraints []byte) error {
538	var req []byte
539	switch k := s.(type) {
540	case *rsa.PrivateKey:
541		if len(k.Primes) != 2 {
542			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
543		}
544		k.Precompute()
545		req = ssh.Marshal(rsaKeyMsg{
546			Type:        ssh.KeyAlgoRSA,
547			N:           k.N,
548			E:           big.NewInt(int64(k.E)),
549			D:           k.D,
550			Iqmp:        k.Precomputed.Qinv,
551			P:           k.Primes[0],
552			Q:           k.Primes[1],
553			Comments:    comment,
554			Constraints: constraints,
555		})
556	case *dsa.PrivateKey:
557		req = ssh.Marshal(dsaKeyMsg{
558			Type:        ssh.KeyAlgoDSA,
559			P:           k.P,
560			Q:           k.Q,
561			G:           k.G,
562			Y:           k.Y,
563			X:           k.X,
564			Comments:    comment,
565			Constraints: constraints,
566		})
567	case *ecdsa.PrivateKey:
568		nistID := fmt.Sprintf("nistp%d", k.Params().BitSize)
569		req = ssh.Marshal(ecdsaKeyMsg{
570			Type:        "ecdsa-sha2-" + nistID,
571			Curve:       nistID,
572			KeyBytes:    elliptic.Marshal(k.Curve, k.X, k.Y),
573			D:           k.D,
574			Comments:    comment,
575			Constraints: constraints,
576		})
577	case ed25519.PrivateKey:
578		req = ssh.Marshal(ed25519KeyMsg{
579			Type:        ssh.KeyAlgoED25519,
580			Pub:         []byte(k)[32:],
581			Priv:        []byte(k),
582			Comments:    comment,
583			Constraints: constraints,
584		})
585	// This function originally supported only *ed25519.PrivateKey, however the
586	// general idiom is to pass ed25519.PrivateKey by value, not by pointer.
587	// We still support the pointer variant for backwards compatibility.
588	case *ed25519.PrivateKey:
589		req = ssh.Marshal(ed25519KeyMsg{
590			Type:        ssh.KeyAlgoED25519,
591			Pub:         []byte(*k)[32:],
592			Priv:        []byte(*k),
593			Comments:    comment,
594			Constraints: constraints,
595		})
596	default:
597		return fmt.Errorf("agent: unsupported key type %T", s)
598	}
599
600	// if constraints are present then the message type needs to be changed.
601	if len(constraints) != 0 {
602		req[0] = agentAddIDConstrained
603	}
604
605	resp, err := c.call(req)
606	if err != nil {
607		return err
608	}
609	if _, ok := resp.(*successAgentMsg); ok {
610		return nil
611	}
612	return errors.New("agent: failure")
613}
614
615type rsaCertMsg struct {
616	Type        string `sshtype:"17|25"`
617	CertBytes   []byte
618	D           *big.Int
619	Iqmp        *big.Int // IQMP = Inverse Q Mod P
620	P           *big.Int
621	Q           *big.Int
622	Comments    string
623	Constraints []byte `ssh:"rest"`
624}
625
626type dsaCertMsg struct {
627	Type        string `sshtype:"17|25"`
628	CertBytes   []byte
629	X           *big.Int
630	Comments    string
631	Constraints []byte `ssh:"rest"`
632}
633
634type ecdsaCertMsg struct {
635	Type        string `sshtype:"17|25"`
636	CertBytes   []byte
637	D           *big.Int
638	Comments    string
639	Constraints []byte `ssh:"rest"`
640}
641
642type ed25519CertMsg struct {
643	Type        string `sshtype:"17|25"`
644	CertBytes   []byte
645	Pub         []byte
646	Priv        []byte
647	Comments    string
648	Constraints []byte `ssh:"rest"`
649}
650
651// Add adds a private key to the agent. If a certificate is given,
652// that certificate is added instead as public key.
653func (c *client) Add(key AddedKey) error {
654	var constraints []byte
655
656	if secs := key.LifetimeSecs; secs != 0 {
657		constraints = append(constraints, ssh.Marshal(constrainLifetimeAgentMsg{secs})...)
658	}
659
660	if key.ConfirmBeforeUse {
661		constraints = append(constraints, agentConstrainConfirm)
662	}
663
664	cert := key.Certificate
665	if cert == nil {
666		return c.insertKey(key.PrivateKey, key.Comment, constraints)
667	}
668	return c.insertCert(key.PrivateKey, cert, key.Comment, constraints)
669}
670
671func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error {
672	var req []byte
673	switch k := s.(type) {
674	case *rsa.PrivateKey:
675		if len(k.Primes) != 2 {
676			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
677		}
678		k.Precompute()
679		req = ssh.Marshal(rsaCertMsg{
680			Type:        cert.Type(),
681			CertBytes:   cert.Marshal(),
682			D:           k.D,
683			Iqmp:        k.Precomputed.Qinv,
684			P:           k.Primes[0],
685			Q:           k.Primes[1],
686			Comments:    comment,
687			Constraints: constraints,
688		})
689	case *dsa.PrivateKey:
690		req = ssh.Marshal(dsaCertMsg{
691			Type:        cert.Type(),
692			CertBytes:   cert.Marshal(),
693			X:           k.X,
694			Comments:    comment,
695			Constraints: constraints,
696		})
697	case *ecdsa.PrivateKey:
698		req = ssh.Marshal(ecdsaCertMsg{
699			Type:        cert.Type(),
700			CertBytes:   cert.Marshal(),
701			D:           k.D,
702			Comments:    comment,
703			Constraints: constraints,
704		})
705	case ed25519.PrivateKey:
706		req = ssh.Marshal(ed25519CertMsg{
707			Type:        cert.Type(),
708			CertBytes:   cert.Marshal(),
709			Pub:         []byte(k)[32:],
710			Priv:        []byte(k),
711			Comments:    comment,
712			Constraints: constraints,
713		})
714	// This function originally supported only *ed25519.PrivateKey, however the
715	// general idiom is to pass ed25519.PrivateKey by value, not by pointer.
716	// We still support the pointer variant for backwards compatibility.
717	case *ed25519.PrivateKey:
718		req = ssh.Marshal(ed25519CertMsg{
719			Type:        cert.Type(),
720			CertBytes:   cert.Marshal(),
721			Pub:         []byte(*k)[32:],
722			Priv:        []byte(*k),
723			Comments:    comment,
724			Constraints: constraints,
725		})
726	default:
727		return fmt.Errorf("agent: unsupported key type %T", s)
728	}
729
730	// if constraints are present then the message type needs to be changed.
731	if len(constraints) != 0 {
732		req[0] = agentAddIDConstrained
733	}
734
735	signer, err := ssh.NewSignerFromKey(s)
736	if err != nil {
737		return err
738	}
739	if !bytes.Equal(cert.Key.Marshal(), signer.PublicKey().Marshal()) {
740		return errors.New("agent: signer and cert have different public key")
741	}
742
743	resp, err := c.call(req)
744	if err != nil {
745		return err
746	}
747	if _, ok := resp.(*successAgentMsg); ok {
748		return nil
749	}
750	return errors.New("agent: failure")
751}
752
753// Signers provides a callback for client authentication.
754func (c *client) Signers() ([]ssh.Signer, error) {
755	keys, err := c.List()
756	if err != nil {
757		return nil, err
758	}
759
760	var result []ssh.Signer
761	for _, k := range keys {
762		result = append(result, &agentKeyringSigner{c, k})
763	}
764	return result, nil
765}
766
767type agentKeyringSigner struct {
768	agent *client
769	pub   ssh.PublicKey
770}
771
772func (s *agentKeyringSigner) PublicKey() ssh.PublicKey {
773	return s.pub
774}
775
776func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) {
777	// The agent has its own entropy source, so the rand argument is ignored.
778	return s.agent.Sign(s.pub, data)
779}
780
781func (s *agentKeyringSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*ssh.Signature, error) {
782	if algorithm == "" || algorithm == underlyingAlgo(s.pub.Type()) {
783		return s.Sign(rand, data)
784	}
785
786	var flags SignatureFlags
787	switch algorithm {
788	case ssh.KeyAlgoRSASHA256:
789		flags = SignatureFlagRsaSha256
790	case ssh.KeyAlgoRSASHA512:
791		flags = SignatureFlagRsaSha512
792	default:
793		return nil, fmt.Errorf("agent: unsupported algorithm %q", algorithm)
794	}
795
796	return s.agent.SignWithFlags(s.pub, data, flags)
797}
798
799var _ ssh.AlgorithmSigner = &agentKeyringSigner{}
800
801// certKeyAlgoNames is a mapping from known certificate algorithm names to the
802// corresponding public key signature algorithm.
803//
804// This map must be kept in sync with the one in certs.go.
805var certKeyAlgoNames = map[string]string{
806	ssh.CertAlgoRSAv01:        ssh.KeyAlgoRSA,
807	ssh.CertAlgoRSASHA256v01:  ssh.KeyAlgoRSASHA256,
808	ssh.CertAlgoRSASHA512v01:  ssh.KeyAlgoRSASHA512,
809	ssh.CertAlgoDSAv01:        ssh.KeyAlgoDSA,
810	ssh.CertAlgoECDSA256v01:   ssh.KeyAlgoECDSA256,
811	ssh.CertAlgoECDSA384v01:   ssh.KeyAlgoECDSA384,
812	ssh.CertAlgoECDSA521v01:   ssh.KeyAlgoECDSA521,
813	ssh.CertAlgoSKECDSA256v01: ssh.KeyAlgoSKECDSA256,
814	ssh.CertAlgoED25519v01:    ssh.KeyAlgoED25519,
815	ssh.CertAlgoSKED25519v01:  ssh.KeyAlgoSKED25519,
816}
817
818// underlyingAlgo returns the signature algorithm associated with algo (which is
819// an advertised or negotiated public key or host key algorithm). These are
820// usually the same, except for certificate algorithms.
821func underlyingAlgo(algo string) string {
822	if a, ok := certKeyAlgoNames[algo]; ok {
823		return a
824	}
825	return algo
826}
827
828// Calls an extension method. It is up to the agent implementation as to whether or not
829// any particular extension is supported and may always return an error. Because the
830// type of the response is up to the implementation, this returns the bytes of the
831// response and does not attempt any type of unmarshalling.
832func (c *client) Extension(extensionType string, contents []byte) ([]byte, error) {
833	req := ssh.Marshal(extensionAgentMsg{
834		ExtensionType: extensionType,
835		Contents:      contents,
836	})
837	buf, err := c.callRaw(req)
838	if err != nil {
839		return nil, err
840	}
841	if len(buf) == 0 {
842		return nil, errors.New("agent: failure; empty response")
843	}
844	// [PROTOCOL.agent] section 4.7 indicates that an SSH_AGENT_FAILURE message
845	// represents an agent that does not support the extension
846	if buf[0] == agentFailure {
847		return nil, ErrExtensionUnsupported
848	}
849	if buf[0] == agentExtensionFailure {
850		return nil, errors.New("agent: generic extension failure")
851	}
852
853	return buf, nil
854}