aboutsummaryrefslogtreecommitdiff
path: root/ssh/keys_test.go
diff options
context:
space:
mode:
authorAdam Langley <agl@golang.org>2014-04-09 13:57:52 -0700
committerAdam Langley <agl@golang.org>2014-04-09 13:57:52 -0700
commitfa50e7408b9ef89ff2965535b59f1a0010c0770b (patch)
treee045a3f48f9ffd3bb712002f8f9f6fd489e8f7ef /ssh/keys_test.go
parent8f45c680ceb25c200b8c301d9184532aeb7cb36e (diff)
downloadgo-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.go292
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)
}
}