ssh: support SSH agent signature flags and custom extensions
This commit implements two new features. To preserve backwards compatibility the new methods are added to an `ExtendedAgent` interface which extends `Agent`. The client code implements `ExtendedAgent` (which extends Agent) so you can call these additional methods against SSH agents such as the OpenSSH agent. The ServeAgent method still accepts Agent but will attempt to upcast the agent to `ExtendedAgent` as needed, so if you supply an ExtendedAgent implementation you can implement these additional methods (which keyring does). The first feature is supporting the standard flags that can be passed to SSH Sign requests, requesting that RSA signatures use SHA-256 or SHA-512. See section 4.5.1 of the SSH agent protocol draft: https://tools.ietf.org/html/draft-miller-ssh-agent-02 The second feature is supporting calling custom extensions from clients and implementing custom extensions from servers. See section 4.7 of the SSH agent protocol draft: https://tools.ietf.org/html/draft-miller-ssh-agent-02 Change-Id: I0f74feb893762c27e921ec37604d3a46434ee6ef GitHub-Last-Rev: 2e23fd01c0e95b664e8507682f0bd5bd61d4c146 GitHub-Pull-Request: golang/crypto#53 Reviewed-on: https://go-review.googlesource.com/c/123955 Reviewed-by: Han-Wen Nienhuys <hanwen@google.com> Run-TryBot: Han-Wen Nienhuys <hanwen@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
committed by
Han-Wen Nienhuys
parent
45a5f77698
commit
dab2b1051b
@@ -20,7 +20,7 @@ import (
|
||||
)
|
||||
|
||||
// startOpenSSHAgent executes ssh-agent, and returns an Agent interface to it.
|
||||
func startOpenSSHAgent(t *testing.T) (client Agent, socket string, cleanup func()) {
|
||||
func startOpenSSHAgent(t *testing.T) (client ExtendedAgent, socket string, cleanup func()) {
|
||||
if testing.Short() {
|
||||
// ssh-agent is not always available, and the key
|
||||
// types supported vary by platform.
|
||||
@@ -79,13 +79,12 @@ func startOpenSSHAgent(t *testing.T) (client Agent, socket string, cleanup func(
|
||||
}
|
||||
}
|
||||
|
||||
// startKeyringAgent uses Keyring to simulate a ssh-agent Server and returns a client.
|
||||
func startKeyringAgent(t *testing.T) (client Agent, cleanup func()) {
|
||||
func startAgent(t *testing.T, agent Agent) (client ExtendedAgent, cleanup func()) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
go ServeAgent(NewKeyring(), c2)
|
||||
go ServeAgent(agent, c2)
|
||||
|
||||
return NewClient(c1), func() {
|
||||
c1.Close()
|
||||
@@ -93,6 +92,11 @@ func startKeyringAgent(t *testing.T) (client Agent, cleanup func()) {
|
||||
}
|
||||
}
|
||||
|
||||
// startKeyringAgent uses Keyring to simulate a ssh-agent Server and returns a client.
|
||||
func startKeyringAgent(t *testing.T) (client ExtendedAgent, cleanup func()) {
|
||||
return startAgent(t, NewKeyring())
|
||||
}
|
||||
|
||||
func testOpenSSHAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
agent, _, cleanup := startOpenSSHAgent(t)
|
||||
defer cleanup()
|
||||
@@ -107,7 +111,7 @@ func testKeyringAgent(t *testing.T, key interface{}, cert *ssh.Certificate, life
|
||||
testAgentInterface(t, agent, key, cert, lifetimeSecs)
|
||||
}
|
||||
|
||||
func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
func testAgentInterface(t *testing.T, agent ExtendedAgent, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
signer, err := ssh.NewSignerFromKey(key)
|
||||
if err != nil {
|
||||
t.Fatalf("NewSignerFromKey(%T): %v", key, err)
|
||||
@@ -159,6 +163,25 @@ func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Ce
|
||||
t.Fatalf("Verify(%s): %v", pubKey.Type(), err)
|
||||
}
|
||||
|
||||
// For tests on RSA keys, try signing with SHA-256 and SHA-512 flags
|
||||
if pubKey.Type() == "ssh-rsa" {
|
||||
sshFlagTest := func(flag SignatureFlags, expectedSigFormat string) {
|
||||
sig, err = agent.SignWithFlags(pubKey, data, flag)
|
||||
if err != nil {
|
||||
t.Fatalf("SignWithFlags(%s): %v", pubKey.Type(), err)
|
||||
}
|
||||
if sig.Format != expectedSigFormat {
|
||||
t.Fatalf("Signature format didn't match expected value: %s != %s", sig.Format, expectedSigFormat)
|
||||
}
|
||||
if err := pubKey.Verify(data, sig); err != nil {
|
||||
t.Fatalf("Verify(%s): %v", pubKey.Type(), err)
|
||||
}
|
||||
}
|
||||
sshFlagTest(0, ssh.SigAlgoRSA)
|
||||
sshFlagTest(SignatureFlagRsaSha256, ssh.SigAlgoRSASHA2256)
|
||||
sshFlagTest(SignatureFlagRsaSha512, ssh.SigAlgoRSASHA2512)
|
||||
}
|
||||
|
||||
// If the key has a lifetime, is it removed when it should be?
|
||||
if lifetimeSecs > 0 {
|
||||
time.Sleep(time.Second*time.Duration(lifetimeSecs) + 100*time.Millisecond)
|
||||
@@ -377,3 +400,38 @@ func testAgentLifetime(t *testing.T, agent Agent) {
|
||||
t.Errorf("Want 0 keys, got %v", len(keys))
|
||||
}
|
||||
}
|
||||
|
||||
type keyringExtended struct {
|
||||
*keyring
|
||||
}
|
||||
|
||||
func (r *keyringExtended) Extension(extensionType string, contents []byte) ([]byte, error) {
|
||||
if extensionType != "my-extension@example.com" {
|
||||
return []byte{agentExtensionFailure}, nil
|
||||
}
|
||||
return append([]byte{agentSuccess}, contents...), nil
|
||||
}
|
||||
|
||||
func TestAgentExtensions(t *testing.T) {
|
||||
agent, _, cleanup := startOpenSSHAgent(t)
|
||||
defer cleanup()
|
||||
result, err := agent.Extension("my-extension@example.com", []byte{0x00, 0x01, 0x02})
|
||||
if err == nil {
|
||||
t.Fatal("should have gotten agent extension failure")
|
||||
}
|
||||
|
||||
agent, cleanup = startAgent(t, &keyringExtended{})
|
||||
defer cleanup()
|
||||
result, err = agent.Extension("my-extension@example.com", []byte{0x00, 0x01, 0x02})
|
||||
if err != nil {
|
||||
t.Fatalf("agent extension failure: %v", err)
|
||||
}
|
||||
if len(result) != 4 || !bytes.Equal(result, []byte{agentSuccess, 0x00, 0x01, 0x02}) {
|
||||
t.Fatalf("agent extension result invalid: %v", result)
|
||||
}
|
||||
|
||||
result, err = agent.Extension("bad-extension@example.com", []byte{0x00, 0x01, 0x02})
|
||||
if err == nil {
|
||||
t.Fatal("should have gotten agent extension failure")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user