-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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: 2e23fd0 GitHub-Pull-Request: #53 Reviewed-on: https://go-review.googlesource.com/c/123955 Reviewed-by: Han-Wen Nienhuys <[email protected]> Run-TryBot: Han-Wen Nienhuys <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
- Loading branch information
1 parent
45a5f77
commit dab2b10
Showing
7 changed files
with
382 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,20 +79,24 @@ 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() | ||
c2.Close() | ||
} | ||
} | ||
|
||
// 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 != "[email protected]" { | ||
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("[email protected]", []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("[email protected]", []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("[email protected]", []byte{0x00, 0x01, 0x02}) | ||
if err == nil { | ||
t.Fatal("should have gotten agent extension failure") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.