summaryrefslogtreecommitdiffstats
path: root/vendor/golang.org/x/crypto/ssh/agent/client.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/crypto/ssh/agent/client.go')
-rw-r--r--vendor/golang.org/x/crypto/ssh/agent/client.go120
1 files changed, 113 insertions, 7 deletions
diff --git a/vendor/golang.org/x/crypto/ssh/agent/client.go b/vendor/golang.org/x/crypto/ssh/agent/client.go
index b1808dd26..51f740500 100644
--- a/vendor/golang.org/x/crypto/ssh/agent/client.go
+++ b/vendor/golang.org/x/crypto/ssh/agent/client.go
@@ -25,10 +25,22 @@ import (
"math/big"
"sync"
+ "crypto"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/ssh"
)
+// SignatureFlags represent additional flags that can be passed to the signature
+// requests an defined in [PROTOCOL.agent] section 4.5.1.
+type SignatureFlags uint32
+
+// SignatureFlag values as defined in [PROTOCOL.agent] section 5.3.
+const (
+ SignatureFlagReserved SignatureFlags = 1 << iota
+ SignatureFlagRsaSha256
+ SignatureFlagRsaSha512
+)
+
// Agent represents the capabilities of an ssh-agent.
type Agent interface {
// List returns the identities known to the agent.
@@ -57,6 +69,26 @@ type Agent interface {
Signers() ([]ssh.Signer, error)
}
+type ExtendedAgent interface {
+ Agent
+
+ // SignWithFlags signs like Sign, but allows for additional flags to be sent/received
+ SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error)
+
+ // Extension processes a custom extension request. Standard-compliant agents are not
+ // required to support any extensions, but this method allows agents to implement
+ // vendor-specific methods or add experimental features. See [PROTOCOL.agent] section 4.7.
+ // If agent extensions are unsupported entirely this method MUST return an
+ // ErrExtensionUnsupported error. Similarly, if just the specific extensionType in
+ // the request is unsupported by the agent then ErrExtensionUnsupported MUST be
+ // returned.
+ //
+ // In the case of success, since [PROTOCOL.agent] section 4.7 specifies that the contents
+ // of the response are unspecified (including the type of the message), the complete
+ // response will be returned as a []byte slice, including the "type" byte of the message.
+ Extension(extensionType string, contents []byte) ([]byte, error)
+}
+
// ConstraintExtension describes an optional constraint defined by users.
type ConstraintExtension struct {
// ExtensionName consist of a UTF-8 string suffixed by the
@@ -179,6 +211,23 @@ type constrainExtensionAgentMsg struct {
Rest []byte `ssh:"rest"`
}
+// See [PROTOCOL.agent], section 4.7
+const agentExtension = 27
+const agentExtensionFailure = 28
+
+// ErrExtensionUnsupported indicates that an extension defined in
+// [PROTOCOL.agent] section 4.7 is unsupported by the agent. Specifically this
+// error indicates that the agent returned a standard SSH_AGENT_FAILURE message
+// as the result of a SSH_AGENTC_EXTENSION request. Note that the protocol
+// specification (and therefore this error) does not distinguish between a
+// specific extension being unsupported and extensions being unsupported entirely.
+var ErrExtensionUnsupported = errors.New("agent: extension unsupported")
+
+type extensionAgentMsg struct {
+ ExtensionType string `sshtype:"27"`
+ Contents []byte
+}
+
// Key represents a protocol 2 public key as defined in
// [PROTOCOL.agent], section 2.5.2.
type Key struct {
@@ -260,7 +309,7 @@ type client struct {
// NewClient returns an Agent that talks to an ssh-agent process over
// the given connection.
-func NewClient(rw io.ReadWriter) Agent {
+func NewClient(rw io.ReadWriter) ExtendedAgent {
return &client{conn: rw}
}
@@ -268,6 +317,21 @@ func NewClient(rw io.ReadWriter) Agent {
// unmarshaled into reply and replyType is set to the first byte of
// the reply, which contains the type of the message.
func (c *client) call(req []byte) (reply interface{}, err error) {
+ buf, err := c.callRaw(req)
+ if err != nil {
+ return nil, err
+ }
+ reply, err = unmarshal(buf)
+ if err != nil {
+ return nil, clientErr(err)
+ }
+ return reply, nil
+}
+
+// callRaw sends an RPC to the agent. On success, the raw
+// bytes of the response are returned; no unmarshalling is
+// performed on the response.
+func (c *client) callRaw(req []byte) (reply []byte, err error) {
c.mu.Lock()
defer c.mu.Unlock()
@@ -284,18 +348,14 @@ func (c *client) call(req []byte) (reply interface{}, err error) {
}
respSize := binary.BigEndian.Uint32(respSizeBuf[:])
if respSize > maxAgentResponseBytes {
- return nil, clientErr(err)
+ return nil, clientErr(errors.New("response too large"))
}
buf := make([]byte, respSize)
if _, err = io.ReadFull(c.conn, buf); err != nil {
return nil, clientErr(err)
}
- reply, err = unmarshal(buf)
- if err != nil {
- return nil, clientErr(err)
- }
- return reply, err
+ return buf, nil
}
func (c *client) simpleCall(req []byte) error {
@@ -369,9 +429,14 @@ func (c *client) List() ([]*Key, error) {
// Sign has the agent sign the data using a protocol 2 key as defined
// in [PROTOCOL.agent] section 2.6.2.
func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
+ return c.SignWithFlags(key, data, 0)
+}
+
+func (c *client) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) {
req := ssh.Marshal(signRequestAgentMsg{
KeyBlob: key.Marshal(),
Data: data,
+ Flags: uint32(flags),
})
msg, err := c.call(req)
@@ -681,3 +746,44 @@ func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature,
// The agent has its own entropy source, so the rand argument is ignored.
return s.agent.Sign(s.pub, data)
}
+
+func (s *agentKeyringSigner) SignWithOpts(rand io.Reader, data []byte, opts crypto.SignerOpts) (*ssh.Signature, error) {
+ var flags SignatureFlags
+ if opts != nil {
+ switch opts.HashFunc() {
+ case crypto.SHA256:
+ flags = SignatureFlagRsaSha256
+ case crypto.SHA512:
+ flags = SignatureFlagRsaSha512
+ }
+ }
+ return s.agent.SignWithFlags(s.pub, data, flags)
+}
+
+// Calls an extension method. It is up to the agent implementation as to whether or not
+// any particular extension is supported and may always return an error. Because the
+// type of the response is up to the implementation, this returns the bytes of the
+// response and does not attempt any type of unmarshalling.
+func (c *client) Extension(extensionType string, contents []byte) ([]byte, error) {
+ req := ssh.Marshal(extensionAgentMsg{
+ ExtensionType: extensionType,
+ Contents: contents,
+ })
+ buf, err := c.callRaw(req)
+ if err != nil {
+ return nil, err
+ }
+ if len(buf) == 0 {
+ return nil, errors.New("agent: failure; empty response")
+ }
+ // [PROTOCOL.agent] section 4.7 indicates that an SSH_AGENT_FAILURE message
+ // represents an agent that does not support the extension
+ if buf[0] == agentFailure {
+ return nil, ErrExtensionUnsupported
+ }
+ if buf[0] == agentExtensionFailure {
+ return nil, errors.New("agent: generic extension failure")
+ }
+
+ return buf, nil
+}