github.com/glycerine/xcryptossh@v7.0.4+incompatible/keys_test.go (about)

     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.
     4  
     5  package ssh
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/dsa"
    10  	"crypto/ecdsa"
    11  	"crypto/elliptic"
    12  	"crypto/rand"
    13  	"crypto/rsa"
    14  	"crypto/x509"
    15  	"encoding/base64"
    16  	"fmt"
    17  	"reflect"
    18  	"strings"
    19  	"testing"
    20  
    21  	"github.com/glycerine/xcryptossh/testdata"
    22  	"golang.org/x/crypto/ed25519"
    23  )
    24  
    25  func rawKey(pub PublicKey) interface{} {
    26  	switch k := pub.(type) {
    27  	case *rsaPublicKey:
    28  		return (*rsa.PublicKey)(k)
    29  	case *dsaPublicKey:
    30  		return (*dsa.PublicKey)(k)
    31  	case *ecdsaPublicKey:
    32  		return (*ecdsa.PublicKey)(k)
    33  	case ed25519PublicKey:
    34  		return (ed25519.PublicKey)(k)
    35  	case *Certificate:
    36  		return k
    37  	}
    38  	panic("unknown key type")
    39  }
    40  
    41  func TestKeyMarshalParse(t *testing.T) {
    42  	defer xtestend(xtestbegin(t))
    43  
    44  	for _, priv := range testSigners {
    45  		pub := priv.PublicKey()
    46  		roundtrip, err := ParsePublicKey(pub.Marshal())
    47  		if err != nil {
    48  			t.Errorf("ParsePublicKey(%T): %v", pub, err)
    49  		}
    50  
    51  		k1 := rawKey(pub)
    52  		k2 := rawKey(roundtrip)
    53  
    54  		if !reflect.DeepEqual(k1, k2) {
    55  			t.Errorf("got %#v in roundtrip, want %#v", k2, k1)
    56  		}
    57  	}
    58  }
    59  
    60  func TestUnsupportedCurves(t *testing.T) {
    61  	defer xtestend(xtestbegin(t))
    62  
    63  	raw, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
    64  	if err != nil {
    65  		t.Fatalf("GenerateKey: %v", err)
    66  	}
    67  
    68  	if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P-256") {
    69  		t.Fatalf("NewPrivateKey should not succeed with P-224, got: %v", err)
    70  	}
    71  
    72  	if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P-256") {
    73  		t.Fatalf("NewPublicKey should not succeed with P-224, got: %v", err)
    74  	}
    75  }
    76  
    77  func TestNewPublicKey(t *testing.T) {
    78  	defer xtestend(xtestbegin(t))
    79  	for _, k := range testSigners {
    80  		raw := rawKey(k.PublicKey())
    81  		// Skip certificates, as NewPublicKey does not support them.
    82  		if _, ok := raw.(*Certificate); ok {
    83  			continue
    84  		}
    85  		pub, err := NewPublicKey(raw)
    86  		if err != nil {
    87  			t.Errorf("NewPublicKey(%#v): %v", raw, err)
    88  		}
    89  		if !reflect.DeepEqual(k.PublicKey(), pub) {
    90  			t.Errorf("NewPublicKey(%#v) = %#v, want %#v", raw, pub, k.PublicKey())
    91  		}
    92  	}
    93  }
    94  
    95  func TestKeySignVerify(t *testing.T) {
    96  	defer xtestend(xtestbegin(t))
    97  	for _, priv := range testSigners {
    98  		pub := priv.PublicKey()
    99  
   100  		data := []byte("sign me")
   101  		sig, err := priv.Sign(rand.Reader, data)
   102  		if err != nil {
   103  			t.Fatalf("Sign(%T): %v", priv, err)
   104  		}
   105  
   106  		if err := pub.Verify(data, sig); err != nil {
   107  			t.Errorf("publicKey.Verify(%T): %v", priv, err)
   108  		}
   109  		sig.Blob[5]++
   110  		if err := pub.Verify(data, sig); err == nil {
   111  			t.Errorf("publicKey.Verify on broken sig did not fail")
   112  		}
   113  	}
   114  }
   115  
   116  func TestParseRSAPrivateKey(t *testing.T) {
   117  	defer xtestend(xtestbegin(t))
   118  	key := testPrivateKeys["rsa"]
   119  
   120  	rsa, ok := key.(*rsa.PrivateKey)
   121  	if !ok {
   122  		t.Fatalf("got %T, want *rsa.PrivateKey", rsa)
   123  	}
   124  
   125  	if err := rsa.Validate(); err != nil {
   126  		t.Errorf("Validate: %v", err)
   127  	}
   128  }
   129  
   130  func TestParseECPrivateKey(t *testing.T) {
   131  	defer xtestend(xtestbegin(t))
   132  	key := testPrivateKeys["ecdsa"]
   133  
   134  	ecKey, ok := key.(*ecdsa.PrivateKey)
   135  	if !ok {
   136  		t.Fatalf("got %T, want *ecdsa.PrivateKey", ecKey)
   137  	}
   138  
   139  	if !validateECPublicKey(ecKey.Curve, ecKey.X, ecKey.Y) {
   140  		t.Fatalf("public key does not validate.")
   141  	}
   142  }
   143  
   144  // See Issue https://github.com/golang/go/issues/6650.
   145  func TestParseEncryptedPrivateKeysFails(t *testing.T) {
   146  	defer xtestend(xtestbegin(t))
   147  	const wantSubstring = "encrypted"
   148  	for i, tt := range testdata.PEMEncryptedKeys {
   149  		_, err := ParsePrivateKey(tt.PEMBytes)
   150  		if err == nil {
   151  			t.Errorf("#%d key %s: ParsePrivateKey successfully parsed, expected an error", i, tt.Name)
   152  			continue
   153  		}
   154  
   155  		if !strings.Contains(err.Error(), wantSubstring) {
   156  			t.Errorf("#%d key %s: got error %q, want substring %q", i, tt.Name, err, wantSubstring)
   157  		}
   158  	}
   159  }
   160  
   161  // Parse encrypted private keys with passphrase
   162  func TestParseEncryptedPrivateKeysWithPassphrase(t *testing.T) {
   163  	defer xtestend(xtestbegin(t))
   164  	data := []byte("sign me")
   165  	for _, tt := range testdata.PEMEncryptedKeys {
   166  		s, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte(tt.EncryptionKey))
   167  		if err != nil {
   168  			t.Fatalf("ParsePrivateKeyWithPassphrase returned error: %s", err)
   169  			continue
   170  		}
   171  		sig, err := s.Sign(rand.Reader, data)
   172  		if err != nil {
   173  			t.Fatalf("dsa.Sign: %v", err)
   174  		}
   175  		if err := s.PublicKey().Verify(data, sig); err != nil {
   176  			t.Errorf("Verify failed: %v", err)
   177  		}
   178  	}
   179  
   180  	tt := testdata.PEMEncryptedKeys[0]
   181  	_, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte("incorrect"))
   182  	if err != x509.IncorrectPasswordError {
   183  		t.Fatalf("got %v want IncorrectPasswordError", err)
   184  	}
   185  }
   186  
   187  func TestParseDSA(t *testing.T) {
   188  	defer xtestend(xtestbegin(t))
   189  	// We actually exercise the ParsePrivateKey codepath here, as opposed to
   190  	// using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go
   191  	// uses.
   192  	s, err := ParsePrivateKey(testdata.PEMBytes["dsa"])
   193  	if err != nil {
   194  		t.Fatalf("ParsePrivateKey returned error: %s", err)
   195  	}
   196  
   197  	data := []byte("sign me")
   198  	sig, err := s.Sign(rand.Reader, data)
   199  	if err != nil {
   200  		t.Fatalf("dsa.Sign: %v", err)
   201  	}
   202  
   203  	if err := s.PublicKey().Verify(data, sig); err != nil {
   204  		t.Errorf("Verify failed: %v", err)
   205  	}
   206  }
   207  
   208  // Tests for authorized_keys parsing.
   209  
   210  // getTestKey returns a public key, and its base64 encoding.
   211  func getTestKey() (PublicKey, string) {
   212  	k := testPublicKeys["rsa"]
   213  
   214  	b := &bytes.Buffer{}
   215  	e := base64.NewEncoder(base64.StdEncoding, b)
   216  	e.Write(k.Marshal())
   217  	e.Close()
   218  
   219  	return k, b.String()
   220  }
   221  
   222  func TestMarshalParsePublicKey(t *testing.T) {
   223  	defer xtestend(xtestbegin(t))
   224  	pub, pubSerialized := getTestKey()
   225  	line := fmt.Sprintf("%s %s user@host", pub.Type(), pubSerialized)
   226  
   227  	authKeys := MarshalAuthorizedKey(pub)
   228  	actualFields := strings.Fields(string(authKeys))
   229  	if len(actualFields) == 0 {
   230  		t.Fatalf("failed authKeys: %v", authKeys)
   231  	}
   232  
   233  	// drop the comment
   234  	expectedFields := strings.Fields(line)[0:2]
   235  
   236  	if !reflect.DeepEqual(actualFields, expectedFields) {
   237  		t.Errorf("got %v, expected %v", actualFields, expectedFields)
   238  	}
   239  
   240  	actPub, _, _, _, err := ParseAuthorizedKey([]byte(line))
   241  	if err != nil {
   242  		t.Fatalf("cannot parse %v: %v", line, err)
   243  	}
   244  	if !reflect.DeepEqual(actPub, pub) {
   245  		t.Errorf("got %v, expected %v", actPub, pub)
   246  	}
   247  }
   248  
   249  type authResult struct {
   250  	pubKey   PublicKey
   251  	options  []string
   252  	comments string
   253  	rest     string
   254  	ok       bool
   255  }
   256  
   257  func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []authResult) {
   258  	rest := authKeys
   259  	var values []authResult
   260  	for len(rest) > 0 {
   261  		var r authResult
   262  		var err error
   263  		r.pubKey, r.comments, r.options, rest, err = ParseAuthorizedKey(rest)
   264  		r.ok = (err == nil)
   265  		t.Log(err)
   266  		r.rest = string(rest)
   267  		values = append(values, r)
   268  	}
   269  
   270  	if !reflect.DeepEqual(values, expected) {
   271  		t.Errorf("got %#v, expected %#v", values, expected)
   272  	}
   273  }
   274  
   275  func TestAuthorizedKeyBasic(t *testing.T) {
   276  	defer xtestend(xtestbegin(t))
   277  	pub, pubSerialized := getTestKey()
   278  	line := "ssh-rsa " + pubSerialized + " user@host"
   279  	testAuthorizedKeys(t, []byte(line),
   280  		[]authResult{
   281  			{pub, nil, "user@host", "", true},
   282  		})
   283  }
   284  
   285  func TestAuth(t *testing.T) {
   286  	defer xtestend(xtestbegin(t))
   287  	pub, pubSerialized := getTestKey()
   288  	authWithOptions := []string{
   289  		`# comments to ignore before any keys...`,
   290  		``,
   291  		`env="HOME=/home/root",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`,
   292  		`# comments to ignore, along with a blank line`,
   293  		``,
   294  		`env="HOME=/home/root2" ssh-rsa ` + pubSerialized + ` user2@host2`,
   295  		``,
   296  		`# more comments, plus a invalid entry`,
   297  		`ssh-rsa data-that-will-not-parse user@host3`,
   298  	}
   299  	for _, eol := range []string{"\n", "\r\n"} {
   300  		authOptions := strings.Join(authWithOptions, eol)
   301  		rest2 := strings.Join(authWithOptions[3:], eol)
   302  		rest3 := strings.Join(authWithOptions[6:], eol)
   303  		testAuthorizedKeys(t, []byte(authOptions), []authResult{
   304  			{pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true},
   305  			{pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true},
   306  			{nil, nil, "", "", false},
   307  		})
   308  	}
   309  }
   310  
   311  func TestAuthWithQuotedSpaceInEnv(t *testing.T) {
   312  	defer xtestend(xtestbegin(t))
   313  	pub, pubSerialized := getTestKey()
   314  	authWithQuotedSpaceInEnv := []byte(`env="HOME=/home/root dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`)
   315  	testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []authResult{
   316  		{pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"}, "user@host", "", true},
   317  	})
   318  }
   319  
   320  func TestAuthWithQuotedCommaInEnv(t *testing.T) {
   321  	defer xtestend(xtestbegin(t))
   322  	pub, pubSerialized := getTestKey()
   323  	authWithQuotedCommaInEnv := []byte(`env="HOME=/home/root,dir",no-port-forwarding ssh-rsa ` + pubSerialized + `   user@host`)
   324  	testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []authResult{
   325  		{pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"}, "user@host", "", true},
   326  	})
   327  }
   328  
   329  func TestAuthWithQuotedQuoteInEnv(t *testing.T) {
   330  	defer xtestend(xtestbegin(t))
   331  	pub, pubSerialized := getTestKey()
   332  	authWithQuotedQuoteInEnv := []byte(`env="HOME=/home/\"root dir",no-port-forwarding` + "\t" + `ssh-rsa` + "\t" + pubSerialized + `   user@host`)
   333  	authWithDoubleQuotedQuote := []byte(`no-port-forwarding,env="HOME=/home/ \"root dir\"" ssh-rsa ` + pubSerialized + "\t" + `user@host`)
   334  	testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []authResult{
   335  		{pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true},
   336  	})
   337  
   338  	testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []authResult{
   339  		{pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root dir\""`}, "user@host", "", true},
   340  	})
   341  }
   342  
   343  func TestAuthWithInvalidSpace(t *testing.T) {
   344  	defer xtestend(xtestbegin(t))
   345  	_, pubSerialized := getTestKey()
   346  	authWithInvalidSpace := []byte(`env="HOME=/home/root dir", no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host
   347  #more to follow but still no valid keys`)
   348  	testAuthorizedKeys(t, []byte(authWithInvalidSpace), []authResult{
   349  		{nil, nil, "", "", false},
   350  	})
   351  }
   352  
   353  func TestAuthWithMissingQuote(t *testing.T) {
   354  	defer xtestend(xtestbegin(t))
   355  	pub, pubSerialized := getTestKey()
   356  	authWithMissingQuote := []byte(`env="HOME=/home/root,no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host
   357  env="HOME=/home/root",shared-control ssh-rsa ` + pubSerialized + ` user@host`)
   358  
   359  	testAuthorizedKeys(t, []byte(authWithMissingQuote), []authResult{
   360  		{pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user@host", "", true},
   361  	})
   362  }
   363  
   364  func TestInvalidEntry(t *testing.T) {
   365  	defer xtestend(xtestbegin(t))
   366  	authInvalid := []byte(`ssh-rsa`)
   367  	_, _, _, _, err := ParseAuthorizedKey(authInvalid)
   368  	if err == nil {
   369  		t.Errorf("got valid entry for %q", authInvalid)
   370  	}
   371  }
   372  
   373  var knownHostsParseTests = []struct {
   374  	input string
   375  	err   string
   376  
   377  	marker  string
   378  	comment string
   379  	hosts   []string
   380  	rest    string
   381  }{
   382  	{
   383  		"",
   384  		"EOF",
   385  
   386  		"", "", nil, "",
   387  	},
   388  	{
   389  		"# Just a comment",
   390  		"EOF",
   391  
   392  		"", "", nil, "",
   393  	},
   394  	{
   395  		"   \t   ",
   396  		"EOF",
   397  
   398  		"", "", nil, "",
   399  	},
   400  	{
   401  		"localhost ssh-rsa {RSAPUB}",
   402  		"",
   403  
   404  		"", "", []string{"localhost"}, "",
   405  	},
   406  	{
   407  		"localhost\tssh-rsa {RSAPUB}",
   408  		"",
   409  
   410  		"", "", []string{"localhost"}, "",
   411  	},
   412  	{
   413  		"localhost\tssh-rsa {RSAPUB}\tcomment comment",
   414  		"",
   415  
   416  		"", "comment comment", []string{"localhost"}, "",
   417  	},
   418  	{
   419  		"localhost\tssh-rsa {RSAPUB}\tcomment comment\n",
   420  		"",
   421  
   422  		"", "comment comment", []string{"localhost"}, "",
   423  	},
   424  	{
   425  		"localhost\tssh-rsa {RSAPUB}\tcomment comment\r\n",
   426  		"",
   427  
   428  		"", "comment comment", []string{"localhost"}, "",
   429  	},
   430  	{
   431  		"localhost\tssh-rsa {RSAPUB}\tcomment comment\r\nnext line",
   432  		"",
   433  
   434  		"", "comment comment", []string{"localhost"}, "next line",
   435  	},
   436  	{
   437  		"localhost,[host2:123]\tssh-rsa {RSAPUB}\tcomment comment",
   438  		"",
   439  
   440  		"", "comment comment", []string{"localhost", "[host2:123]"}, "",
   441  	},
   442  	{
   443  		"@marker \tlocalhost,[host2:123]\tssh-rsa {RSAPUB}",
   444  		"",
   445  
   446  		"marker", "", []string{"localhost", "[host2:123]"}, "",
   447  	},
   448  	{
   449  		"@marker \tlocalhost,[host2:123]\tssh-rsa aabbccdd",
   450  		"short read",
   451  
   452  		"", "", nil, "",
   453  	},
   454  }
   455  
   456  func TestKnownHostsParsing(t *testing.T) {
   457  	defer xtestend(xtestbegin(t))
   458  	rsaPub, rsaPubSerialized := getTestKey()
   459  
   460  	for i, test := range knownHostsParseTests {
   461  		var expectedKey PublicKey
   462  		const rsaKeyToken = "{RSAPUB}"
   463  
   464  		input := test.input
   465  		if strings.Contains(input, rsaKeyToken) {
   466  			expectedKey = rsaPub
   467  			input = strings.Replace(test.input, rsaKeyToken, rsaPubSerialized, -1)
   468  		}
   469  
   470  		marker, hosts, pubKey, comment, rest, err := ParseKnownHosts([]byte(input))
   471  		if err != nil {
   472  			if len(test.err) == 0 {
   473  				t.Errorf("#%d: unexpectedly failed with %q", i, err)
   474  			} else if !strings.Contains(err.Error(), test.err) {
   475  				t.Errorf("#%d: expected error containing %q, but got %q", i, test.err, err)
   476  			}
   477  			continue
   478  		} else if len(test.err) != 0 {
   479  			t.Errorf("#%d: succeeded but expected error including %q", i, test.err)
   480  			continue
   481  		}
   482  
   483  		if !reflect.DeepEqual(expectedKey, pubKey) {
   484  			t.Errorf("#%d: expected key %#v, but got %#v", i, expectedKey, pubKey)
   485  		}
   486  
   487  		if marker != test.marker {
   488  			t.Errorf("#%d: expected marker %q, but got %q", i, test.marker, marker)
   489  		}
   490  
   491  		if comment != test.comment {
   492  			t.Errorf("#%d: expected comment %q, but got %q", i, test.comment, comment)
   493  		}
   494  
   495  		if !reflect.DeepEqual(test.hosts, hosts) {
   496  			t.Errorf("#%d: expected hosts %#v, but got %#v", i, test.hosts, hosts)
   497  		}
   498  
   499  		if rest := string(rest); rest != test.rest {
   500  			t.Errorf("#%d: expected remaining input to be %q, but got %q", i, test.rest, rest)
   501  		}
   502  	}
   503  }
   504  
   505  func TestFingerprintLegacyMD5(t *testing.T) {
   506  	defer xtestend(xtestbegin(t))
   507  	pub, _ := getTestKey()
   508  	fingerprint := FingerprintLegacyMD5(pub)
   509  	want := "fb:61:6d:1a:e3:f0:95:45:3c:a0:79:be:4a:93:63:66" // ssh-keygen -lf -E md5 rsa
   510  	if fingerprint != want {
   511  		t.Errorf("got fingerprint %q want %q", fingerprint, want)
   512  	}
   513  }
   514  
   515  func TestFingerprintSHA256(t *testing.T) {
   516  	defer xtestend(xtestbegin(t))
   517  	pub, _ := getTestKey()
   518  	fingerprint := FingerprintSHA256(pub)
   519  	want := "SHA256:Anr3LjZK8YVpjrxu79myrW9Hrb/wpcMNpVvTq/RcBm8" // ssh-keygen -lf rsa
   520  	if fingerprint != want {
   521  		t.Errorf("got fingerprint %q want %q", fingerprint, want)
   522  	}
   523  }