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