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	default:
434		return nil, fmt.Errorf("agent: failed to list keys, unexpected message type %T", msg)
435	}
436}
437
438// Sign has the agent sign the data using a protocol 2 key as defined
439// in [PROTOCOL.agent] section 2.6.2.
440func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
441	return c.SignWithFlags(key, data, 0)
442}
443
444func (c *client) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) {
445	req := ssh.Marshal(signRequestAgentMsg{
446		KeyBlob: key.Marshal(),
447		Data:    data,
448		Flags:   uint32(flags),
449	})
450
451	msg, err := c.call(req)
452	if err != nil {
453		return nil, err
454	}
455
456	switch msg := msg.(type) {
457	case *signResponseAgentMsg:
458		var sig ssh.Signature
459		if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil {
460			return nil, err
461		}
462
463		return &sig, nil
464	case *failureAgentMsg:
465		return nil, errors.New("agent: failed to sign challenge")
466	default:
467		return nil, fmt.Errorf("agent: failed to sign challenge, unexpected message type %T", msg)
468	}
469}
470
471// unmarshal parses an agent message in packet, returning the parsed
472// form and the message type of packet.
473func unmarshal(packet []byte) (interface{}, error) {
474	if len(packet) < 1 {
475		return nil, errors.New("agent: empty packet")
476	}
477	var msg interface{}
478	switch packet[0] {
479	case agentFailure:
480		return new(failureAgentMsg), nil
481	case agentSuccess:
482		return new(successAgentMsg), nil
483	case agentIdentitiesAnswer:
484		msg = new(identitiesAnswerAgentMsg)
485	case agentSignResponse:
486		msg = new(signResponseAgentMsg)
487	case agentV1IdentitiesAnswer:
488		msg = new(agentV1IdentityMsg)
489	default:
490		return nil, fmt.Errorf("agent: unknown type tag %d", packet[0])
491	}
492	if err := ssh.Unmarshal(packet, msg); err != nil {
493		return nil, err
494	}
495	return msg, nil
496}
497
498type rsaKeyMsg struct {
499	Type        string `sshtype:"17|25"`
500	N           *big.Int
501	E           *big.Int
502	D           *big.Int
503	Iqmp        *big.Int // IQMP = Inverse Q Mod P
504	P           *big.Int
505	Q           *big.Int
506	Comments    string
507	Constraints []byte `ssh:"rest"`
508}
509
510type dsaKeyMsg struct {
511	Type        string `sshtype:"17|25"`
512	P           *big.Int
513	Q           *big.Int
514	G           *big.Int
515	Y           *big.Int
516	X           *big.Int
517	Comments    string
518	Constraints []byte `ssh:"rest"`
519}
520
521type ecdsaKeyMsg struct {
522	Type        string `sshtype:"17|25"`
523	Curve       string
524	KeyBytes    []byte
525	D           *big.Int
526	Comments    string
527	Constraints []byte `ssh:"rest"`
528}
529
530type ed25519KeyMsg struct {
531	Type        string `sshtype:"17|25"`
532	Pub         []byte
533	Priv        []byte
534	Comments    string
535	Constraints []byte `ssh:"rest"`
536}
537
538// Insert adds a private key to the agent.
539func (c *client) insertKey(s interface{}, comment string, constraints []byte) error {
540	var req []byte
541	switch k := s.(type) {
542	case *rsa.PrivateKey:
543		if len(k.Primes) != 2 {
544			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
545		}
546		k.Precompute()
547		req = ssh.Marshal(rsaKeyMsg{
548			Type:        ssh.KeyAlgoRSA,
549			N:           k.N,
550			E:           big.NewInt(int64(k.E)),
551			D:           k.D,
552			Iqmp:        k.Precomputed.Qinv,
553			P:           k.Primes[0],
554			Q:           k.Primes[1],
555			Comments:    comment,
556			Constraints: constraints,
557		})
558	case *dsa.PrivateKey:
559		req = ssh.Marshal(dsaKeyMsg{
560			Type:        ssh.InsecureKeyAlgoDSA,
561			P:           k.P,
562			Q:           k.Q,
563			G:           k.G,
564			Y:           k.Y,
565			X:           k.X,
566			Comments:    comment,
567			Constraints: constraints,
568		})
569	case *ecdsa.PrivateKey:
570		nistID := fmt.Sprintf("nistp%d", k.Params().BitSize)
571		req = ssh.Marshal(ecdsaKeyMsg{
572			Type:        "ecdsa-sha2-" + nistID,
573			Curve:       nistID,
574			KeyBytes:    elliptic.Marshal(k.Curve, k.X, k.Y),
575			D:           k.D,
576			Comments:    comment,
577			Constraints: constraints,
578		})
579	case ed25519.PrivateKey:
580		req = ssh.Marshal(ed25519KeyMsg{
581			Type:        ssh.KeyAlgoED25519,
582			Pub:         []byte(k)[32:],
583			Priv:        []byte(k),
584			Comments:    comment,
585			Constraints: constraints,
586		})
587	// This function originally supported only *ed25519.PrivateKey, however the
588	// general idiom is to pass ed25519.PrivateKey by value, not by pointer.
589	// We still support the pointer variant for backwards compatibility.
590	case *ed25519.PrivateKey:
591		req = ssh.Marshal(ed25519KeyMsg{
592			Type:        ssh.KeyAlgoED25519,
593			Pub:         []byte(*k)[32:],
594			Priv:        []byte(*k),
595			Comments:    comment,
596			Constraints: constraints,
597		})
598	default:
599		return fmt.Errorf("agent: unsupported key type %T", s)
600	}
601
602	// if constraints are present then the message type needs to be changed.
603	if len(constraints) != 0 {
604		req[0] = agentAddIDConstrained
605	}
606
607	resp, err := c.call(req)
608	if err != nil {
609		return err
610	}
611	if _, ok := resp.(*successAgentMsg); ok {
612		return nil
613	}
614	return errors.New("agent: failure")
615}
616
617type rsaCertMsg struct {
618	Type        string `sshtype:"17|25"`
619	CertBytes   []byte
620	D           *big.Int
621	Iqmp        *big.Int // IQMP = Inverse Q Mod P
622	P           *big.Int
623	Q           *big.Int
624	Comments    string
625	Constraints []byte `ssh:"rest"`
626}
627
628type dsaCertMsg struct {
629	Type        string `sshtype:"17|25"`
630	CertBytes   []byte
631	X           *big.Int
632	Comments    string
633	Constraints []byte `ssh:"rest"`
634}
635
636type ecdsaCertMsg struct {
637	Type        string `sshtype:"17|25"`
638	CertBytes   []byte
639	D           *big.Int
640	Comments    string
641	Constraints []byte `ssh:"rest"`
642}
643
644type ed25519CertMsg struct {
645	Type        string `sshtype:"17|25"`
646	CertBytes   []byte
647	Pub         []byte
648	Priv        []byte
649	Comments    string
650	Constraints []byte `ssh:"rest"`
651}
652
653// Add adds a private key to the agent. If a certificate is given,
654// that certificate is added instead as public key.
655func (c *client) Add(key AddedKey) error {
656	var constraints []byte
657
658	if secs := key.LifetimeSecs; secs != 0 {
659		constraints = append(constraints, ssh.Marshal(constrainLifetimeAgentMsg{secs})...)
660	}
661
662	if key.ConfirmBeforeUse {
663		constraints = append(constraints, agentConstrainConfirm)
664	}
665
666	cert := key.Certificate
667	if cert == nil {
668		return c.insertKey(key.PrivateKey, key.Comment, constraints)
669	}
670	return c.insertCert(key.PrivateKey, cert, key.Comment, constraints)
671}
672
673func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error {
674	var req []byte
675	switch k := s.(type) {
676	case *rsa.PrivateKey:
677		if len(k.Primes) != 2 {
678			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
679		}
680		k.Precompute()
681		req = ssh.Marshal(rsaCertMsg{
682			Type:        cert.Type(),
683			CertBytes:   cert.Marshal(),
684			D:           k.D,
685			Iqmp:        k.Precomputed.Qinv,
686			P:           k.Primes[0],
687			Q:           k.Primes[1],
688			Comments:    comment,
689			Constraints: constraints,
690		})
691	case *dsa.PrivateKey:
692		req = ssh.Marshal(dsaCertMsg{
693			Type:        cert.Type(),
694			CertBytes:   cert.Marshal(),
695			X:           k.X,
696			Comments:    comment,
697			Constraints: constraints,
698		})
699	case *ecdsa.PrivateKey:
700		req = ssh.Marshal(ecdsaCertMsg{
701			Type:        cert.Type(),
702			CertBytes:   cert.Marshal(),
703			D:           k.D,
704			Comments:    comment,
705			Constraints: constraints,
706		})
707	case ed25519.PrivateKey:
708		req = ssh.Marshal(ed25519CertMsg{
709			Type:        cert.Type(),
710			CertBytes:   cert.Marshal(),
711			Pub:         []byte(k)[32:],
712			Priv:        []byte(k),
713			Comments:    comment,
714			Constraints: constraints,
715		})
716	// This function originally supported only *ed25519.PrivateKey, however the
717	// general idiom is to pass ed25519.PrivateKey by value, not by pointer.
718	// We still support the pointer variant for backwards compatibility.
719	case *ed25519.PrivateKey:
720		req = ssh.Marshal(ed25519CertMsg{
721			Type:        cert.Type(),
722			CertBytes:   cert.Marshal(),
723			Pub:         []byte(*k)[32:],
724			Priv:        []byte(*k),
725			Comments:    comment,
726			Constraints: constraints,
727		})
728	default:
729		return fmt.Errorf("agent: unsupported key type %T", s)
730	}
731
732	// if constraints are present then the message type needs to be changed.
733	if len(constraints) != 0 {
734		req[0] = agentAddIDConstrained
735	}
736
737	signer, err := ssh.NewSignerFromKey(s)
738	if err != nil {
739		return err
740	}
741	if !bytes.Equal(cert.Key.Marshal(), signer.PublicKey().Marshal()) {
742		return errors.New("agent: signer and cert have different public key")
743	}
744
745	resp, err := c.call(req)
746	if err != nil {
747		return err
748	}
749	if _, ok := resp.(*successAgentMsg); ok {
750		return nil
751	}
752	return errors.New("agent: failure")
753}
754
755// Signers provides a callback for client authentication.
756func (c *client) Signers() ([]ssh.Signer, error) {
757	keys, err := c.List()
758	if err != nil {
759		return nil, err
760	}
761
762	var result []ssh.Signer
763	for _, k := range keys {
764		result = append(result, &agentKeyringSigner{c, k})
765	}
766	return result, nil
767}
768
769type agentKeyringSigner struct {
770	agent *client
771	pub   ssh.PublicKey
772}
773
774func (s *agentKeyringSigner) PublicKey() ssh.PublicKey {
775	return s.pub
776}
777
778func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) {
779	// The agent has its own entropy source, so the rand argument is ignored.
780	return s.agent.Sign(s.pub, data)
781}
782
783func (s *agentKeyringSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*ssh.Signature, error) {
784	if algorithm == "" || algorithm == underlyingAlgo(s.pub.Type()) {
785		return s.Sign(rand, data)
786	}
787
788	var flags SignatureFlags
789	switch algorithm {
790	case ssh.KeyAlgoRSASHA256:
791		flags = SignatureFlagRsaSha256
792	case ssh.KeyAlgoRSASHA512:
793		flags = SignatureFlagRsaSha512
794	default:
795		return nil, fmt.Errorf("agent: unsupported algorithm %q", algorithm)
796	}
797
798	return s.agent.SignWithFlags(s.pub, data, flags)
799}
800
801var _ ssh.AlgorithmSigner = &agentKeyringSigner{}
802
803// certKeyAlgoNames is a mapping from known certificate algorithm names to the
804// corresponding public key signature algorithm.
805//
806// This map must be kept in sync with the one in certs.go.
807var certKeyAlgoNames = map[string]string{
808	ssh.CertAlgoRSAv01:         ssh.KeyAlgoRSA,
809	ssh.CertAlgoRSASHA256v01:   ssh.KeyAlgoRSASHA256,
810	ssh.CertAlgoRSASHA512v01:   ssh.KeyAlgoRSASHA512,
811	ssh.InsecureCertAlgoDSAv01: ssh.InsecureKeyAlgoDSA,
812	ssh.CertAlgoECDSA256v01:    ssh.KeyAlgoECDSA256,
813	ssh.CertAlgoECDSA384v01:    ssh.KeyAlgoECDSA384,
814	ssh.CertAlgoECDSA521v01:    ssh.KeyAlgoECDSA521,
815	ssh.CertAlgoSKECDSA256v01:  ssh.KeyAlgoSKECDSA256,
816	ssh.CertAlgoED25519v01:     ssh.KeyAlgoED25519,
817	ssh.CertAlgoSKED25519v01:   ssh.KeyAlgoSKED25519,
818}
819
820// underlyingAlgo returns the signature algorithm associated with algo (which is
821// an advertised or negotiated public key or host key algorithm). These are
822// usually the same, except for certificate algorithms.
823func underlyingAlgo(algo string) string {
824	if a, ok := certKeyAlgoNames[algo]; ok {
825		return a
826	}
827	return algo
828}
829
830// Calls an extension method. It is up to the agent implementation as to whether or not
831// any particular extension is supported and may always return an error. Because the
832// type of the response is up to the implementation, this returns the bytes of the
833// response and does not attempt any type of unmarshalling.
834func (c *client) Extension(extensionType string, contents []byte) ([]byte, error) {
835	req := ssh.Marshal(extensionAgentMsg{
836		ExtensionType: extensionType,
837		Contents:      contents,
838	})
839	buf, err := c.callRaw(req)
840	if err != nil {
841		return nil, err
842	}
843	if len(buf) == 0 {
844		return nil, errors.New("agent: failure; empty response")
845	}
846	// [PROTOCOL.agent] section 4.7 indicates that an SSH_AGENT_FAILURE message
847	// represents an agent that does not support the extension
848	if buf[0] == agentFailure {
849		return nil, ErrExtensionUnsupported
850	}
851	if buf[0] == agentExtensionFailure {
852		return nil, errors.New("agent: generic extension failure")
853	}
854
855	return buf, nil
856}