| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package knownhosts |
| |
| import ( |
| "bytes" |
| "fmt" |
| "net" |
| "reflect" |
| "testing" |
| |
| "golang.org/x/crypto/ssh" |
| ) |
| |
| const edKeyStr = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGBAarftlLeoyf+v+nVchEZII/vna2PCV8FaX4vsF5BX" |
| const alternateEdKeyStr = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIXffBYeYL+WVzVru8npl5JHt2cjlr4ornFTWzoij9sx" |
| const ecKeyStr = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNLCu01+wpXe3xB5olXCN4SqU2rQu0qjSRKJO4Bg+JRCPU+ENcgdA5srTU8xYDz/GEa4dzK5ldPw4J/gZgSXCMs=" |
| |
| var ecKey, alternateEdKey, edKey ssh.PublicKey |
| var testAddr = &net.TCPAddr{ |
| IP: net.IP{198, 41, 30, 196}, |
| Port: 22, |
| } |
| |
| var testAddr6 = &net.TCPAddr{ |
| IP: net.IP{198, 41, 30, 196, |
| 1, 2, 3, 4, |
| 1, 2, 3, 4, |
| 1, 2, 3, 4, |
| }, |
| Port: 22, |
| } |
| |
| func init() { |
| var err error |
| ecKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(ecKeyStr)) |
| if err != nil { |
| panic(err) |
| } |
| edKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(edKeyStr)) |
| if err != nil { |
| panic(err) |
| } |
| alternateEdKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(alternateEdKeyStr)) |
| if err != nil { |
| panic(err) |
| } |
| } |
| |
| func testDB(t *testing.T, s string) *hostKeyDB { |
| db := newHostKeyDB() |
| if err := db.Read(bytes.NewBufferString(s), "testdb"); err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| |
| return db |
| } |
| |
| func TestRevoked(t *testing.T) { |
| db := testDB(t, "\n\n@revoked * "+edKeyStr+"\n") |
| want := &RevokedError{ |
| Revoked: KnownKey{ |
| Key: edKey, |
| Filename: "testdb", |
| Line: 3, |
| }, |
| } |
| if err := db.check("", &net.TCPAddr{ |
| Port: 42, |
| }, edKey); err == nil { |
| t.Fatal("no error for revoked key") |
| } else if !reflect.DeepEqual(want, err) { |
| t.Fatalf("got %#v, want %#v", want, err) |
| } |
| } |
| |
| func TestBracket(t *testing.T) { |
| db := testDB(t, `[git.eclipse.org]:29418,[198.41.30.196]:29418 `+edKeyStr) |
| |
| if err := db.check("git.eclipse.org:29418", &net.TCPAddr{ |
| IP: net.IP{198, 41, 30, 196}, |
| Port: 29418, |
| }, edKey); err != nil { |
| t.Errorf("got error %v, want none", err) |
| } |
| |
| if err := db.check("git.eclipse.org:29419", &net.TCPAddr{ |
| Port: 42, |
| }, edKey); err == nil { |
| t.Fatalf("no error for unknown address") |
| } else if ke, ok := err.(*KeyError); !ok { |
| t.Fatalf("got type %T, want *KeyError", err) |
| } else if len(ke.Want) > 0 { |
| t.Fatalf("got Want %v, want []", ke.Want) |
| } |
| } |
| |
| func TestNewKeyType(t *testing.T) { |
| str := fmt.Sprintf("%s %s", testAddr, edKeyStr) |
| db := testDB(t, str) |
| if err := db.check("", testAddr, ecKey); err == nil { |
| t.Fatalf("no error for unknown address") |
| } else if ke, ok := err.(*KeyError); !ok { |
| t.Fatalf("got type %T, want *KeyError", err) |
| } else if len(ke.Want) == 0 { |
| t.Fatalf("got empty KeyError.Want") |
| } |
| } |
| |
| func TestSameKeyType(t *testing.T) { |
| str := fmt.Sprintf("%s %s", testAddr, edKeyStr) |
| db := testDB(t, str) |
| if err := db.check("", testAddr, alternateEdKey); err == nil { |
| t.Fatalf("no error for unknown address") |
| } else if ke, ok := err.(*KeyError); !ok { |
| t.Fatalf("got type %T, want *KeyError", err) |
| } else if len(ke.Want) == 0 { |
| t.Fatalf("got empty KeyError.Want") |
| } else if got, want := ke.Want[0].Key.Marshal(), edKey.Marshal(); !bytes.Equal(got, want) { |
| t.Fatalf("got key %q, want %q", got, want) |
| } |
| } |
| |
| func TestIPAddress(t *testing.T) { |
| str := fmt.Sprintf("%s %s", testAddr, edKeyStr) |
| db := testDB(t, str) |
| if err := db.check("", testAddr, edKey); err != nil { |
| t.Errorf("got error %q, want none", err) |
| } |
| } |
| |
| func TestIPv6Address(t *testing.T) { |
| str := fmt.Sprintf("%s %s", testAddr6, edKeyStr) |
| db := testDB(t, str) |
| |
| if err := db.check("", testAddr6, edKey); err != nil { |
| t.Errorf("got error %q, want none", err) |
| } |
| } |
| |
| func TestBasic(t *testing.T) { |
| str := fmt.Sprintf("#comment\n\nserver.org,%s %s\notherhost %s", testAddr, edKeyStr, ecKeyStr) |
| db := testDB(t, str) |
| if err := db.check("server.org:22", testAddr, edKey); err != nil { |
| t.Errorf("got error %q, want none", err) |
| } |
| |
| want := KnownKey{ |
| Key: edKey, |
| Filename: "testdb", |
| Line: 3, |
| } |
| if err := db.check("server.org:22", testAddr, ecKey); err == nil { |
| t.Errorf("succeeded, want KeyError") |
| } else if ke, ok := err.(*KeyError); !ok { |
| t.Errorf("got %T, want *KeyError", err) |
| } else if len(ke.Want) != 1 { |
| t.Errorf("got %v, want 1 entry", ke) |
| } else if !reflect.DeepEqual(ke.Want[0], want) { |
| t.Errorf("got %v, want %v", ke.Want[0], want) |
| } |
| } |
| |
| func TestNegate(t *testing.T) { |
| str := fmt.Sprintf("%s,!server.org %s", testAddr, edKeyStr) |
| db := testDB(t, str) |
| if err := db.check("server.org:22", testAddr, ecKey); err == nil { |
| t.Errorf("succeeded") |
| } else if ke, ok := err.(*KeyError); !ok { |
| t.Errorf("got error type %T, want *KeyError", err) |
| } else if len(ke.Want) != 0 { |
| t.Errorf("got expected keys %d (first of type %s), want []", len(ke.Want), ke.Want[0].Key.Type()) |
| } |
| } |
| |
| func TestWildcard(t *testing.T) { |
| str := fmt.Sprintf("server*.domain %s", edKeyStr) |
| db := testDB(t, str) |
| |
| want := &KeyError{ |
| Want: []KnownKey{{ |
| Filename: "testdb", |
| Line: 1, |
| Key: edKey, |
| }}, |
| } |
| |
| got := db.check("server.domain:22", &net.TCPAddr{}, ecKey) |
| if !reflect.DeepEqual(got, want) { |
| t.Errorf("got %s, want %s", got, want) |
| } |
| } |
| |
| func TestLine(t *testing.T) { |
| for in, want := range map[string]string{ |
| "server.org": "server.org " + edKeyStr, |
| "server.org:22": "server.org " + edKeyStr, |
| "server.org:23": "[server.org]:23 " + edKeyStr, |
| "[c629:1ec4:102:304:102:304:102:304]:22": "[c629:1ec4:102:304:102:304:102:304] " + edKeyStr, |
| "[c629:1ec4:102:304:102:304:102:304]:23": "[c629:1ec4:102:304:102:304:102:304]:23 " + edKeyStr, |
| } { |
| if got := Line([]string{in}, edKey); got != want { |
| t.Errorf("Line(%q) = %q, want %q", in, got, want) |
| } |
| } |
| } |
| |
| func TestWildcardMatch(t *testing.T) { |
| for _, c := range []struct { |
| pat, str string |
| want bool |
| }{ |
| {"a?b", "abb", true}, |
| {"ab", "abc", false}, |
| {"abc", "ab", false}, |
| {"a*b", "axxxb", true}, |
| {"a*b", "axbxb", true}, |
| {"a*b", "axbxbc", false}, |
| {"a*?", "axbxc", true}, |
| {"a*b*", "axxbxxxxxx", true}, |
| {"a*b*c", "axxbxxxxxxc", true}, |
| {"a*b*?", "axxbxxxxxxc", true}, |
| {"a*b*z", "axxbxxbxxxz", true}, |
| {"a*b*z", "axxbxxzxxxz", true}, |
| {"a*b*z", "axxbxxzxxx", false}, |
| } { |
| got := wildcardMatch([]byte(c.pat), []byte(c.str)) |
| if got != c.want { |
| t.Errorf("wildcardMatch(%q, %q) = %v, want %v", c.pat, c.str, got, c.want) |
| } |
| |
| } |
| } |
| |
| // TODO(hanwen): test coverage for certificates. |