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