...

Source file src/golang.org/x/crypto/ssh/agent/client.go

Documentation: golang.org/x/crypto/ssh/agent

     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
    13  package agent // import "golang.org/x/crypto/ssh/agent"
    14  
    15  import (
    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.
    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 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.
   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  	// 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.
   156  const 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
   163  const agentFailure = 5
   164  
   165  type failureAgentMsg struct{}
   166  
   167  const agentSuccess = 6
   168  
   169  type successAgentMsg struct{}
   170  
   171  // See [PROTOCOL.agent], section 2.5.2.
   172  const agentRequestIdentities = 11
   173  
   174  type requestIdentitiesAgentMsg struct{}
   175  
   176  // See [PROTOCOL.agent], section 2.5.2.
   177  const agentIdentitiesAnswer = 12
   178  
   179  type identitiesAnswerAgentMsg struct {
   180  	NumKeys uint32 `sshtype:"12"`
   181  	Keys    []byte `ssh:"rest"`
   182  }
   183  
   184  // See [PROTOCOL.agent], section 2.6.2.
   185  const agentSignRequest = 13
   186  
   187  type 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
   196  const agentSignResponse = 14
   197  
   198  type signResponseAgentMsg struct {
   199  	SigBlob []byte `sshtype:"14"`
   200  }
   201  
   202  type publicKey struct {
   203  	Format string
   204  	Rest   []byte `ssh:"rest"`
   205  }
   206  
   207  // 3.7 Key constraint identifiers
   208  type constrainLifetimeAgentMsg struct {
   209  	LifetimeSecs uint32 `sshtype:"1"`
   210  }
   211  
   212  type 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
   221  const agentExtension = 27
   222  const 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.
   230  var ErrExtensionUnsupported = errors.New("agent: extension unsupported")
   231  
   232  type 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.
   241  type Key struct {
   242  	Format  string
   243  	Blob    []byte
   244  	Comment string
   245  }
   246  
   247  func 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.
   253  func (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.
   264  func (k *Key) Type() string {
   265  	return k.Format
   266  }
   267  
   268  // Marshal returns key blob to satisfy the ssh.PublicKey interface.
   269  func (k *Key) Marshal() []byte {
   270  	return k.Blob
   271  }
   272  
   273  // Verify satisfies the ssh.PublicKey interface.
   274  func (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  
   282  type wireKey struct {
   283  	Format string
   284  	Rest   []byte `ssh:"rest"`
   285  }
   286  
   287  func 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.
   311  type 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.
   320  func 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.
   327  func (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.
   342  func (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  
   369  func (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  
   380  func (c *client) RemoveAll() error {
   381  	return c.simpleCall([]byte{agentRemoveAllIdentities})
   382  }
   383  
   384  func (c *client) Remove(key ssh.PublicKey) error {
   385  	req := ssh.Marshal(&agentRemoveIdentityMsg{
   386  		KeyBlob: key.Marshal(),
   387  	})
   388  	return c.simpleCall(req)
   389  }
   390  
   391  func (c *client) Lock(passphrase []byte) error {
   392  	req := ssh.Marshal(&agentLockMsg{
   393  		Passphrase: passphrase,
   394  	})
   395  	return c.simpleCall(req)
   396  }
   397  
   398  func (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.
   406  func (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.
   439  func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
   440  	return c.SignWithFlags(key, data, 0)
   441  }
   442  
   443  func (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.
   471  func 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  
   496  type 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  
   508  type 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  
   519  type 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  
   528  type 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.
   537  func (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  
   615  type 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  
   626  type 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  
   634  type 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  
   642  type 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.
   653  func (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  
   671  func (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.
   754  func (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  
   767  type agentKeyringSigner struct {
   768  	agent *client
   769  	pub   ssh.PublicKey
   770  }
   771  
   772  func (s *agentKeyringSigner) PublicKey() ssh.PublicKey {
   773  	return s.pub
   774  }
   775  
   776  func (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  
   781  func (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  
   799  var _ 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.
   805  var 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.
   821  func 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.
   832  func (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  }
   855  

View as plain text