1 // Copyright 2014 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.
21 "golang.org/x/crypto/ed25519"
22 "golang.org/x/crypto/ssh/testdata"
25 func rawKey(pub PublicKey) interface{} {
26 switch k := pub.(type) {
28 return (*rsa.PublicKey)(k)
30 return (*dsa.PublicKey)(k)
32 return (*ecdsa.PublicKey)(k)
33 case ed25519PublicKey:
34 return (ed25519.PublicKey)(k)
38 panic("unknown key type")
41 func TestKeyMarshalParse(t *testing.T) {
42 for _, priv := range testSigners {
43 pub := priv.PublicKey()
44 roundtrip, err := ParsePublicKey(pub.Marshal())
46 t.Errorf("ParsePublicKey(%T): %v", pub, err)
50 k2 := rawKey(roundtrip)
52 if !reflect.DeepEqual(k1, k2) {
53 t.Errorf("got %#v in roundtrip, want %#v", k2, k1)
58 func TestUnsupportedCurves(t *testing.T) {
59 raw, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
61 t.Fatalf("GenerateKey: %v", err)
64 if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P-256") {
65 t.Fatalf("NewPrivateKey should not succeed with P-224, got: %v", err)
68 if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P-256") {
69 t.Fatalf("NewPublicKey should not succeed with P-224, got: %v", err)
73 func TestNewPublicKey(t *testing.T) {
74 for _, k := range testSigners {
75 raw := rawKey(k.PublicKey())
76 // Skip certificates, as NewPublicKey does not support them.
77 if _, ok := raw.(*Certificate); ok {
80 pub, err := NewPublicKey(raw)
82 t.Errorf("NewPublicKey(%#v): %v", raw, err)
84 if !reflect.DeepEqual(k.PublicKey(), pub) {
85 t.Errorf("NewPublicKey(%#v) = %#v, want %#v", raw, pub, k.PublicKey())
90 func TestKeySignVerify(t *testing.T) {
91 for _, priv := range testSigners {
92 pub := priv.PublicKey()
94 data := []byte("sign me")
95 sig, err := priv.Sign(rand.Reader, data)
97 t.Fatalf("Sign(%T): %v", priv, err)
100 if err := pub.Verify(data, sig); err != nil {
101 t.Errorf("publicKey.Verify(%T): %v", priv, err)
104 if err := pub.Verify(data, sig); err == nil {
105 t.Errorf("publicKey.Verify on broken sig did not fail")
110 func TestParseRSAPrivateKey(t *testing.T) {
111 key := testPrivateKeys["rsa"]
113 rsa, ok := key.(*rsa.PrivateKey)
115 t.Fatalf("got %T, want *rsa.PrivateKey", rsa)
118 if err := rsa.Validate(); err != nil {
119 t.Errorf("Validate: %v", err)
123 func TestParseECPrivateKey(t *testing.T) {
124 key := testPrivateKeys["ecdsa"]
126 ecKey, ok := key.(*ecdsa.PrivateKey)
128 t.Fatalf("got %T, want *ecdsa.PrivateKey", ecKey)
131 if !validateECPublicKey(ecKey.Curve, ecKey.X, ecKey.Y) {
132 t.Fatalf("public key does not validate.")
136 // See Issue https://github.com/golang/go/issues/6650.
137 func TestParseEncryptedPrivateKeysFails(t *testing.T) {
138 const wantSubstring = "encrypted"
139 for i, tt := range testdata.PEMEncryptedKeys {
140 _, err := ParsePrivateKey(tt.PEMBytes)
142 t.Errorf("#%d key %s: ParsePrivateKey successfully parsed, expected an error", i, tt.Name)
146 if !strings.Contains(err.Error(), wantSubstring) {
147 t.Errorf("#%d key %s: got error %q, want substring %q", i, tt.Name, err, wantSubstring)
152 // Parse encrypted private keys with passphrase
153 func TestParseEncryptedPrivateKeysWithPassphrase(t *testing.T) {
154 data := []byte("sign me")
155 for _, tt := range testdata.PEMEncryptedKeys {
156 s, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte(tt.EncryptionKey))
158 t.Fatalf("ParsePrivateKeyWithPassphrase returned error: %s", err)
161 sig, err := s.Sign(rand.Reader, data)
163 t.Fatalf("dsa.Sign: %v", err)
165 if err := s.PublicKey().Verify(data, sig); err != nil {
166 t.Errorf("Verify failed: %v", err)
170 tt := testdata.PEMEncryptedKeys[0]
171 _, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte("incorrect"))
172 if err != x509.IncorrectPasswordError {
173 t.Fatalf("got %v want IncorrectPasswordError", err)
177 func TestParseDSA(t *testing.T) {
178 // We actually exercise the ParsePrivateKey codepath here, as opposed to
179 // using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go
181 s, err := ParsePrivateKey(testdata.PEMBytes["dsa"])
183 t.Fatalf("ParsePrivateKey returned error: %s", err)
186 data := []byte("sign me")
187 sig, err := s.Sign(rand.Reader, data)
189 t.Fatalf("dsa.Sign: %v", err)
192 if err := s.PublicKey().Verify(data, sig); err != nil {
193 t.Errorf("Verify failed: %v", err)
197 // Tests for authorized_keys parsing.
199 // getTestKey returns a public key, and its base64 encoding.
200 func getTestKey() (PublicKey, string) {
201 k := testPublicKeys["rsa"]
204 e := base64.NewEncoder(base64.StdEncoding, b)
211 func TestMarshalParsePublicKey(t *testing.T) {
212 pub, pubSerialized := getTestKey()
213 line := fmt.Sprintf("%s %s user@host", pub.Type(), pubSerialized)
215 authKeys := MarshalAuthorizedKey(pub)
216 actualFields := strings.Fields(string(authKeys))
217 if len(actualFields) == 0 {
218 t.Fatalf("failed authKeys: %v", authKeys)
222 expectedFields := strings.Fields(line)[0:2]
224 if !reflect.DeepEqual(actualFields, expectedFields) {
225 t.Errorf("got %v, expected %v", actualFields, expectedFields)
228 actPub, _, _, _, err := ParseAuthorizedKey([]byte(line))
230 t.Fatalf("cannot parse %v: %v", line, err)
232 if !reflect.DeepEqual(actPub, pub) {
233 t.Errorf("got %v, expected %v", actPub, pub)
237 type authResult struct {
245 func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []authResult) {
247 var values []authResult
251 r.pubKey, r.comments, r.options, rest, err = ParseAuthorizedKey(rest)
254 r.rest = string(rest)
255 values = append(values, r)
258 if !reflect.DeepEqual(values, expected) {
259 t.Errorf("got %#v, expected %#v", values, expected)
263 func TestAuthorizedKeyBasic(t *testing.T) {
264 pub, pubSerialized := getTestKey()
265 line := "ssh-rsa " + pubSerialized + " user@host"
266 testAuthorizedKeys(t, []byte(line),
268 {pub, nil, "user@host", "", true},
272 func TestAuth(t *testing.T) {
273 pub, pubSerialized := getTestKey()
274 authWithOptions := []string{
275 `# comments to ignore before any keys...`,
277 `env="HOME=/home/root",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`,
278 `# comments to ignore, along with a blank line`,
280 `env="HOME=/home/root2" ssh-rsa ` + pubSerialized + ` user2@host2`,
282 `# more comments, plus a invalid entry`,
283 `ssh-rsa data-that-will-not-parse user@host3`,
285 for _, eol := range []string{"\n", "\r\n"} {
286 authOptions := strings.Join(authWithOptions, eol)
287 rest2 := strings.Join(authWithOptions[3:], eol)
288 rest3 := strings.Join(authWithOptions[6:], eol)
289 testAuthorizedKeys(t, []byte(authOptions), []authResult{
290 {pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true},
291 {pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true},
292 {nil, nil, "", "", false},
297 func TestAuthWithQuotedSpaceInEnv(t *testing.T) {
298 pub, pubSerialized := getTestKey()
299 authWithQuotedSpaceInEnv := []byte(`env="HOME=/home/root dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`)
300 testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []authResult{
301 {pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"}, "user@host", "", true},
305 func TestAuthWithQuotedCommaInEnv(t *testing.T) {
306 pub, pubSerialized := getTestKey()
307 authWithQuotedCommaInEnv := []byte(`env="HOME=/home/root,dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`)
308 testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []authResult{
309 {pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"}, "user@host", "", true},
313 func TestAuthWithQuotedQuoteInEnv(t *testing.T) {
314 pub, pubSerialized := getTestKey()
315 authWithQuotedQuoteInEnv := []byte(`env="HOME=/home/\"root dir",no-port-forwarding` + "\t" + `ssh-rsa` + "\t" + pubSerialized + ` user@host`)
316 authWithDoubleQuotedQuote := []byte(`no-port-forwarding,env="HOME=/home/ \"root dir\"" ssh-rsa ` + pubSerialized + "\t" + `user@host`)
317 testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []authResult{
318 {pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true},
321 testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []authResult{
322 {pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root dir\""`}, "user@host", "", true},
326 func TestAuthWithInvalidSpace(t *testing.T) {
327 _, pubSerialized := getTestKey()
328 authWithInvalidSpace := []byte(`env="HOME=/home/root dir", no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host
329 #more to follow but still no valid keys`)
330 testAuthorizedKeys(t, []byte(authWithInvalidSpace), []authResult{
331 {nil, nil, "", "", false},
335 func TestAuthWithMissingQuote(t *testing.T) {
336 pub, pubSerialized := getTestKey()
337 authWithMissingQuote := []byte(`env="HOME=/home/root,no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host
338 env="HOME=/home/root",shared-control ssh-rsa ` + pubSerialized + ` user@host`)
340 testAuthorizedKeys(t, []byte(authWithMissingQuote), []authResult{
341 {pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user@host", "", true},
345 func TestInvalidEntry(t *testing.T) {
346 authInvalid := []byte(`ssh-rsa`)
347 _, _, _, _, err := ParseAuthorizedKey(authInvalid)
349 t.Errorf("got valid entry for %q", authInvalid)
353 var knownHostsParseTests = []struct {
381 "localhost ssh-rsa {RSAPUB}",
384 "", "", []string{"localhost"}, "",
387 "localhost\tssh-rsa {RSAPUB}",
390 "", "", []string{"localhost"}, "",
393 "localhost\tssh-rsa {RSAPUB}\tcomment comment",
396 "", "comment comment", []string{"localhost"}, "",
399 "localhost\tssh-rsa {RSAPUB}\tcomment comment\n",
402 "", "comment comment", []string{"localhost"}, "",
405 "localhost\tssh-rsa {RSAPUB}\tcomment comment\r\n",
408 "", "comment comment", []string{"localhost"}, "",
411 "localhost\tssh-rsa {RSAPUB}\tcomment comment\r\nnext line",
414 "", "comment comment", []string{"localhost"}, "next line",
417 "localhost,[host2:123]\tssh-rsa {RSAPUB}\tcomment comment",
420 "", "comment comment", []string{"localhost", "[host2:123]"}, "",
423 "@marker \tlocalhost,[host2:123]\tssh-rsa {RSAPUB}",
426 "marker", "", []string{"localhost", "[host2:123]"}, "",
429 "@marker \tlocalhost,[host2:123]\tssh-rsa aabbccdd",
436 func TestKnownHostsParsing(t *testing.T) {
437 rsaPub, rsaPubSerialized := getTestKey()
439 for i, test := range knownHostsParseTests {
440 var expectedKey PublicKey
441 const rsaKeyToken = "{RSAPUB}"
444 if strings.Contains(input, rsaKeyToken) {
446 input = strings.Replace(test.input, rsaKeyToken, rsaPubSerialized, -1)
449 marker, hosts, pubKey, comment, rest, err := ParseKnownHosts([]byte(input))
451 if len(test.err) == 0 {
452 t.Errorf("#%d: unexpectedly failed with %q", i, err)
453 } else if !strings.Contains(err.Error(), test.err) {
454 t.Errorf("#%d: expected error containing %q, but got %q", i, test.err, err)
457 } else if len(test.err) != 0 {
458 t.Errorf("#%d: succeeded but expected error including %q", i, test.err)
462 if !reflect.DeepEqual(expectedKey, pubKey) {
463 t.Errorf("#%d: expected key %#v, but got %#v", i, expectedKey, pubKey)
466 if marker != test.marker {
467 t.Errorf("#%d: expected marker %q, but got %q", i, test.marker, marker)
470 if comment != test.comment {
471 t.Errorf("#%d: expected comment %q, but got %q", i, test.comment, comment)
474 if !reflect.DeepEqual(test.hosts, hosts) {
475 t.Errorf("#%d: expected hosts %#v, but got %#v", i, test.hosts, hosts)
478 if rest := string(rest); rest != test.rest {
479 t.Errorf("#%d: expected remaining input to be %q, but got %q", i, test.rest, rest)
484 func TestFingerprintLegacyMD5(t *testing.T) {
485 pub, _ := getTestKey()
486 fingerprint := FingerprintLegacyMD5(pub)
487 want := "fb:61:6d:1a:e3:f0:95:45:3c:a0:79:be:4a:93:63:66" // ssh-keygen -lf -E md5 rsa
488 if fingerprint != want {
489 t.Errorf("got fingerprint %q want %q", fingerprint, want)
493 func TestFingerprintSHA256(t *testing.T) {
494 pub, _ := getTestKey()
495 fingerprint := FingerprintSHA256(pub)
496 want := "SHA256:Anr3LjZK8YVpjrxu79myrW9Hrb/wpcMNpVvTq/RcBm8" // ssh-keygen -lf rsa
497 if fingerprint != want {
498 t.Errorf("got fingerprint %q want %q", fingerprint, want)