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.
19 "golang.org/x/crypto/ssh"
22 // startAgent executes ssh-agent, and returns a Agent interface to it.
23 func startAgent(t *testing.T) (client Agent, socket string, cleanup func()) {
25 // ssh-agent is not always available, and the key
26 // types supported vary by platform.
27 t.Skip("skipping test due to -short")
30 bin, err := exec.LookPath("ssh-agent")
32 t.Skip("could not find ssh-agent")
35 cmd := exec.Command(bin, "-s")
36 out, err := cmd.Output()
38 t.Fatalf("cmd.Output: %v", err)
43 SSH_AUTH_SOCK=/tmp/ssh-P65gpcqArqvH/agent.15541; export SSH_AUTH_SOCK;
44 SSH_AGENT_PID=15542; export SSH_AGENT_PID;
47 fields := bytes.Split(out, []byte(";"))
48 line := bytes.SplitN(fields[0], []byte("="), 2)
49 line[0] = bytes.TrimLeft(line[0], "\n")
50 if string(line[0]) != "SSH_AUTH_SOCK" {
51 t.Fatalf("could not find key SSH_AUTH_SOCK in %q", fields[0])
53 socket = string(line[1])
55 line = bytes.SplitN(fields[2], []byte("="), 2)
56 line[0] = bytes.TrimLeft(line[0], "\n")
57 if string(line[0]) != "SSH_AGENT_PID" {
58 t.Fatalf("could not find key SSH_AGENT_PID in %q", fields[2])
61 pid, err := strconv.Atoi(string(pidStr))
63 t.Fatalf("Atoi(%q): %v", pidStr, err)
66 conn, err := net.Dial("unix", string(socket))
68 t.Fatalf("net.Dial: %v", err)
72 return ac, socket, func() {
73 proc, _ := os.FindProcess(pid)
78 os.RemoveAll(filepath.Dir(socket))
82 func testAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
83 agent, _, cleanup := startAgent(t)
86 testAgentInterface(t, agent, key, cert, lifetimeSecs)
89 func testKeyring(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
91 testAgentInterface(t, a, key, cert, lifetimeSecs)
94 func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
95 signer, err := ssh.NewSignerFromKey(key)
97 t.Fatalf("NewSignerFromKey(%T): %v", key, err)
99 // The agent should start up empty.
100 if keys, err := agent.List(); err != nil {
101 t.Fatalf("RequestIdentities: %v", err)
102 } else if len(keys) > 0 {
103 t.Fatalf("got %d keys, want 0: %v", len(keys), keys)
106 // Attempt to insert the key, with certificate if specified.
107 var pubKey ssh.PublicKey
109 err = agent.Add(AddedKey{
113 LifetimeSecs: lifetimeSecs,
117 err = agent.Add(AddedKey{PrivateKey: key, Comment: "comment", LifetimeSecs: lifetimeSecs})
118 pubKey = signer.PublicKey()
121 t.Fatalf("insert(%T): %v", key, err)
124 // Did the key get inserted successfully?
125 if keys, err := agent.List(); err != nil {
126 t.Fatalf("List: %v", err)
127 } else if len(keys) != 1 {
128 t.Fatalf("got %v, want 1 key", keys)
129 } else if keys[0].Comment != "comment" {
130 t.Fatalf("key comment: got %v, want %v", keys[0].Comment, "comment")
131 } else if !bytes.Equal(keys[0].Blob, pubKey.Marshal()) {
132 t.Fatalf("key mismatch")
135 // Can the agent make a valid signature?
136 data := []byte("hello")
137 sig, err := agent.Sign(pubKey, data)
139 t.Fatalf("Sign(%s): %v", pubKey.Type(), err)
142 if err := pubKey.Verify(data, sig); err != nil {
143 t.Fatalf("Verify(%s): %v", pubKey.Type(), err)
146 // If the key has a lifetime, is it removed when it should be?
147 if lifetimeSecs > 0 {
148 time.Sleep(time.Second*time.Duration(lifetimeSecs) + 100*time.Millisecond)
149 keys, err := agent.List()
151 t.Fatalf("List: %v", err)
154 t.Fatalf("key not expired")
160 func TestAgent(t *testing.T) {
161 for _, keyType := range []string{"rsa", "dsa", "ecdsa", "ed25519"} {
162 testAgent(t, testPrivateKeys[keyType], nil, 0)
163 testKeyring(t, testPrivateKeys[keyType], nil, 1)
167 func TestCert(t *testing.T) {
168 cert := &ssh.Certificate{
169 Key: testPublicKeys["rsa"],
170 ValidBefore: ssh.CertTimeInfinity,
171 CertType: ssh.UserCert,
173 cert.SignCert(rand.Reader, testSigners["ecdsa"])
175 testAgent(t, testPrivateKeys["rsa"], cert, 0)
176 testKeyring(t, testPrivateKeys["rsa"], cert, 1)
179 // netPipe is analogous to net.Pipe, but it uses a real net.Conn, and
180 // therefore is buffered (net.Pipe deadlocks if both sides start with
182 func netPipe() (net.Conn, net.Conn, error) {
183 listener, err := net.Listen("tcp", "127.0.0.1:0")
185 listener, err = net.Listen("tcp", "[::1]:0")
190 defer listener.Close()
191 c1, err := net.Dial("tcp", listener.Addr().String())
196 c2, err := listener.Accept()
205 func TestAuth(t *testing.T) {
206 agent, _, cleanup := startAgent(t)
209 a, b, err := netPipe()
211 t.Fatalf("netPipe: %v", err)
217 if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment"}); err != nil {
218 t.Errorf("Add: %v", err)
221 serverConf := ssh.ServerConfig{}
222 serverConf.AddHostKey(testSigners["rsa"])
223 serverConf.PublicKeyCallback = func(c ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
224 if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
228 return nil, errors.New("pubkey rejected")
232 conn, _, _, err := ssh.NewServerConn(a, &serverConf)
234 t.Fatalf("Server: %v", err)
239 conf := ssh.ClientConfig{
240 HostKeyCallback: ssh.InsecureIgnoreHostKey(),
242 conf.Auth = append(conf.Auth, ssh.PublicKeysCallback(agent.Signers))
243 conn, _, _, err := ssh.NewClientConn(b, "", &conf)
245 t.Fatalf("NewClientConn: %v", err)
250 func TestLockClient(t *testing.T) {
251 agent, _, cleanup := startAgent(t)
253 testLockAgent(agent, t)
256 func testLockAgent(agent Agent, t *testing.T) {
257 if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment 1"}); err != nil {
258 t.Errorf("Add: %v", err)
260 if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["dsa"], Comment: "comment dsa"}); err != nil {
261 t.Errorf("Add: %v", err)
263 if keys, err := agent.List(); err != nil {
264 t.Errorf("List: %v", err)
265 } else if len(keys) != 2 {
266 t.Errorf("Want 2 keys, got %v", keys)
269 passphrase := []byte("secret")
270 if err := agent.Lock(passphrase); err != nil {
271 t.Errorf("Lock: %v", err)
274 if keys, err := agent.List(); err != nil {
275 t.Errorf("List: %v", err)
276 } else if len(keys) != 0 {
277 t.Errorf("Want 0 keys, got %v", keys)
280 signer, _ := ssh.NewSignerFromKey(testPrivateKeys["rsa"])
281 if _, err := agent.Sign(signer.PublicKey(), []byte("hello")); err == nil {
282 t.Fatalf("Sign did not fail")
285 if err := agent.Remove(signer.PublicKey()); err == nil {
286 t.Fatalf("Remove did not fail")
289 if err := agent.RemoveAll(); err == nil {
290 t.Fatalf("RemoveAll did not fail")
293 if err := agent.Unlock(nil); err == nil {
294 t.Errorf("Unlock with wrong passphrase succeeded")
296 if err := agent.Unlock(passphrase); err != nil {
297 t.Errorf("Unlock: %v", err)
300 if err := agent.Remove(signer.PublicKey()); err != nil {
301 t.Fatalf("Remove: %v", err)
304 if keys, err := agent.List(); err != nil {
305 t.Errorf("List: %v", err)
306 } else if len(keys) != 1 {
307 t.Errorf("Want 1 keys, got %v", keys)
311 func TestAgentLifetime(t *testing.T) {
312 agent, _, cleanup := startAgent(t)
315 for _, keyType := range []string{"rsa", "dsa", "ecdsa"} {
316 // Add private keys to the agent.
317 err := agent.Add(AddedKey{
318 PrivateKey: testPrivateKeys[keyType],
323 t.Fatalf("add: %v", err)
325 // Add certs to the agent.
326 cert := &ssh.Certificate{
327 Key: testPublicKeys[keyType],
328 ValidBefore: ssh.CertTimeInfinity,
329 CertType: ssh.UserCert,
331 cert.SignCert(rand.Reader, testSigners[keyType])
332 err = agent.Add(AddedKey{
333 PrivateKey: testPrivateKeys[keyType],
339 t.Fatalf("add: %v", err)
342 time.Sleep(1100 * time.Millisecond)
343 if keys, err := agent.List(); err != nil {
344 t.Errorf("List: %v", err)
345 } else if len(keys) != 0 {
346 t.Errorf("Want 0 keys, got %v", len(keys))