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  }