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]: https://tools.ietf.org/html/draft-miller-ssh-agent-00
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	"crypto"
29	"golang.org/x/crypto/ed25519"
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 [RFC4251], 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	agentConstrainExtension = 3
147)
148
149// maxAgentResponseBytes is the maximum agent reply size that is accepted. This
150// is a sanity check, not a limit in the spec.
151const maxAgentResponseBytes = 16 << 20
152
153// Agent messages:
154// These structures mirror the wire format of the corresponding ssh agent
155// messages found in [PROTOCOL.agent].
156
157// 3.4 Generic replies from agent to client
158const agentFailure = 5
159
160type failureAgentMsg struct{}
161
162const agentSuccess = 6
163
164type successAgentMsg struct{}
165
166// See [PROTOCOL.agent], section 2.5.2.
167const agentRequestIdentities = 11
168
169type requestIdentitiesAgentMsg struct{}
170
171// See [PROTOCOL.agent], section 2.5.2.
172const agentIdentitiesAnswer = 12
173
174type identitiesAnswerAgentMsg struct {
175	NumKeys uint32 `sshtype:"12"`
176	Keys    []byte `ssh:"rest"`
177}
178
179// See [PROTOCOL.agent], section 2.6.2.
180const agentSignRequest = 13
181
182type signRequestAgentMsg struct {
183	KeyBlob []byte `sshtype:"13"`
184	Data    []byte
185	Flags   uint32
186}
187
188// See [PROTOCOL.agent], section 2.6.2.
189
190// 3.6 Replies from agent to client for protocol 2 key operations
191const agentSignResponse = 14
192
193type signResponseAgentMsg struct {
194	SigBlob []byte `sshtype:"14"`
195}
196
197type publicKey struct {
198	Format string
199	Rest   []byte `ssh:"rest"`
200}
201
202// 3.7 Key constraint identifiers
203type constrainLifetimeAgentMsg struct {
204	LifetimeSecs uint32 `sshtype:"1"`
205}
206
207type constrainExtensionAgentMsg struct {
208	ExtensionName    string `sshtype:"3"`
209	ExtensionDetails []byte
210
211	// Rest is a field used for parsing, not part of message
212	Rest []byte `ssh:"rest"`
213}
214
215// See [PROTOCOL.agent], section 4.7
216const agentExtension = 27
217const agentExtensionFailure = 28
218
219// ErrExtensionUnsupported indicates that an extension defined in
220// [PROTOCOL.agent] section 4.7 is unsupported by the agent. Specifically this
221// error indicates that the agent returned a standard SSH_AGENT_FAILURE message
222// as the result of a SSH_AGENTC_EXTENSION request. Note that the protocol
223// specification (and therefore this error) does not distinguish between a
224// specific extension being unsupported and extensions being unsupported entirely.
225var ErrExtensionUnsupported = errors.New("agent: extension unsupported")
226
227type extensionAgentMsg struct {
228	ExtensionType string `sshtype:"27"`
229	Contents      []byte
230}
231
232// Key represents a protocol 2 public key as defined in
233// [PROTOCOL.agent], section 2.5.2.
234type Key struct {
235	Format  string
236	Blob    []byte
237	Comment string
238}
239
240func clientErr(err error) error {
241	return fmt.Errorf("agent: client error: %v", err)
242}
243
244// String returns the storage form of an agent key with the format, base64
245// encoded serialized key, and the comment if it is not empty.
246func (k *Key) String() string {
247	s := string(k.Format) + " " + base64.StdEncoding.EncodeToString(k.Blob)
248
249	if k.Comment != "" {
250		s += " " + k.Comment
251	}
252
253	return s
254}
255
256// Type returns the public key type.
257func (k *Key) Type() string {
258	return k.Format
259}
260
261// Marshal returns key blob to satisfy the ssh.PublicKey interface.
262func (k *Key) Marshal() []byte {
263	return k.Blob
264}
265
266// Verify satisfies the ssh.PublicKey interface.
267func (k *Key) Verify(data []byte, sig *ssh.Signature) error {
268	pubKey, err := ssh.ParsePublicKey(k.Blob)
269	if err != nil {
270		return fmt.Errorf("agent: bad public key: %v", err)
271	}
272	return pubKey.Verify(data, sig)
273}
274
275type wireKey struct {
276	Format string
277	Rest   []byte `ssh:"rest"`
278}
279
280func parseKey(in []byte) (out *Key, rest []byte, err error) {
281	var record struct {
282		Blob    []byte
283		Comment string
284		Rest    []byte `ssh:"rest"`
285	}
286
287	if err := ssh.Unmarshal(in, &record); err != nil {
288		return nil, nil, err
289	}
290
291	var wk wireKey
292	if err := ssh.Unmarshal(record.Blob, &wk); err != nil {
293		return nil, nil, err
294	}
295
296	return &Key{
297		Format:  wk.Format,
298		Blob:    record.Blob,
299		Comment: record.Comment,
300	}, record.Rest, nil
301}
302
303// client is a client for an ssh-agent process.
304type client struct {
305	// conn is typically a *net.UnixConn
306	conn io.ReadWriter
307	// mu is used to prevent concurrent access to the agent
308	mu sync.Mutex
309}
310
311// NewClient returns an Agent that talks to an ssh-agent process over
312// the given connection.
313func NewClient(rw io.ReadWriter) ExtendedAgent {
314	return &client{conn: rw}
315}
316
317// call sends an RPC to the agent. On success, the reply is
318// unmarshaled into reply and replyType is set to the first byte of
319// the reply, which contains the type of the message.
320func (c *client) call(req []byte) (reply interface{}, err error) {
321	buf, err := c.callRaw(req)
322	if err != nil {
323		return nil, err
324	}
325	reply, err = unmarshal(buf)
326	if err != nil {
327		return nil, clientErr(err)
328	}
329	return reply, nil
330}
331
332// callRaw sends an RPC to the agent. On success, the raw
333// bytes of the response are returned; no unmarshalling is
334// performed on the response.
335func (c *client) callRaw(req []byte) (reply []byte, err error) {
336	c.mu.Lock()
337	defer c.mu.Unlock()
338
339	msg := make([]byte, 4+len(req))
340	binary.BigEndian.PutUint32(msg, uint32(len(req)))
341	copy(msg[4:], req)
342	if _, err = c.conn.Write(msg); err != nil {
343		return nil, clientErr(err)
344	}
345
346	var respSizeBuf [4]byte
347	if _, err = io.ReadFull(c.conn, respSizeBuf[:]); err != nil {
348		return nil, clientErr(err)
349	}
350	respSize := binary.BigEndian.Uint32(respSizeBuf[:])
351	if respSize > maxAgentResponseBytes {
352		return nil, clientErr(errors.New("response too large"))
353	}
354
355	buf := make([]byte, respSize)
356	if _, err = io.ReadFull(c.conn, buf); err != nil {
357		return nil, clientErr(err)
358	}
359	return buf, nil
360}
361
362func (c *client) simpleCall(req []byte) error {
363	resp, err := c.call(req)
364	if err != nil {
365		return err
366	}
367	if _, ok := resp.(*successAgentMsg); ok {
368		return nil
369	}
370	return errors.New("agent: failure")
371}
372
373func (c *client) RemoveAll() error {
374	return c.simpleCall([]byte{agentRemoveAllIdentities})
375}
376
377func (c *client) Remove(key ssh.PublicKey) error {
378	req := ssh.Marshal(&agentRemoveIdentityMsg{
379		KeyBlob: key.Marshal(),
380	})
381	return c.simpleCall(req)
382}
383
384func (c *client) Lock(passphrase []byte) error {
385	req := ssh.Marshal(&agentLockMsg{
386		Passphrase: passphrase,
387	})
388	return c.simpleCall(req)
389}
390
391func (c *client) Unlock(passphrase []byte) error {
392	req := ssh.Marshal(&agentUnlockMsg{
393		Passphrase: passphrase,
394	})
395	return c.simpleCall(req)
396}
397
398// List returns the identities known to the agent.
399func (c *client) List() ([]*Key, error) {
400	// see [PROTOCOL.agent] section 2.5.2.
401	req := []byte{agentRequestIdentities}
402
403	msg, err := c.call(req)
404	if err != nil {
405		return nil, err
406	}
407
408	switch msg := msg.(type) {
409	case *identitiesAnswerAgentMsg:
410		if msg.NumKeys > maxAgentResponseBytes/8 {
411			return nil, errors.New("agent: too many keys in agent reply")
412		}
413		keys := make([]*Key, msg.NumKeys)
414		data := msg.Keys
415		for i := uint32(0); i < msg.NumKeys; i++ {
416			var key *Key
417			var err error
418			if key, data, err = parseKey(data); err != nil {
419				return nil, err
420			}
421			keys[i] = key
422		}
423		return keys, nil
424	case *failureAgentMsg:
425		return nil, errors.New("agent: failed to list keys")
426	}
427	panic("unreachable")
428}
429
430// Sign has the agent sign the data using a protocol 2 key as defined
431// in [PROTOCOL.agent] section 2.6.2.
432func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
433	return c.SignWithFlags(key, data, 0)
434}
435
436func (c *client) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) {
437	req := ssh.Marshal(signRequestAgentMsg{
438		KeyBlob: key.Marshal(),
439		Data:    data,
440		Flags:   uint32(flags),
441	})
442
443	msg, err := c.call(req)
444	if err != nil {
445		return nil, err
446	}
447
448	switch msg := msg.(type) {
449	case *signResponseAgentMsg:
450		var sig ssh.Signature
451		if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil {
452			return nil, err
453		}
454
455		return &sig, nil
456	case *failureAgentMsg:
457		return nil, errors.New("agent: failed to sign challenge")
458	}
459	panic("unreachable")
460}
461
462// unmarshal parses an agent message in packet, returning the parsed
463// form and the message type of packet.
464func unmarshal(packet []byte) (interface{}, error) {
465	if len(packet) < 1 {
466		return nil, errors.New("agent: empty packet")
467	}
468	var msg interface{}
469	switch packet[0] {
470	case agentFailure:
471		return new(failureAgentMsg), nil
472	case agentSuccess:
473		return new(successAgentMsg), nil
474	case agentIdentitiesAnswer:
475		msg = new(identitiesAnswerAgentMsg)
476	case agentSignResponse:
477		msg = new(signResponseAgentMsg)
478	case agentV1IdentitiesAnswer:
479		msg = new(agentV1IdentityMsg)
480	default:
481		return nil, fmt.Errorf("agent: unknown type tag %d", packet[0])
482	}
483	if err := ssh.Unmarshal(packet, msg); err != nil {
484		return nil, err
485	}
486	return msg, nil
487}
488
489type rsaKeyMsg struct {
490	Type        string `sshtype:"17|25"`
491	N           *big.Int
492	E           *big.Int
493	D           *big.Int
494	Iqmp        *big.Int // IQMP = Inverse Q Mod P
495	P           *big.Int
496	Q           *big.Int
497	Comments    string
498	Constraints []byte `ssh:"rest"`
499}
500
501type dsaKeyMsg struct {
502	Type        string `sshtype:"17|25"`
503	P           *big.Int
504	Q           *big.Int
505	G           *big.Int
506	Y           *big.Int
507	X           *big.Int
508	Comments    string
509	Constraints []byte `ssh:"rest"`
510}
511
512type ecdsaKeyMsg struct {
513	Type        string `sshtype:"17|25"`
514	Curve       string
515	KeyBytes    []byte
516	D           *big.Int
517	Comments    string
518	Constraints []byte `ssh:"rest"`
519}
520
521type ed25519KeyMsg struct {
522	Type        string `sshtype:"17|25"`
523	Pub         []byte
524	Priv        []byte
525	Comments    string
526	Constraints []byte `ssh:"rest"`
527}
528
529// Insert adds a private key to the agent.
530func (c *client) insertKey(s interface{}, comment string, constraints []byte) error {
531	var req []byte
532	switch k := s.(type) {
533	case *rsa.PrivateKey:
534		if len(k.Primes) != 2 {
535			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
536		}
537		k.Precompute()
538		req = ssh.Marshal(rsaKeyMsg{
539			Type:        ssh.KeyAlgoRSA,
540			N:           k.N,
541			E:           big.NewInt(int64(k.E)),
542			D:           k.D,
543			Iqmp:        k.Precomputed.Qinv,
544			P:           k.Primes[0],
545			Q:           k.Primes[1],
546			Comments:    comment,
547			Constraints: constraints,
548		})
549	case *dsa.PrivateKey:
550		req = ssh.Marshal(dsaKeyMsg{
551			Type:        ssh.KeyAlgoDSA,
552			P:           k.P,
553			Q:           k.Q,
554			G:           k.G,
555			Y:           k.Y,
556			X:           k.X,
557			Comments:    comment,
558			Constraints: constraints,
559		})
560	case *ecdsa.PrivateKey:
561		nistID := fmt.Sprintf("nistp%d", k.Params().BitSize)
562		req = ssh.Marshal(ecdsaKeyMsg{
563			Type:        "ecdsa-sha2-" + nistID,
564			Curve:       nistID,
565			KeyBytes:    elliptic.Marshal(k.Curve, k.X, k.Y),
566			D:           k.D,
567			Comments:    comment,
568			Constraints: constraints,
569		})
570	case ed25519.PrivateKey:
571		req = ssh.Marshal(ed25519KeyMsg{
572			Type:        ssh.KeyAlgoED25519,
573			Pub:         []byte(k)[32:],
574			Priv:        []byte(k),
575			Comments:    comment,
576			Constraints: constraints,
577		})
578	// This function originally supported only *ed25519.PrivateKey, however the
579	// general idiom is to pass ed25519.PrivateKey by value, not by pointer.
580	// We still support the pointer variant for backwards compatibility.
581	case *ed25519.PrivateKey:
582		req = ssh.Marshal(ed25519KeyMsg{
583			Type:        ssh.KeyAlgoED25519,
584			Pub:         []byte(*k)[32:],
585			Priv:        []byte(*k),
586			Comments:    comment,
587			Constraints: constraints,
588		})
589	default:
590		return fmt.Errorf("agent: unsupported key type %T", s)
591	}
592
593	// if constraints are present then the message type needs to be changed.
594	if len(constraints) != 0 {
595		req[0] = agentAddIDConstrained
596	}
597
598	resp, err := c.call(req)
599	if err != nil {
600		return err
601	}
602	if _, ok := resp.(*successAgentMsg); ok {
603		return nil
604	}
605	return errors.New("agent: failure")
606}
607
608type rsaCertMsg struct {
609	Type        string `sshtype:"17|25"`
610	CertBytes   []byte
611	D           *big.Int
612	Iqmp        *big.Int // IQMP = Inverse Q Mod P
613	P           *big.Int
614	Q           *big.Int
615	Comments    string
616	Constraints []byte `ssh:"rest"`
617}
618
619type dsaCertMsg struct {
620	Type        string `sshtype:"17|25"`
621	CertBytes   []byte
622	X           *big.Int
623	Comments    string
624	Constraints []byte `ssh:"rest"`
625}
626
627type ecdsaCertMsg struct {
628	Type        string `sshtype:"17|25"`
629	CertBytes   []byte
630	D           *big.Int
631	Comments    string
632	Constraints []byte `ssh:"rest"`
633}
634
635type ed25519CertMsg struct {
636	Type        string `sshtype:"17|25"`
637	CertBytes   []byte
638	Pub         []byte
639	Priv        []byte
640	Comments    string
641	Constraints []byte `ssh:"rest"`
642}
643
644// Add adds a private key to the agent. If a certificate is given,
645// that certificate is added instead as public key.
646func (c *client) Add(key AddedKey) error {
647	var constraints []byte
648
649	if secs := key.LifetimeSecs; secs != 0 {
650		constraints = append(constraints, ssh.Marshal(constrainLifetimeAgentMsg{secs})...)
651	}
652
653	if key.ConfirmBeforeUse {
654		constraints = append(constraints, agentConstrainConfirm)
655	}
656
657	cert := key.Certificate
658	if cert == nil {
659		return c.insertKey(key.PrivateKey, key.Comment, constraints)
660	}
661	return c.insertCert(key.PrivateKey, cert, key.Comment, constraints)
662}
663
664func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error {
665	var req []byte
666	switch k := s.(type) {
667	case *rsa.PrivateKey:
668		if len(k.Primes) != 2 {
669			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
670		}
671		k.Precompute()
672		req = ssh.Marshal(rsaCertMsg{
673			Type:        cert.Type(),
674			CertBytes:   cert.Marshal(),
675			D:           k.D,
676			Iqmp:        k.Precomputed.Qinv,
677			P:           k.Primes[0],
678			Q:           k.Primes[1],
679			Comments:    comment,
680			Constraints: constraints,
681		})
682	case *dsa.PrivateKey:
683		req = ssh.Marshal(dsaCertMsg{
684			Type:        cert.Type(),
685			CertBytes:   cert.Marshal(),
686			X:           k.X,
687			Comments:    comment,
688			Constraints: constraints,
689		})
690	case *ecdsa.PrivateKey:
691		req = ssh.Marshal(ecdsaCertMsg{
692			Type:        cert.Type(),
693			CertBytes:   cert.Marshal(),
694			D:           k.D,
695			Comments:    comment,
696			Constraints: constraints,
697		})
698	case ed25519.PrivateKey:
699		req = ssh.Marshal(ed25519CertMsg{
700			Type:        cert.Type(),
701			CertBytes:   cert.Marshal(),
702			Pub:         []byte(k)[32:],
703			Priv:        []byte(k),
704			Comments:    comment,
705			Constraints: constraints,
706		})
707	// This function originally supported only *ed25519.PrivateKey, however the
708	// general idiom is to pass ed25519.PrivateKey by value, not by pointer.
709	// We still support the pointer variant for backwards compatibility.
710	case *ed25519.PrivateKey:
711		req = ssh.Marshal(ed25519CertMsg{
712			Type:        cert.Type(),
713			CertBytes:   cert.Marshal(),
714			Pub:         []byte(*k)[32:],
715			Priv:        []byte(*k),
716			Comments:    comment,
717			Constraints: constraints,
718		})
719	default:
720		return fmt.Errorf("agent: unsupported key type %T", s)
721	}
722
723	// if constraints are present then the message type needs to be changed.
724	if len(constraints) != 0 {
725		req[0] = agentAddIDConstrained
726	}
727
728	signer, err := ssh.NewSignerFromKey(s)
729	if err != nil {
730		return err
731	}
732	if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 {
733		return errors.New("agent: signer and cert have different public key")
734	}
735
736	resp, err := c.call(req)
737	if err != nil {
738		return err
739	}
740	if _, ok := resp.(*successAgentMsg); ok {
741		return nil
742	}
743	return errors.New("agent: failure")
744}
745
746// Signers provides a callback for client authentication.
747func (c *client) Signers() ([]ssh.Signer, error) {
748	keys, err := c.List()
749	if err != nil {
750		return nil, err
751	}
752
753	var result []ssh.Signer
754	for _, k := range keys {
755		result = append(result, &agentKeyringSigner{c, k})
756	}
757	return result, nil
758}
759
760type agentKeyringSigner struct {
761	agent *client
762	pub   ssh.PublicKey
763}
764
765func (s *agentKeyringSigner) PublicKey() ssh.PublicKey {
766	return s.pub
767}
768
769func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) {
770	// The agent has its own entropy source, so the rand argument is ignored.
771	return s.agent.Sign(s.pub, data)
772}
773
774func (s *agentKeyringSigner) SignWithOpts(rand io.Reader, data []byte, opts crypto.SignerOpts) (*ssh.Signature, error) {
775	var flags SignatureFlags
776	if opts != nil {
777		switch opts.HashFunc() {
778		case crypto.SHA256:
779			flags = SignatureFlagRsaSha256
780		case crypto.SHA512:
781			flags = SignatureFlagRsaSha512
782		}
783	}
784	return s.agent.SignWithFlags(s.pub, data, flags)
785}
786
787// Calls an extension method. It is up to the agent implementation as to whether or not
788// any particular extension is supported and may always return an error. Because the
789// type of the response is up to the implementation, this returns the bytes of the
790// response and does not attempt any type of unmarshalling.
791func (c *client) Extension(extensionType string, contents []byte) ([]byte, error) {
792	req := ssh.Marshal(extensionAgentMsg{
793		ExtensionType: extensionType,
794		Contents:      contents,
795	})
796	buf, err := c.callRaw(req)
797	if err != nil {
798		return nil, err
799	}
800	if len(buf) == 0 {
801		return nil, errors.New("agent: failure; empty response")
802	}
803	// [PROTOCOL.agent] section 4.7 indicates that an SSH_AGENT_FAILURE message
804	// represents an agent that does not support the extension
805	if buf[0] == agentFailure {
806		return nil, ErrExtensionUnsupported
807	}
808	if buf[0] == agentExtensionFailure {
809		return nil, errors.New("agent: generic extension failure")
810	}
811
812	return buf, nil
813}
814