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}