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
5package ssh
6
7import (
8	"bytes"
9	"crypto/dsa"
10	"crypto/ecdsa"
11	"crypto/elliptic"
12	"crypto/rand"
13	"crypto/rsa"
14	"crypto/x509"
15	"encoding/base64"
16	"encoding/hex"
17	"encoding/pem"
18	"fmt"
19	"io"
20	"reflect"
21	"strings"
22	"testing"
23
24	"golang.org/x/crypto/ed25519"
25	"golang.org/x/crypto/ssh/testdata"
26)
27
28func rawKey(pub PublicKey) interface{} {
29	switch k := pub.(type) {
30	case *rsaPublicKey:
31		return (*rsa.PublicKey)(k)
32	case *dsaPublicKey:
33		return (*dsa.PublicKey)(k)
34	case *ecdsaPublicKey:
35		return (*ecdsa.PublicKey)(k)
36	case ed25519PublicKey:
37		return (ed25519.PublicKey)(k)
38	case *Certificate:
39		return k
40	}
41	panic("unknown key type")
42}
43
44func TestKeyMarshalParse(t *testing.T) {
45	for _, priv := range testSigners {
46		pub := priv.PublicKey()
47		roundtrip, err := ParsePublicKey(pub.Marshal())
48		if err != nil {
49			t.Errorf("ParsePublicKey(%T): %v", pub, err)
50		}
51
52		k1 := rawKey(pub)
53		k2 := rawKey(roundtrip)
54
55		if !reflect.DeepEqual(k1, k2) {
56			t.Errorf("got %#v in roundtrip, want %#v", k2, k1)
57		}
58	}
59}
60
61func TestUnsupportedCurves(t *testing.T) {
62	raw, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
63	if err != nil {
64		t.Fatalf("GenerateKey: %v", err)
65	}
66
67	if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P-256") {
68		t.Fatalf("NewPrivateKey should not succeed with P-224, got: %v", err)
69	}
70
71	if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P-256") {
72		t.Fatalf("NewPublicKey should not succeed with P-224, got: %v", err)
73	}
74}
75
76func TestNewPublicKey(t *testing.T) {
77	for _, k := range testSigners {
78		raw := rawKey(k.PublicKey())
79		// Skip certificates, as NewPublicKey does not support them.
80		if _, ok := raw.(*Certificate); ok {
81			continue
82		}
83		pub, err := NewPublicKey(raw)
84		if err != nil {
85			t.Errorf("NewPublicKey(%#v): %v", raw, err)
86		}
87		if !reflect.DeepEqual(k.PublicKey(), pub) {
88			t.Errorf("NewPublicKey(%#v) = %#v, want %#v", raw, pub, k.PublicKey())
89		}
90	}
91}
92
93func TestKeySignVerify(t *testing.T) {
94	for _, priv := range testSigners {
95		pub := priv.PublicKey()
96
97		data := []byte("sign me")
98		sig, err := priv.Sign(rand.Reader, data)
99		if err != nil {
100			t.Fatalf("Sign(%T): %v", priv, err)
101		}
102
103		if err := pub.Verify(data, sig); err != nil {
104			t.Errorf("publicKey.Verify(%T): %v", priv, err)
105		}
106		sig.Blob[5]++
107		if err := pub.Verify(data, sig); err == nil {
108			t.Errorf("publicKey.Verify on broken sig did not fail")
109		}
110	}
111}
112
113func TestKeySignWithAlgorithmVerify(t *testing.T) {
114	for _, priv := range testSigners {
115		if algorithmSigner, ok := priv.(AlgorithmSigner); !ok {
116			t.Errorf("Signers constructed by ssh package should always implement the AlgorithmSigner interface: %T", priv)
117		} else {
118			pub := priv.PublicKey()
119			data := []byte("sign me")
120
121			signWithAlgTestCase := func(algorithm string, expectedAlg string) {
122				sig, err := algorithmSigner.SignWithAlgorithm(rand.Reader, data, algorithm)
123				if err != nil {
124					t.Fatalf("Sign(%T): %v", priv, err)
125				}
126				if sig.Format != expectedAlg {
127					t.Errorf("signature format did not match requested signature algorithm: %s != %s", sig.Format, expectedAlg)
128				}
129
130				if err := pub.Verify(data, sig); err != nil {
131					t.Errorf("publicKey.Verify(%T): %v", priv, err)
132				}
133				sig.Blob[5]++
134				if err := pub.Verify(data, sig); err == nil {
135					t.Errorf("publicKey.Verify on broken sig did not fail")
136				}
137			}
138
139			// Using the empty string as the algorithm name should result in the same signature format as the algorithm-free Sign method.
140			defaultSig, err := priv.Sign(rand.Reader, data)
141			if err != nil {
142				t.Fatalf("Sign(%T): %v", priv, err)
143			}
144			signWithAlgTestCase("", defaultSig.Format)
145
146			// RSA keys are the only ones which currently support more than one signing algorithm
147			if pub.Type() == KeyAlgoRSA {
148				for _, algorithm := range []string{SigAlgoRSA, SigAlgoRSASHA2256, SigAlgoRSASHA2512} {
149					signWithAlgTestCase(algorithm, algorithm)
150				}
151			}
152		}
153	}
154}
155
156func TestParseRSAPrivateKey(t *testing.T) {
157	key := testPrivateKeys["rsa"]
158
159	rsa, ok := key.(*rsa.PrivateKey)
160	if !ok {
161		t.Fatalf("got %T, want *rsa.PrivateKey", rsa)
162	}
163
164	if err := rsa.Validate(); err != nil {
165		t.Errorf("Validate: %v", err)
166	}
167}
168
169func TestParseECPrivateKey(t *testing.T) {
170	key := testPrivateKeys["ecdsa"]
171
172	ecKey, ok := key.(*ecdsa.PrivateKey)
173	if !ok {
174		t.Fatalf("got %T, want *ecdsa.PrivateKey", ecKey)
175	}
176
177	if !validateECPublicKey(ecKey.Curve, ecKey.X, ecKey.Y) {
178		t.Fatalf("public key does not validate.")
179	}
180}
181
182func TestParseEncryptedPrivateKeysWithPassphrase(t *testing.T) {
183	data := []byte("sign me")
184	for _, tt := range testdata.PEMEncryptedKeys {
185		t.Run(tt.Name, func(t *testing.T) {
186			_, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte("incorrect"))
187			if err != x509.IncorrectPasswordError {
188				t.Errorf("got %v want IncorrectPasswordError", err)
189			}
190
191			s, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte(tt.EncryptionKey))
192			if err != nil {
193				t.Fatalf("ParsePrivateKeyWithPassphrase returned error: %s", err)
194			}
195
196			sig, err := s.Sign(rand.Reader, data)
197			if err != nil {
198				t.Fatalf("Signer.Sign: %v", err)
199			}
200			if err := s.PublicKey().Verify(data, sig); err != nil {
201				t.Errorf("Verify failed: %v", err)
202			}
203
204			_, err = ParsePrivateKey(tt.PEMBytes)
205			if err == nil {
206				t.Fatalf("ParsePrivateKey succeeded, expected an error")
207			}
208
209			if err, ok := err.(*PassphraseMissingError); !ok {
210				t.Errorf("got error %q, want PassphraseMissingError", err)
211			} else if tt.IncludesPublicKey {
212				if err.PublicKey == nil {
213					t.Fatalf("expected PassphraseMissingError.PublicKey not to be nil")
214				}
215				got, want := err.PublicKey.Marshal(), s.PublicKey().Marshal()
216				if !bytes.Equal(got, want) {
217					t.Errorf("error field %q doesn't match signer public key %q", got, want)
218				}
219			}
220		})
221	}
222}
223
224func TestParseDSA(t *testing.T) {
225	// We actually exercise the ParsePrivateKey codepath here, as opposed to
226	// using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go
227	// uses.
228	s, err := ParsePrivateKey(testdata.PEMBytes["dsa"])
229	if err != nil {
230		t.Fatalf("ParsePrivateKey returned error: %s", err)
231	}
232
233	data := []byte("sign me")
234	sig, err := s.Sign(rand.Reader, data)
235	if err != nil {
236		t.Fatalf("dsa.Sign: %v", err)
237	}
238
239	if err := s.PublicKey().Verify(data, sig); err != nil {
240		t.Errorf("Verify failed: %v", err)
241	}
242}
243
244// Tests for authorized_keys parsing.
245
246// getTestKey returns a public key, and its base64 encoding.
247func getTestKey() (PublicKey, string) {
248	k := testPublicKeys["rsa"]
249
250	b := &bytes.Buffer{}
251	e := base64.NewEncoder(base64.StdEncoding, b)
252	e.Write(k.Marshal())
253	e.Close()
254
255	return k, b.String()
256}
257
258func TestMarshalParsePublicKey(t *testing.T) {
259	pub, pubSerialized := getTestKey()
260	line := fmt.Sprintf("%s %s user@host", pub.Type(), pubSerialized)
261
262	authKeys := MarshalAuthorizedKey(pub)
263	actualFields := strings.Fields(string(authKeys))
264	if len(actualFields) == 0 {
265		t.Fatalf("failed authKeys: %v", authKeys)
266	}
267
268	// drop the comment
269	expectedFields := strings.Fields(line)[0:2]
270
271	if !reflect.DeepEqual(actualFields, expectedFields) {
272		t.Errorf("got %v, expected %v", actualFields, expectedFields)
273	}
274
275	actPub, _, _, _, err := ParseAuthorizedKey([]byte(line))
276	if err != nil {
277		t.Fatalf("cannot parse %v: %v", line, err)
278	}
279	if !reflect.DeepEqual(actPub, pub) {
280		t.Errorf("got %v, expected %v", actPub, pub)
281	}
282}
283
284type testAuthResult struct {
285	pubKey   PublicKey
286	options  []string
287	comments string
288	rest     string
289	ok       bool
290}
291
292func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []testAuthResult) {
293	rest := authKeys
294	var values []testAuthResult
295	for len(rest) > 0 {
296		var r testAuthResult
297		var err error
298		r.pubKey, r.comments, r.options, rest, err = ParseAuthorizedKey(rest)
299		r.ok = (err == nil)
300		t.Log(err)
301		r.rest = string(rest)
302		values = append(values, r)
303	}
304
305	if !reflect.DeepEqual(values, expected) {
306		t.Errorf("got %#v, expected %#v", values, expected)
307	}
308}
309
310func TestAuthorizedKeyBasic(t *testing.T) {
311	pub, pubSerialized := getTestKey()
312	line := "ssh-rsa " + pubSerialized + " user@host"
313	testAuthorizedKeys(t, []byte(line),
314		[]testAuthResult{
315			{pub, nil, "user@host", "", true},
316		})
317}
318
319func TestAuth(t *testing.T) {
320	pub, pubSerialized := getTestKey()
321	authWithOptions := []string{
322		`# comments to ignore before any keys...`,
323		``,
324		`env="HOME=/home/root",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`,
325		`# comments to ignore, along with a blank line`,
326		``,
327		`env="HOME=/home/root2" ssh-rsa ` + pubSerialized + ` user2@host2`,
328		``,
329		`# more comments, plus a invalid entry`,
330		`ssh-rsa data-that-will-not-parse user@host3`,
331	}
332	for _, eol := range []string{"\n", "\r\n"} {
333		authOptions := strings.Join(authWithOptions, eol)
334		rest2 := strings.Join(authWithOptions[3:], eol)
335		rest3 := strings.Join(authWithOptions[6:], eol)
336		testAuthorizedKeys(t, []byte(authOptions), []testAuthResult{
337			{pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true},
338			{pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true},
339			{nil, nil, "", "", false},
340		})
341	}
342}
343
344func TestAuthWithQuotedSpaceInEnv(t *testing.T) {
345	pub, pubSerialized := getTestKey()
346	authWithQuotedSpaceInEnv := []byte(`env="HOME=/home/root dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`)
347	testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []testAuthResult{
348		{pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"}, "user@host", "", true},
349	})
350}
351
352func TestAuthWithQuotedCommaInEnv(t *testing.T) {
353	pub, pubSerialized := getTestKey()
354	authWithQuotedCommaInEnv := []byte(`env="HOME=/home/root,dir",no-port-forwarding ssh-rsa ` + pubSerialized + `   user@host`)
355	testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []testAuthResult{
356		{pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"}, "user@host", "", true},
357	})
358}
359
360func TestAuthWithQuotedQuoteInEnv(t *testing.T) {
361	pub, pubSerialized := getTestKey()
362	authWithQuotedQuoteInEnv := []byte(`env="HOME=/home/\"root dir",no-port-forwarding` + "\t" + `ssh-rsa` + "\t" + pubSerialized + `   user@host`)
363	authWithDoubleQuotedQuote := []byte(`no-port-forwarding,env="HOME=/home/ \"root dir\"" ssh-rsa ` + pubSerialized + "\t" + `user@host`)
364	testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []testAuthResult{
365		{pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true},
366	})
367
368	testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []testAuthResult{
369		{pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root dir\""`}, "user@host", "", true},
370	})
371}
372
373func TestAuthWithInvalidSpace(t *testing.T) {
374	_, pubSerialized := getTestKey()
375	authWithInvalidSpace := []byte(`env="HOME=/home/root dir", no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host
376#more to follow but still no valid keys`)
377	testAuthorizedKeys(t, []byte(authWithInvalidSpace), []testAuthResult{
378		{nil, nil, "", "", false},
379	})
380}
381
382func TestAuthWithMissingQuote(t *testing.T) {
383	pub, pubSerialized := getTestKey()
384	authWithMissingQuote := []byte(`env="HOME=/home/root,no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host
385env="HOME=/home/root",shared-control ssh-rsa ` + pubSerialized + ` user@host`)
386
387	testAuthorizedKeys(t, []byte(authWithMissingQuote), []testAuthResult{
388		{pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user@host", "", true},
389	})
390}
391
392func TestInvalidEntry(t *testing.T) {
393	authInvalid := []byte(`ssh-rsa`)
394	_, _, _, _, err := ParseAuthorizedKey(authInvalid)
395	if err == nil {
396		t.Errorf("got valid entry for %q", authInvalid)
397	}
398}
399
400var knownHostsParseTests = []struct {
401	input string
402	err   string
403
404	marker  string
405	comment string
406	hosts   []string
407	rest    string
408}{
409	{
410		"",
411		"EOF",
412
413		"", "", nil, "",
414	},
415	{
416		"# Just a comment",
417		"EOF",
418
419		"", "", nil, "",
420	},
421	{
422		"   \t   ",
423		"EOF",
424
425		"", "", nil, "",
426	},
427	{
428		"localhost ssh-rsa {RSAPUB}",
429		"",
430
431		"", "", []string{"localhost"}, "",
432	},
433	{
434		"localhost\tssh-rsa {RSAPUB}",
435		"",
436
437		"", "", []string{"localhost"}, "",
438	},
439	{
440		"localhost\tssh-rsa {RSAPUB}\tcomment comment",
441		"",
442
443		"", "comment comment", []string{"localhost"}, "",
444	},
445	{
446		"localhost\tssh-rsa {RSAPUB}\tcomment comment\n",
447		"",
448
449		"", "comment comment", []string{"localhost"}, "",
450	},
451	{
452		"localhost\tssh-rsa {RSAPUB}\tcomment comment\r\n",
453		"",
454
455		"", "comment comment", []string{"localhost"}, "",
456	},
457	{
458		"localhost\tssh-rsa {RSAPUB}\tcomment comment\r\nnext line",
459		"",
460
461		"", "comment comment", []string{"localhost"}, "next line",
462	},
463	{
464		"localhost,[host2:123]\tssh-rsa {RSAPUB}\tcomment comment",
465		"",
466
467		"", "comment comment", []string{"localhost", "[host2:123]"}, "",
468	},
469	{
470		"@marker \tlocalhost,[host2:123]\tssh-rsa {RSAPUB}",
471		"",
472
473		"marker", "", []string{"localhost", "[host2:123]"}, "",
474	},
475	{
476		"@marker \tlocalhost,[host2:123]\tssh-rsa aabbccdd",
477		"short read",
478
479		"", "", nil, "",
480	},
481}
482
483func TestKnownHostsParsing(t *testing.T) {
484	rsaPub, rsaPubSerialized := getTestKey()
485
486	for i, test := range knownHostsParseTests {
487		var expectedKey PublicKey
488		const rsaKeyToken = "{RSAPUB}"
489
490		input := test.input
491		if strings.Contains(input, rsaKeyToken) {
492			expectedKey = rsaPub
493			input = strings.Replace(test.input, rsaKeyToken, rsaPubSerialized, -1)
494		}
495
496		marker, hosts, pubKey, comment, rest, err := ParseKnownHosts([]byte(input))
497		if err != nil {
498			if len(test.err) == 0 {
499				t.Errorf("#%d: unexpectedly failed with %q", i, err)
500			} else if !strings.Contains(err.Error(), test.err) {
501				t.Errorf("#%d: expected error containing %q, but got %q", i, test.err, err)
502			}
503			continue
504		} else if len(test.err) != 0 {
505			t.Errorf("#%d: succeeded but expected error including %q", i, test.err)
506			continue
507		}
508
509		if !reflect.DeepEqual(expectedKey, pubKey) {
510			t.Errorf("#%d: expected key %#v, but got %#v", i, expectedKey, pubKey)
511		}
512
513		if marker != test.marker {
514			t.Errorf("#%d: expected marker %q, but got %q", i, test.marker, marker)
515		}
516
517		if comment != test.comment {
518			t.Errorf("#%d: expected comment %q, but got %q", i, test.comment, comment)
519		}
520
521		if !reflect.DeepEqual(test.hosts, hosts) {
522			t.Errorf("#%d: expected hosts %#v, but got %#v", i, test.hosts, hosts)
523		}
524
525		if rest := string(rest); rest != test.rest {
526			t.Errorf("#%d: expected remaining input to be %q, but got %q", i, test.rest, rest)
527		}
528	}
529}
530
531func TestFingerprintLegacyMD5(t *testing.T) {
532	pub, _ := getTestKey()
533	fingerprint := FingerprintLegacyMD5(pub)
534	want := "fb:61:6d:1a:e3:f0:95:45:3c:a0:79:be:4a:93:63:66" // ssh-keygen -lf -E md5 rsa
535	if fingerprint != want {
536		t.Errorf("got fingerprint %q want %q", fingerprint, want)
537	}
538}
539
540func TestFingerprintSHA256(t *testing.T) {
541	pub, _ := getTestKey()
542	fingerprint := FingerprintSHA256(pub)
543	want := "SHA256:Anr3LjZK8YVpjrxu79myrW9Hrb/wpcMNpVvTq/RcBm8" // ssh-keygen -lf rsa
544	if fingerprint != want {
545		t.Errorf("got fingerprint %q want %q", fingerprint, want)
546	}
547}
548
549func TestInvalidKeys(t *testing.T) {
550	keyTypes := []string{
551		"RSA PRIVATE KEY",
552		"PRIVATE KEY",
553		"EC PRIVATE KEY",
554		"DSA PRIVATE KEY",
555		"OPENSSH PRIVATE KEY",
556	}
557
558	for _, keyType := range keyTypes {
559		for _, dataLen := range []int{0, 1, 2, 5, 10, 20} {
560			data := make([]byte, dataLen)
561			if _, err := io.ReadFull(rand.Reader, data); err != nil {
562				t.Fatal(err)
563			}
564
565			var buf bytes.Buffer
566			pem.Encode(&buf, &pem.Block{
567				Type:  keyType,
568				Bytes: data,
569			})
570
571			// This test is just to ensure that the function
572			// doesn't panic so the return value is ignored.
573			ParseRawPrivateKey(buf.Bytes())
574		}
575	}
576}
577
578func TestSKKeys(t *testing.T) {
579	for _, d := range testdata.SKData {
580		pk, _, _, _, err := ParseAuthorizedKey(d.PubKey)
581		if err != nil {
582			t.Fatalf("parseAuthorizedKey returned error: %v", err)
583		}
584
585		sigBuf := make([]byte, hex.DecodedLen(len(d.HexSignature)))
586		if _, err := hex.Decode(sigBuf, d.HexSignature); err != nil {
587			t.Fatalf("hex.Decode() failed: %v", err)
588		}
589
590		dataBuf := make([]byte, hex.DecodedLen(len(d.HexData)))
591		if _, err := hex.Decode(dataBuf, d.HexData); err != nil {
592			t.Fatalf("hex.Decode() failed: %v", err)
593		}
594
595		sig, _, ok := parseSignature(sigBuf)
596		if !ok {
597			t.Fatalf("parseSignature(%v) failed", sigBuf)
598		}
599
600		// Test that good data and signature pass verification
601		if err := pk.Verify(dataBuf, sig); err != nil {
602			t.Errorf("%s: PublicKey.Verify(%v, %v) failed: %v", d.Name, dataBuf, sig, err)
603		}
604
605		// Invalid data being passed in
606		invalidData := []byte("INVALID DATA")
607		if err := pk.Verify(invalidData, sig); err == nil {
608			t.Errorf("%s with invalid data: PublicKey.Verify(%v, %v) passed unexpectedly", d.Name, invalidData, sig)
609		}
610
611		// Change byte in blob to corrup signature
612		sig.Blob[5] = byte('A')
613		// Corrupted data being passed in
614		if err := pk.Verify(dataBuf, sig); err == nil {
615			t.Errorf("%s with corrupted signature: PublicKey.Verify(%v, %v) passed unexpectedly", d.Name, dataBuf, sig)
616		}
617	}
618}
619