github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/crypto/ssh/agent/client.go (about) 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 12 package agent // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh/agent" 13 14 import ( 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 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh" 30 "golang.org/x/crypto/ed25519" 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. 35 type SignatureFlags uint32 36 37 // SignatureFlag values as defined in [PROTOCOL.agent] section 5.3. 38 const ( 39 SignatureFlagReserved SignatureFlags = 1 << iota 40 SignatureFlagRsaSha256 41 SignatureFlagRsaSha512 42 ) 43 44 // Agent represents the capabilities of an ssh-agent. 45 type 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 72 type 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. 93 type 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. 104 type 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. 126 const ( 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. 151 const 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 158 const agentFailure = 5 159 160 type failureAgentMsg struct{} 161 162 const agentSuccess = 6 163 164 type successAgentMsg struct{} 165 166 // See [PROTOCOL.agent], section 2.5.2. 167 const agentRequestIdentities = 11 168 169 type requestIdentitiesAgentMsg struct{} 170 171 // See [PROTOCOL.agent], section 2.5.2. 172 const agentIdentitiesAnswer = 12 173 174 type identitiesAnswerAgentMsg struct { 175 NumKeys uint32 `sshtype:"12"` 176 Keys []byte `ssh:"rest"` 177 } 178 179 // See [PROTOCOL.agent], section 2.6.2. 180 const agentSignRequest = 13 181 182 type 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 191 const agentSignResponse = 14 192 193 type signResponseAgentMsg struct { 194 SigBlob []byte `sshtype:"14"` 195 } 196 197 type publicKey struct { 198 Format string 199 Rest []byte `ssh:"rest"` 200 } 201 202 // 3.7 Key constraint identifiers 203 type constrainLifetimeAgentMsg struct { 204 LifetimeSecs uint32 `sshtype:"1"` 205 } 206 207 type 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 216 const agentExtension = 27 217 const 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. 225 var ErrExtensionUnsupported = errors.New("agent: extension unsupported") 226 227 type 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. 234 type Key struct { 235 Format string 236 Blob []byte 237 Comment string 238 } 239 240 func 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. 246 func (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. 257 func (k *Key) Type() string { 258 return k.Format 259 } 260 261 // Marshal returns key blob to satisfy the ssh.PublicKey interface. 262 func (k *Key) Marshal() []byte { 263 return k.Blob 264 } 265 266 // Verify satisfies the ssh.PublicKey interface. 267 func (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 275 type wireKey struct { 276 Format string 277 Rest []byte `ssh:"rest"` 278 } 279 280 func 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. 304 type 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. 313 func 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. 320 func (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. 335 func (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 362 func (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 373 func (c *client) RemoveAll() error { 374 return c.simpleCall([]byte{agentRemoveAllIdentities}) 375 } 376 377 func (c *client) Remove(key ssh.PublicKey) error { 378 req := ssh.Marshal(&agentRemoveIdentityMsg{ 379 KeyBlob: key.Marshal(), 380 }) 381 return c.simpleCall(req) 382 } 383 384 func (c *client) Lock(passphrase []byte) error { 385 req := ssh.Marshal(&agentLockMsg{ 386 Passphrase: passphrase, 387 }) 388 return c.simpleCall(req) 389 } 390 391 func (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. 399 func (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. 432 func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { 433 return c.SignWithFlags(key, data, 0) 434 } 435 436 func (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. 464 func 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 489 type 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 501 type 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 512 type 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 521 type 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. 530 func (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 608 type 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 619 type 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 627 type 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 635 type 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. 646 func (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 664 func (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. 747 func (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 760 type agentKeyringSigner struct { 761 agent *client 762 pub ssh.PublicKey 763 } 764 765 func (s *agentKeyringSigner) PublicKey() ssh.PublicKey { 766 return s.pub 767 } 768 769 func (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 774 func (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. 791 func (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 }