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