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