diff options
| author | Adam Langley <agl@golang.org> | 2014-04-09 13:57:52 -0700 |
|---|---|---|
| committer | Adam Langley <agl@golang.org> | 2014-04-09 13:57:52 -0700 |
| commit | fa50e7408b9ef89ff2965535b59f1a0010c0770b (patch) | |
| tree | e045a3f48f9ffd3bb712002f8f9f6fd489e8f7ef /ssh/keys_test.go | |
| parent | 8f45c680ceb25c200b8c301d9184532aeb7cb36e (diff) | |
| download | go-x-crypto-fa50e7408b9ef89ff2965535b59f1a0010c0770b.tar.xz | |
go.crypto/ssh: import gosshnew.
See https://groups.google.com/d/msg/Golang-nuts/AoVxQ4bB5XQ/i8kpMxdbVlEJ
R=hanwen
CC=golang-codereviews
https://golang.org/cl/86190043
Diffstat (limited to 'ssh/keys_test.go')
| -rw-r--r-- | ssh/keys_test.go | 292 |
1 files changed, 192 insertions, 100 deletions
diff --git a/ssh/keys_test.go b/ssh/keys_test.go index 3c4b735..cd49565 100644 --- a/ssh/keys_test.go +++ b/ssh/keys_test.go @@ -1,66 +1,25 @@ +// Copyright 2014 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 ssh import ( + "bytes" "crypto/dsa" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/rsa" + "encoding/base64" + "fmt" "reflect" "strings" "testing" -) -var ( - ecdsaKey Signer - ecdsa384Key Signer - ecdsa521Key Signer - testCertKey Signer + "code.google.com/p/go.crypto/ssh/testdata" ) -type testSigner struct { - Signer - pub PublicKey -} - -func (ts *testSigner) PublicKey() PublicKey { - if ts.pub != nil { - return ts.pub - } - return ts.Signer.PublicKey() -} - -func init() { - raw256, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - ecdsaKey, _ = NewSignerFromKey(raw256) - - raw384, _ := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - ecdsa384Key, _ = NewSignerFromKey(raw384) - - raw521, _ := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - ecdsa521Key, _ = NewSignerFromKey(raw521) - - // Create a cert and sign it for use in tests. - testCert := &OpenSSHCertV01{ - Nonce: []byte{}, // To pass reflect.DeepEqual after marshal & parse, this must be non-nil - Key: ecdsaKey.PublicKey(), - ValidPrincipals: []string{"gopher1", "gopher2"}, // increases test coverage - ValidAfter: 0, // unix epoch - ValidBefore: maxUint64, // The end of currently representable time. - Reserved: []byte{}, // To pass reflect.DeepEqual after marshal & parse, this must be non-nil - SignatureKey: rsaKey.PublicKey(), - } - sigBytes, _ := rsaKey.Sign(rand.Reader, testCert.BytesForSigning()) - testCert.Signature = &signature{ - Format: testCert.SignatureKey.PublicKeyAlgo(), - Blob: sigBytes, - } - testCertKey = &testSigner{ - Signer: ecdsaKey, - pub: testCert, - } -} - func rawKey(pub PublicKey) interface{} { switch k := pub.(type) { case *rsaPublicKey: @@ -69,23 +28,18 @@ func rawKey(pub PublicKey) interface{} { return (*dsa.PublicKey)(k) case *ecdsaPublicKey: return (*ecdsa.PublicKey)(k) - case *OpenSSHCertV01: + case *Certificate: return k } panic("unknown key type") } func TestKeyMarshalParse(t *testing.T) { - keys := []Signer{rsaKey, dsaKey, ecdsaKey, ecdsa384Key, ecdsa521Key, testCertKey} - for _, priv := range keys { + for _, priv := range testSigners { pub := priv.PublicKey() - roundtrip, rest, ok := ParsePublicKey(MarshalPublicKey(pub)) - if !ok { - t.Errorf("ParsePublicKey(%T) failed", pub) - } - - if len(rest) > 0 { - t.Errorf("ParsePublicKey(%T): trailing junk", pub) + roundtrip, err := ParsePublicKey(pub.Marshal()) + if err != nil { + t.Errorf("ParsePublicKey(%T): %v", pub, err) } k1 := rawKey(pub) @@ -113,9 +67,12 @@ func TestUnsupportedCurves(t *testing.T) { } func TestNewPublicKey(t *testing.T) { - keys := []Signer{rsaKey, dsaKey, ecdsaKey} - for _, k := range keys { + for _, k := range testSigners { raw := rawKey(k.PublicKey()) + // Skip certificates, as NewPublicKey does not support them. + if _, ok := raw.(*Certificate); ok { + continue + } pub, err := NewPublicKey(raw) if err != nil { t.Errorf("NewPublicKey(%#v): %v", raw, err) @@ -127,8 +84,7 @@ func TestNewPublicKey(t *testing.T) { } func TestKeySignVerify(t *testing.T) { - keys := []Signer{rsaKey, dsaKey, ecdsaKey, testCertKey} - for _, priv := range keys { + for _, priv := range testSigners { pub := priv.PublicKey() data := []byte("sign me") @@ -137,19 +93,20 @@ func TestKeySignVerify(t *testing.T) { t.Fatalf("Sign(%T): %v", priv, err) } - if !pub.Verify(data, sig) { - t.Errorf("publicKey.Verify(%T) failed", priv) + if err := pub.Verify(data, sig); err != nil { + t.Errorf("publicKey.Verify(%T): %v", priv, err) + } + sig.Blob[5]++ + if err := pub.Verify(data, sig); err == nil { + t.Errorf("publicKey.Verify on broken sig did not fail") } } } func TestParseRSAPrivateKey(t *testing.T) { - key, err := ParsePrivateKey([]byte(testServerPrivateKey)) - if err != nil { - t.Fatalf("ParsePrivateKey: %v", err) - } + key := testPrivateKeys["rsa"] - rsa, ok := key.(*rsaPrivateKey) + rsa, ok := key.(*rsa.PrivateKey) if !ok { t.Fatalf("got %T, want *rsa.PrivateKey", rsa) } @@ -160,21 +117,11 @@ func TestParseRSAPrivateKey(t *testing.T) { } func TestParseECPrivateKey(t *testing.T) { - // Taken from the data in test/ . - pem := []byte(`-----BEGIN EC PRIVATE KEY----- -MHcCAQEEINGWx0zo6fhJ/0EAfrPzVFyFC9s18lBt3cRoEDhS3ARooAoGCCqGSM49 -AwEHoUQDQgAEi9Hdw6KvZcWxfg2IDhA7UkpDtzzt6ZqJXSsFdLd+Kx4S3Sx4cVO+ -6/ZOXRnPmNAlLUqjShUsUBBngG0u2fqEqA== ------END EC PRIVATE KEY-----`) + key := testPrivateKeys["ecdsa"] - key, err := ParsePrivateKey(pem) - if err != nil { - t.Fatalf("ParsePrivateKey: %v", err) - } - - ecKey, ok := key.(*ecdsaPrivateKey) + ecKey, ok := key.(*ecdsa.PrivateKey) if !ok { - t.Fatalf("got %T, want *ecdsaPrivateKey", ecKey) + t.Fatalf("got %T, want *ecdsa.PrivateKey", ecKey) } if !validateECPublicKey(ecKey.Curve, ecKey.X, ecKey.Y) { @@ -182,22 +129,11 @@ AwEHoUQDQgAEi9Hdw6KvZcWxfg2IDhA7UkpDtzzt6ZqJXSsFdLd+Kx4S3Sx4cVO+ } } -// ssh-keygen -t dsa -f /tmp/idsa.pem -var dsaPEM = `-----BEGIN DSA PRIVATE KEY----- -MIIBuwIBAAKBgQD6PDSEyXiI9jfNs97WuM46MSDCYlOqWw80ajN16AohtBncs1YB -lHk//dQOvCYOsYaE+gNix2jtoRjwXhDsc25/IqQbU1ahb7mB8/rsaILRGIbA5WH3 -EgFtJmXFovDz3if6F6TzvhFpHgJRmLYVR8cqsezL3hEZOvvs2iH7MorkxwIVAJHD -nD82+lxh2fb4PMsIiaXudAsBAoGAQRf7Q/iaPRn43ZquUhd6WwvirqUj+tkIu6eV -2nZWYmXLlqFQKEy4Tejl7Wkyzr2OSYvbXLzo7TNxLKoWor6ips0phYPPMyXld14r -juhT24CrhOzuLMhDduMDi032wDIZG4Y+K7ElU8Oufn8Sj5Wge8r6ANmmVgmFfynr -FhdYCngCgYEA3ucGJ93/Mx4q4eKRDxcWD3QzWyqpbRVRRV1Vmih9Ha/qC994nJFz -DQIdjxDIT2Rk2AGzMqFEB68Zc3O+Wcsmz5eWWzEwFxaTwOGWTyDqsDRLm3fD+QYj -nOwuxb0Kce+gWI8voWcqC9cyRm09jGzu2Ab3Bhtpg8JJ8L7gS3MRZK4CFEx4UAfY -Fmsr0W6fHB9nhS4/UXM8 ------END DSA PRIVATE KEY-----` - func TestParseDSA(t *testing.T) { - s, err := ParsePrivateKey([]byte(dsaPEM)) + // We actually exercise the ParsePrivateKey codepath here, as opposed to + // using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go + // uses. + s, err := ParsePrivateKey(testdata.PEMBytes["dsa"]) if err != nil { t.Fatalf("ParsePrivateKey returned error: %s", err) } @@ -208,7 +144,163 @@ func TestParseDSA(t *testing.T) { t.Fatalf("dsa.Sign: %v", err) } - if !s.PublicKey().Verify(data, sig) { - t.Error("Verify failed.") + if err := s.PublicKey().Verify(data, sig); err != nil { + t.Errorf("Verify failed: %v", err) + } +} + +// Tests for authorized_keys parsing. + +// getTestKey returns a public key, and its base64 encoding. +func getTestKey() (PublicKey, string) { + k := testPublicKeys["rsa"] + + b := &bytes.Buffer{} + e := base64.NewEncoder(base64.StdEncoding, b) + e.Write(k.Marshal()) + e.Close() + + return k, b.String() +} + +func TestMarshalParsePublicKey(t *testing.T) { + pub, pubSerialized := getTestKey() + line := fmt.Sprintf("%s %s user@host", pub.Type(), pubSerialized) + + authKeys := MarshalAuthorizedKey(pub) + actualFields := strings.Fields(string(authKeys)) + if len(actualFields) == 0 { + t.Fatalf("failed authKeys: %v", authKeys) + } + + // drop the comment + expectedFields := strings.Fields(line)[0:2] + + if !reflect.DeepEqual(actualFields, expectedFields) { + t.Errorf("got %v, expected %v", actualFields, expectedFields) + } + + actPub, _, _, _, err := ParseAuthorizedKey([]byte(line)) + if err != nil { + t.Fatalf("cannot parse %v: %v", line, err) + } + if !reflect.DeepEqual(actPub, pub) { + t.Errorf("got %v, expected %v", actPub, pub) + } +} + +type authResult struct { + pubKey PublicKey + options []string + comments string + rest string + ok bool +} + +func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []authResult) { + rest := authKeys + var values []authResult + for len(rest) > 0 { + var r authResult + var err error + r.pubKey, r.comments, r.options, rest, err = ParseAuthorizedKey(rest) + r.ok = (err == nil) + t.Log(err) + r.rest = string(rest) + values = append(values, r) + } + + if !reflect.DeepEqual(values, expected) { + t.Errorf("got %#v, expected %#v", values, expected) + } +} + +func TestAuthorizedKeyBasic(t *testing.T) { + pub, pubSerialized := getTestKey() + line := "ssh-rsa " + pubSerialized + " user@host" + testAuthorizedKeys(t, []byte(line), + []authResult{ + {pub, nil, "user@host", "", true}, + }) +} + +func TestAuth(t *testing.T) { + pub, pubSerialized := getTestKey() + authWithOptions := []string{ + `# comments to ignore before any keys...`, + ``, + `env="HOME=/home/root",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`, + `# comments to ignore, along with a blank line`, + ``, + `env="HOME=/home/root2" ssh-rsa ` + pubSerialized + ` user2@host2`, + ``, + `# more comments, plus a invalid entry`, + `ssh-rsa data-that-will-not-parse user@host3`, + } + for _, eol := range []string{"\n", "\r\n"} { + authOptions := strings.Join(authWithOptions, eol) + rest2 := strings.Join(authWithOptions[3:], eol) + rest3 := strings.Join(authWithOptions[6:], eol) + testAuthorizedKeys(t, []byte(authOptions), []authResult{ + {pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true}, + {pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true}, + {nil, nil, "", "", false}, + }) + } +} + +func TestAuthWithQuotedSpaceInEnv(t *testing.T) { + pub, pubSerialized := getTestKey() + authWithQuotedSpaceInEnv := []byte(`env="HOME=/home/root dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`) + testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []authResult{ + {pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"}, "user@host", "", true}, + }) +} + +func TestAuthWithQuotedCommaInEnv(t *testing.T) { + pub, pubSerialized := getTestKey() + authWithQuotedCommaInEnv := []byte(`env="HOME=/home/root,dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`) + testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []authResult{ + {pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"}, "user@host", "", true}, + }) +} + +func TestAuthWithQuotedQuoteInEnv(t *testing.T) { + pub, pubSerialized := getTestKey() + authWithQuotedQuoteInEnv := []byte(`env="HOME=/home/\"root dir",no-port-forwarding` + "\t" + `ssh-rsa` + "\t" + pubSerialized + ` user@host`) + authWithDoubleQuotedQuote := []byte(`no-port-forwarding,env="HOME=/home/ \"root dir\"" ssh-rsa ` + pubSerialized + "\t" + `user@host`) + testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []authResult{ + {pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true}, + }) + + testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []authResult{ + {pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root dir\""`}, "user@host", "", true}, + }) +} + +func TestAuthWithInvalidSpace(t *testing.T) { + _, pubSerialized := getTestKey() + authWithInvalidSpace := []byte(`env="HOME=/home/root dir", no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host +#more to follow but still no valid keys`) + testAuthorizedKeys(t, []byte(authWithInvalidSpace), []authResult{ + {nil, nil, "", "", false}, + }) +} + +func TestAuthWithMissingQuote(t *testing.T) { + pub, pubSerialized := getTestKey() + authWithMissingQuote := []byte(`env="HOME=/home/root,no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host +env="HOME=/home/root",shared-control ssh-rsa ` + pubSerialized + ` user@host`) + + testAuthorizedKeys(t, []byte(authWithMissingQuote), []authResult{ + {pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user@host", "", true}, + }) +} + +func TestInvalidEntry(t *testing.T) { + authInvalid := []byte(`ssh-rsa`) + _, _, _, _, err := ParseAuthorizedKey(authInvalid) + if err == nil { + t.Errorf("got valid entry for %q", authInvalid) } } |
