// 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/ed25519"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"math/big"
	"reflect"
	"strings"
	"testing"

	"golang.org/x/crypto/ssh/testdata"
)

func rawKey(pub PublicKey) interface{} {
	switch k := pub.(type) {
	case *rsaPublicKey:
		return (*rsa.PublicKey)(k)
	case *dsaPublicKey:
		return (*dsa.PublicKey)(k)
	case *ecdsaPublicKey:
		return (*ecdsa.PublicKey)(k)
	case ed25519PublicKey:
		return (ed25519.PublicKey)(k)
	case *Certificate:
		return k
	}
	panic("unknown key type")
}

func TestKeyMarshalParse(t *testing.T) {
	for _, priv := range testSigners {
		pub := priv.PublicKey()
		roundtrip, err := ParsePublicKey(pub.Marshal())
		if err != nil {
			t.Errorf("ParsePublicKey(%T): %v", pub, err)
		}

		k1 := rawKey(pub)
		k2 := rawKey(roundtrip)

		if !reflect.DeepEqual(k1, k2) {
			t.Errorf("got %#v in roundtrip, want %#v", k2, k1)
		}
	}
}

func TestParsePublicKeyWithSigningAlgoAsKeyFormat(t *testing.T) {
	key := []byte(`rsa-sha2-256 AAAADHJzYS1zaGEyLTI1NgAAAAMBAAEAAAEBAJ7qMyjLXEJCCJmRknuCLo0uPi5GrPY5pQYr84lhlN8Gor5KVL2LKYCW4e70r5xzj7SrHHSCft1FMlYg1KDO9xrprJh733kQqAPWETmSuH0EfRtGtcH6EarKyVxk6As076/yNiiMKVBtG0RPa1L7FviTfcYK4vnCCVrbv3RmA5CCzuG5BSMbRLxzVb4Ri3p8jhxYT8N4QGe/2yqvJLys5vQ9szpZR3tcFp3DJIVZhBRfR6LnoY23XZniAAMQaUVBX86dXQ++dNwAwZSXSt9Og+AniOCiBYqhNVa5n3DID/H7YtEtG+CbZr3r2KD3fv8AfSLRar4XOp8rsRdD31h/kr8=`)
	_, _, _, _, err := ParseAuthorizedKey(key)
	if err == nil {
		t.Fatal("parsing a public key using a signature algorithm as the key format succeeded unexpectedly")
	}
	if !strings.Contains(err.Error(), `signature algorithm "rsa-sha2-256" isn't a key format`) {
		t.Errorf(`got %v, expected 'signature algorithm "rsa-sha2-256" isn't a key format'`, err)
	}
}

func TestUnsupportedCurves(t *testing.T) {
	raw, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
	if err != nil {
		t.Fatalf("GenerateKey: %v", err)
	}

	if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P-256") {
		t.Fatalf("NewPrivateKey should not succeed with P-224, got: %v", err)
	}

	if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P-256") {
		t.Fatalf("NewPublicKey should not succeed with P-224, got: %v", err)
	}
}

func TestNewPublicKey(t *testing.T) {
	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)
		}
		if !reflect.DeepEqual(k.PublicKey(), pub) {
			t.Errorf("NewPublicKey(%#v) = %#v, want %#v", raw, pub, k.PublicKey())
		}
	}
}

func TestKeySignVerify(t *testing.T) {
	for _, priv := range testSigners {
		pub := priv.PublicKey()

		data := []byte("sign me")
		sig, err := priv.Sign(rand.Reader, data)
		if err != nil {
			t.Fatalf("Sign(%T): %v", priv, err)
		}

		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 TestKeySignWithAlgorithmVerify(t *testing.T) {
	for k, priv := range testSigners {
		if algorithmSigner, ok := priv.(MultiAlgorithmSigner); !ok {
			t.Errorf("Signers %q constructed by ssh package should always implement the MultiAlgorithmSigner interface: %T", k, priv)
		} else {
			pub := priv.PublicKey()
			data := []byte("sign me")

			signWithAlgTestCase := func(algorithm string, expectedAlg string) {
				sig, err := algorithmSigner.SignWithAlgorithm(rand.Reader, data, algorithm)
				if err != nil {
					t.Fatalf("Sign(%T): %v", priv, err)
				}
				if sig.Format != expectedAlg {
					t.Errorf("signature format did not match requested signature algorithm: %s != %s", sig.Format, expectedAlg)
				}

				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")
				}
			}

			// Using the empty string as the algorithm name should result in the same signature format as the algorithm-free Sign method.
			defaultSig, err := priv.Sign(rand.Reader, data)
			if err != nil {
				t.Fatalf("Sign(%T): %v", priv, err)
			}
			signWithAlgTestCase("", defaultSig.Format)

			// RSA keys are the only ones which currently support more than one signing algorithm
			if pub.Type() == KeyAlgoRSA {
				for _, algorithm := range []string{KeyAlgoRSA, KeyAlgoRSASHA256, KeyAlgoRSASHA512} {
					signWithAlgTestCase(algorithm, algorithm)
				}
			}
		}
	}
}

func TestKeySignWithShortSignature(t *testing.T) {
	signer := testSigners["rsa"].(AlgorithmSigner)
	pub := signer.PublicKey()
	// Note: data obtained by empirically trying until a result
	// starting with 0 appeared
	tests := []struct {
		algorithm string
		data      []byte
	}{
		{
			algorithm: KeyAlgoRSA,
			data:      []byte("sign me92"),
		},
		{
			algorithm: KeyAlgoRSASHA256,
			data:      []byte("sign me294"),
		},
		{
			algorithm: KeyAlgoRSASHA512,
			data:      []byte("sign me60"),
		},
	}

	for _, tt := range tests {
		sig, err := signer.SignWithAlgorithm(rand.Reader, tt.data, tt.algorithm)
		if err != nil {
			t.Fatalf("Sign(%T): %v", signer, err)
		}
		if sig.Blob[0] != 0 {
			t.Errorf("%s: Expected signature with a leading 0", tt.algorithm)
		}
		sig.Blob = sig.Blob[1:]
		if err := pub.Verify(tt.data, sig); err != nil {
			t.Errorf("publicKey.Verify(%s): %v", tt.algorithm, err)
		}
	}
}

func TestParseRSAPrivateKey(t *testing.T) {
	key := testPrivateKeys["rsa"]

	rsa, ok := key.(*rsa.PrivateKey)
	if !ok {
		t.Fatalf("got %T, want *rsa.PrivateKey", rsa)
	}

	if err := rsa.Validate(); err != nil {
		t.Errorf("Validate: %v", err)
	}
}

func TestParseRSAModulusTooLarge(t *testing.T) {
	rsa16384 := "ssh-rsa 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"
	_, _, _, _, err := ParseAuthorizedKey([]byte(rsa16384))

	if err == nil {
		t.Fatal("ParseAuthorizedKey accepted a 16384-bit modulus; expected it to be rejected")
	}

	expectedError := "rsa modulus too large"
	if !strings.Contains(err.Error(), expectedError) {
		t.Errorf("unexpected error message: got %q, want substring %q", err.Error(), expectedError)
	}
}

func TestParseECPrivateKey(t *testing.T) {
	key := testPrivateKeys["ecdsa"]

	ecKey, ok := key.(*ecdsa.PrivateKey)
	if !ok {
		t.Fatalf("got %T, want *ecdsa.PrivateKey", ecKey)
	}

	if !validateECPublicKey(ecKey.Curve, ecKey.X, ecKey.Y) {
		t.Fatalf("public key does not validate.")
	}
}

func TestParseEncryptedPrivateKeysWithPassphrase(t *testing.T) {
	data := []byte("sign me")
	for _, tt := range testdata.PEMEncryptedKeys {
		t.Run(tt.Name, func(t *testing.T) {
			_, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte("incorrect"))
			if err != x509.IncorrectPasswordError {
				t.Errorf("got %v want IncorrectPasswordError", err)
			}

			s, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte(tt.EncryptionKey))
			if err != nil {
				t.Fatalf("ParsePrivateKeyWithPassphrase returned error: %s", err)
			}

			sig, err := s.Sign(rand.Reader, data)
			if err != nil {
				t.Fatalf("Signer.Sign: %v", err)
			}
			if err := s.PublicKey().Verify(data, sig); err != nil {
				t.Errorf("Verify failed: %v", err)
			}

			_, err = ParsePrivateKey(tt.PEMBytes)
			if err == nil {
				t.Fatalf("ParsePrivateKey succeeded, expected an error")
			}

			if err, ok := err.(*PassphraseMissingError); !ok {
				t.Errorf("got error %q, want PassphraseMissingError", err)
			} else if tt.IncludesPublicKey {
				if err.PublicKey == nil {
					t.Fatalf("expected PassphraseMissingError.PublicKey not to be nil")
				}
				got, want := err.PublicKey.Marshal(), s.PublicKey().Marshal()
				if !bytes.Equal(got, want) {
					t.Errorf("error field %q doesn't match signer public key %q", got, want)
				}
			}
		})
	}
}

func TestParseEncryptedPrivateKeysWithUnsupportedCiphers(t *testing.T) {
	for _, tt := range testdata.UnsupportedCipherData {
		t.Run(tt.Name, func(t *testing.T) {
			_, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte(tt.EncryptionKey))
			if err == nil {
				t.Fatalf("expected 'unknown cipher' error for %q, got nil", tt.Name)
				// If this cipher is now supported, remove it from testdata.UnsupportedCipherData
			}
			if !strings.Contains(err.Error(), "unknown cipher") {
				t.Errorf("wanted 'unknown cipher' error, got %v", err.Error())
			}
		})
	}
}

func TestParseEncryptedPrivateKeysWithIncorrectPassphrase(t *testing.T) {
	pem := testdata.PEMEncryptedKeys[0].PEMBytes
	for i := 0; i < 4096; i++ {
		_, err := ParseRawPrivateKeyWithPassphrase(pem, []byte(fmt.Sprintf("%d", i)))
		if !errors.Is(err, x509.IncorrectPasswordError) {
			t.Fatalf("expected error: %v, got: %v", x509.IncorrectPasswordError, err)
		}
	}
}

func TestParseDSA(t *testing.T) {
	// 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)
	}

	data := []byte("sign me")
	sig, err := s.Sign(rand.Reader, data)
	if err != nil {
		t.Fatalf("dsa.Sign: %v", err)
	}

	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)
	}
}

func TestParseDSAHugeQ(t *testing.T) {
	P := new(big.Int).Lsh(big.NewInt(1), 1023)
	Q := new(big.Int).Lsh(big.NewInt(1), 20000) // very large
	// G and Y: Dummy values, just needs to be < P to pass that specific check
	G := big.NewInt(2)
	Y := big.NewInt(5)

	rawKey := struct {
		P, Q, G, Y *big.Int
	}{
		P: P,
		Q: Q,
		G: G,
		Y: Y,
	}

	inputBytes := Marshal(&rawKey)

	_, _, err := parseDSA(inputBytes)
	if err == nil {
		t.Fatal("parseDSA accepted a DSA key with large Q")
	}

	expectedError := "ssh: unsupported DSA sub-prime size"
	if !strings.Contains(err.Error(), expectedError) {
		t.Errorf("unexpected error message: got %q, want substring %q", err.Error(), expectedError)
	}
}

func TestParseDSAYOutOfRange(t *testing.T) {
	// Valid 1024/160 parameters (values don't need to be a real DSA group,
	// they only need to pass the checkDSAParams bit-length checks and the
	// G < P / G > 0 checks).
	P := new(big.Int).Lsh(big.NewInt(1), 1023)
	P.SetBit(P, 0, 1) // make P odd so it can pass as a prime candidate shape
	Q := new(big.Int).Lsh(big.NewInt(1), 159)
	Q.SetBit(Q, 0, 1)
	G := big.NewInt(2)

	for _, tc := range []struct {
		name string
		Y    *big.Int
	}{
		{"Y_zero", big.NewInt(0)},
		{"Y_negative", big.NewInt(-1)},
		{"Y_equals_P", new(big.Int).Set(P)},
		{"Y_greater_than_P", new(big.Int).Add(P, big.NewInt(1))},
		{"Y_much_greater_than_P", new(big.Int).Lsh(big.NewInt(1), 20000)},
	} {
		t.Run(tc.name, func(t *testing.T) {
			rawKey := struct {
				P, Q, G, Y *big.Int
			}{P: P, Q: Q, G: G, Y: tc.Y}

			_, _, err := parseDSA(Marshal(&rawKey))
			if err == nil {
				t.Fatalf("parseDSA accepted a DSA key with Y=%s (P=%s)", tc.Y, P)
			}
			expectedError := "DSA public value Y out of range"
			if !strings.Contains(err.Error(), expectedError) {
				t.Errorf("unexpected error message: got %q, want substring %q", err.Error(), expectedError)
			}
		})
	}
}

func TestMarshalPrivateKey(t *testing.T) {
	tests := []struct {
		name string
	}{
		{"rsa-openssh-format"},
		{"ed25519"},
		{"p256-openssh-format"},
		{"p384-openssh-format"},
		{"p521-openssh-format"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			expected, ok := testPrivateKeys[tt.name]
			if !ok {
				t.Fatalf("cannot find key %s", tt.name)
			}

			block, err := MarshalPrivateKey(expected, "test@golang.org")
			if err != nil {
				t.Fatalf("cannot marshal %s: %v", tt.name, err)
			}

			key, err := ParseRawPrivateKey(pem.EncodeToMemory(block))
			if err != nil {
				t.Fatalf("cannot parse %s: %v", tt.name, err)
			}

			if !reflect.DeepEqual(expected, key) {
				t.Errorf("unexpected marshaled key %s", tt.name)
			}
		})
	}
}

func TestMarshalPrivateKeyWithPassphrase(t *testing.T) {
	tests := []struct {
		name string
	}{
		{"rsa-openssh-format"},
		{"ed25519"},
		{"p256-openssh-format"},
		{"p384-openssh-format"},
		{"p521-openssh-format"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			expected, ok := testPrivateKeys[tt.name]
			if !ok {
				t.Fatalf("cannot find key %s", tt.name)
			}

			block, err := MarshalPrivateKeyWithPassphrase(expected, "test@golang.org", []byte("test-passphrase"))
			if err != nil {
				t.Fatalf("cannot marshal %s: %v", tt.name, err)
			}

			key, err := ParseRawPrivateKeyWithPassphrase(pem.EncodeToMemory(block), []byte("test-passphrase"))
			if err != nil {
				t.Fatalf("cannot parse %s: %v", tt.name, err)
			}

			if !reflect.DeepEqual(expected, key) {
				t.Errorf("unexpected marshaled key %s", tt.name)
			}
		})
	}
}

type testAuthResult struct {
	pubKey   PublicKey
	options  []string
	comments string
	rest     string
	ok       bool
}

func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []testAuthResult) {
	rest := authKeys
	var values []testAuthResult
	for len(rest) > 0 {
		var r testAuthResult
		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),
		[]testAuthResult{
			{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), []testAuthResult{
			{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), []testAuthResult{
		{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), []testAuthResult{
		{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), []testAuthResult{
		{pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true},
	})

	testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []testAuthResult{
		{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), []testAuthResult{
		{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), []testAuthResult{
		{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)
	}
}

var knownHostsParseTests = []struct {
	input string
	err   string

	marker  string
	comment string
	hosts   []string
	rest    string
}{
	{
		"",
		"EOF",

		"", "", nil, "",
	},
	{
		"# Just a comment",
		"EOF",

		"", "", nil, "",
	},
	{
		"   \t   ",
		"EOF",

		"", "", nil, "",
	},
	{
		"localhost ssh-rsa {RSAPUB}",
		"",

		"", "", []string{"localhost"}, "",
	},
	{
		"localhost\tssh-rsa {RSAPUB}",
		"",

		"", "", []string{"localhost"}, "",
	},
	{
		"localhost\tssh-rsa {RSAPUB}\tcomment comment",
		"",

		"", "comment comment", []string{"localhost"}, "",
	},
	{
		"localhost\tssh-rsa {RSAPUB}\tcomment comment\n",
		"",

		"", "comment comment", []string{"localhost"}, "",
	},
	{
		"localhost\tssh-rsa {RSAPUB}\tcomment comment\r\n",
		"",

		"", "comment comment", []string{"localhost"}, "",
	},
	{
		"localhost\tssh-rsa {RSAPUB}\tcomment comment\r\nnext line",
		"",

		"", "comment comment", []string{"localhost"}, "next line",
	},
	{
		"localhost,[host2:123]\tssh-rsa {RSAPUB}\tcomment comment",
		"",

		"", "comment comment", []string{"localhost", "[host2:123]"}, "",
	},
	{
		"@marker \tlocalhost,[host2:123]\tssh-rsa {RSAPUB}",
		"",

		"marker", "", []string{"localhost", "[host2:123]"}, "",
	},
	{
		"@marker \tlocalhost,[host2:123]\tssh-rsa aabbccdd",
		"short read",

		"", "", nil, "",
	},
}

func TestKnownHostsParsing(t *testing.T) {
	rsaPub, rsaPubSerialized := getTestKey()

	for i, test := range knownHostsParseTests {
		var expectedKey PublicKey
		const rsaKeyToken = "{RSAPUB}"

		input := test.input
		if strings.Contains(input, rsaKeyToken) {
			expectedKey = rsaPub
			input = strings.Replace(test.input, rsaKeyToken, rsaPubSerialized, -1)
		}

		marker, hosts, pubKey, comment, rest, err := ParseKnownHosts([]byte(input))
		if err != nil {
			if len(test.err) == 0 {
				t.Errorf("#%d: unexpectedly failed with %q", i, err)
			} else if !strings.Contains(err.Error(), test.err) {
				t.Errorf("#%d: expected error containing %q, but got %q", i, test.err, err)
			}
			continue
		} else if len(test.err) != 0 {
			t.Errorf("#%d: succeeded but expected error including %q", i, test.err)
			continue
		}

		if !reflect.DeepEqual(expectedKey, pubKey) {
			t.Errorf("#%d: expected key %#v, but got %#v", i, expectedKey, pubKey)
		}

		if marker != test.marker {
			t.Errorf("#%d: expected marker %q, but got %q", i, test.marker, marker)
		}

		if comment != test.comment {
			t.Errorf("#%d: expected comment %q, but got %q", i, test.comment, comment)
		}

		if !reflect.DeepEqual(test.hosts, hosts) {
			t.Errorf("#%d: expected hosts %#v, but got %#v", i, test.hosts, hosts)
		}

		if rest := string(rest); rest != test.rest {
			t.Errorf("#%d: expected remaining input to be %q, but got %q", i, test.rest, rest)
		}
	}
}

func TestFingerprintLegacyMD5(t *testing.T) {
	pub, _ := getTestKey()
	fingerprint := FingerprintLegacyMD5(pub)
	want := "b7:ef:d3:d5:89:29:52:96:9f:df:47:41:4d:15:37:f4" // ssh-keygen -lf -E md5 rsa
	if fingerprint != want {
		t.Errorf("got fingerprint %q want %q", fingerprint, want)
	}
}

func TestFingerprintSHA256(t *testing.T) {
	pub, _ := getTestKey()
	fingerprint := FingerprintSHA256(pub)
	want := "SHA256:fi5+D7UmDZDE9Q2sAVvvlpcQSIakN4DERdINgXd2AnE" // ssh-keygen -lf rsa
	if fingerprint != want {
		t.Errorf("got fingerprint %q want %q", fingerprint, want)
	}
}

func TestInvalidKeys(t *testing.T) {
	keyTypes := []string{
		"RSA PRIVATE KEY",
		"PRIVATE KEY",
		"EC PRIVATE KEY",
		"DSA PRIVATE KEY",
		"OPENSSH PRIVATE KEY",
	}

	for _, keyType := range keyTypes {
		for _, dataLen := range []int{0, 1, 2, 5, 10, 20} {
			data := make([]byte, dataLen)
			if _, err := io.ReadFull(rand.Reader, data); err != nil {
				t.Fatal(err)
			}

			var buf bytes.Buffer
			pem.Encode(&buf, &pem.Block{
				Type:  keyType,
				Bytes: data,
			})

			// This test is just to ensure that the function
			// doesn't panic so the return value is ignored.
			ParseRawPrivateKey(buf.Bytes())
		}
	}
}

func TestSKKeys(t *testing.T) {
	for _, d := range testdata.SKData {
		pk, _, _, _, err := ParseAuthorizedKey(d.PubKey)
		if err != nil {
			t.Fatalf("parseAuthorizedKey returned error: %v", err)
		}

		sigBuf := make([]byte, hex.DecodedLen(len(d.HexSignature)))
		if _, err := hex.Decode(sigBuf, d.HexSignature); err != nil {
			t.Fatalf("hex.Decode() failed: %v", err)
		}

		dataBuf := make([]byte, hex.DecodedLen(len(d.HexData)))
		if _, err := hex.Decode(dataBuf, d.HexData); err != nil {
			t.Fatalf("hex.Decode() failed: %v", err)
		}

		sig, _, ok := parseSignature(sigBuf)
		if !ok {
			t.Fatalf("parseSignature(%v) failed", sigBuf)
		}

		// Test that good data and signature pass verification
		if err := pk.Verify(dataBuf, sig); err != nil {
			t.Errorf("%s: PublicKey.Verify(%v, %v) failed: %v", d.Name, dataBuf, sig, err)
		}

		// Invalid data being passed in
		invalidData := []byte("INVALID DATA")
		if err := pk.Verify(invalidData, sig); err == nil {
			t.Errorf("%s with invalid data: PublicKey.Verify(%v, %v) passed unexpectedly", d.Name, invalidData, sig)
		}

		// Change byte in blob to corrup signature
		sig.Blob[5] = byte('A')
		// Corrupted data being passed in
		if err := pk.Verify(dataBuf, sig); err == nil {
			t.Errorf("%s with corrupted signature: PublicKey.Verify(%v, %v) passed unexpectedly", d.Name, dataBuf, sig)
		}
	}
}

// skTestHarness builds SK-formatted signatures over a fixed payload
// using a caller-supplied signing function, letting tests vary the UP
// flag byte without duplicating the wire-format scaffolding.
type skTestHarness struct {
	format      string
	application string
	data        []byte
	// sign takes the SHA-256 digest of the marshalled SK blob and
	// returns the value to embed in Signature.Blob. For ECDSA keys the
	// helper feeds the digest to ecdsa.Sign; for ed25519 the helper
	// feeds the raw marshalled blob to ed25519.Sign.
	signDigest func(digest []byte) []byte
	signBlob   func(blob []byte) []byte
}

func (h skTestHarness) sign(t *testing.T, flags byte) *Signature {
	t.Helper()
	hsh := sha256.New()
	hsh.Write([]byte(h.application))
	appDigest := hsh.Sum(nil)

	hsh.Reset()
	hsh.Write(h.data)
	dataDigest := hsh.Sum(nil)

	var counter uint32 = 1
	blob := struct {
		ApplicationDigest []byte `ssh:"rest"`
		Flags             byte
		Counter           uint32
		MessageDigest     []byte `ssh:"rest"`
	}{appDigest, flags, counter, dataDigest}
	marshalled := Marshal(blob)

	var sigBlob []byte
	if h.signDigest != nil {
		hsh.Reset()
		hsh.Write(marshalled)
		sigBlob = h.signDigest(hsh.Sum(nil))
	} else {
		sigBlob = h.signBlob(marshalled)
	}

	return &Signature{
		Format: h.format,
		Blob:   sigBlob,
		Rest: Marshal(struct {
			Flags   byte
			Counter uint32
		}{flags, counter}),
	}
}

func TestSKUserPresence(t *testing.T) {
	ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		t.Fatal(err)
	}

	h := skTestHarness{
		format:      "sk-ecdsa-sha2-nistp256@openssh.com",
		application: "ssh:",
		data:        []byte("test data"),
		signDigest: func(digest []byte) []byte {
			r, s, err := ecdsa.Sign(rand.Reader, ecKey, digest)
			if err != nil {
				t.Fatal(err)
			}
			return Marshal(struct{ R, S *big.Int }{r, s})
		},
	}

	pk := &skECDSAPublicKey{
		application: h.application,
		PublicKey:   ecKey.PublicKey,
	}

	// Valid signature with UP=1 should pass.
	if err := pk.Verify(h.data, h.sign(t, flagUserPresence)); err != nil {
		t.Errorf("Verify failed with UP=1: %v", err)
	}

	// Valid signature with UP=0 should fail with the user-presence sentinel.
	sigNoUP := h.sign(t, 0)
	if err := pk.Verify(h.data, sigNoUP); !errors.Is(err, errSKMissingUserPresence) {
		t.Errorf("expected errSKMissingUserPresence, got: %v", err)
	}

	// UV set but UP clear must still fail: we only waive UP, never UV-only.
	if err := pk.Verify(h.data, h.sign(t, 0x04)); !errors.Is(err, errSKMissingUserPresence) {
		t.Errorf("UV-only (flags=0x04): expected errSKMissingUserPresence, got: %v", err)
	}

	// With noTouchRequired, UP=0 passes; UP=1+UV=1 also passes.
	pk.noTouchRequired = true
	if err := pk.Verify(h.data, sigNoUP); err != nil {
		t.Errorf("Verify with noTouchRequired failed: %v", err)
	}
	if err := pk.Verify(h.data, h.sign(t, flagUserPresence|0x04)); err != nil {
		t.Errorf("Verify UP|UV with noTouchRequired failed: %v", err)
	}
}

func TestSKKeyWithoutUP(t *testing.T) {
	ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	edPub, _, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}

	skEC := &skECDSAPublicKey{PublicKey: ecKey.PublicKey}
	skED := &skEd25519PublicKey{PublicKey: edPub}
	certEC := &Certificate{Key: &skECDSAPublicKey{PublicKey: ecKey.PublicKey}}
	certED := &Certificate{Key: &skEd25519PublicKey{PublicKey: edPub}}
	rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		t.Fatal(err)
	}
	rsaPub, err := NewPublicKey(&rsaKey.PublicKey)
	if err != nil {
		t.Fatal(err)
	}
	certRSA := &Certificate{Key: rsaPub}

	// SK raw keys: return a clone with the flag set, original untouched.
	gotEC := skKeyWithoutUP(skEC)
	if gotEC == skEC {
		t.Error("skECDSAPublicKey: expected a clone, got the original pointer")
	}
	if !gotEC.(*skECDSAPublicKey).noTouchRequired {
		t.Error("skECDSAPublicKey clone: noTouchRequired not set")
	}
	if skEC.noTouchRequired {
		t.Error("skECDSAPublicKey: original mutated")
	}

	gotED := skKeyWithoutUP(skED)
	if gotED == skED {
		t.Error("skEd25519PublicKey: expected a clone, got the original pointer")
	}
	if !gotED.(*skEd25519PublicKey).noTouchRequired {
		t.Error("skEd25519PublicKey clone: noTouchRequired not set")
	}
	if skED.noTouchRequired {
		t.Error("skEd25519PublicKey: original mutated")
	}

	// Certificate wrapping SK: return a clone of the cert with a cloned
	// SK key inside. Neither the original cert nor the original inner
	// key must be mutated.
	originalInnerEC := certEC.Key
	gotCertEC := skKeyWithoutUP(certEC)
	if gotCertEC == certEC {
		t.Error("*Certificate(SK ecdsa): expected a clone, got the original pointer")
	}
	if got := gotCertEC.(*Certificate).Key.(*skECDSAPublicKey); !got.noTouchRequired {
		t.Error("*Certificate(SK ecdsa): inner clone missing noTouchRequired")
	}
	if certEC.Key != originalInnerEC {
		t.Error("*Certificate(SK ecdsa): original cert's Key pointer mutated")
	}
	if originalInnerEC.(*skECDSAPublicKey).noTouchRequired {
		t.Error("*Certificate(SK ecdsa): original inner key mutated")
	}

	gotCertED := skKeyWithoutUP(certED)
	if gotCertED == certED {
		t.Error("*Certificate(SK ed25519): expected a clone, got the original pointer")
	}
	if got := gotCertED.(*Certificate).Key.(*skEd25519PublicKey); !got.noTouchRequired {
		t.Error("*Certificate(SK ed25519): inner clone missing noTouchRequired")
	}

	// Non-SK key inside a cert: return original unchanged (nothing to clone).
	if got := skKeyWithoutUP(certRSA); got != certRSA {
		t.Error("*Certificate(RSA): expected the original pointer back")
	}

	// Plain non-SK key: return original unchanged.
	if got := skKeyWithoutUP(rsaPub); got != rsaPub {
		t.Error("rsaPublicKey: expected the original pointer back")
	}

	// Pathological: *Certificate whose Key is itself a *Certificate.
	// The SSH cert format forbids this and parseCert rejects it, but
	// a Go caller can still construct such a value. skKeyWithoutUP
	// must not recurse into it (or panic); it returns the input
	// unchanged. This also defends against a hypothetical cycle built
	// from hand-constructed Certificate pointers.
	nestedCert := &Certificate{Key: &Certificate{Key: skEC}}
	if got := skKeyWithoutUP(nestedCert); got != nestedCert {
		t.Error("*Certificate wrapping *Certificate: expected the original pointer back")
	}
	selfCycle := &Certificate{}
	selfCycle.Key = selfCycle
	if got := skKeyWithoutUP(selfCycle); got != selfCycle {
		t.Error("self-referential *Certificate: expected the original pointer back")
	}
}

func TestNoTouchAllowed(t *testing.T) {
	ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	sk := &skECDSAPublicKey{PublicKey: ecKey.PublicKey}
	certNoExt := &Certificate{Key: sk}
	certOptOut := &Certificate{
		Key:         sk,
		Permissions: Permissions{Extensions: map[string]string{"no-touch-required": ""}},
	}
	// Non-empty value: OpenSSH writes "" but any value must be accepted
	// because the check is presence-only.
	certOptOutNonEmpty := &Certificate{
		Key:         sk,
		Permissions: Permissions{Extensions: map[string]string{"no-touch-required": "yes"}},
	}
	// no-touch-required belongs in Extensions, never CriticalOptions.
	// Putting it in CriticalOptions must NOT be treated as opt-out.
	certCritOnly := &Certificate{
		Key:         sk,
		Permissions: Permissions{CriticalOptions: map[string]string{"no-touch-required": ""}},
	}

	permsEmpty := &Permissions{}
	permsOptOut := &Permissions{Extensions: map[string]string{"no-touch-required": ""}}
	permsCritOnly := &Permissions{CriticalOptions: map[string]string{"no-touch-required": ""}}

	cases := []struct {
		name  string
		pub   PublicKey
		perms *Permissions
		want  bool
	}{
		{"nil perms, no cert", sk, nil, false},
		{"empty perms, no cert", sk, permsEmpty, false},
		{"perms opt-out, raw key", sk, permsOptOut, true},
		{"nil perms, cert opt-out", certOptOut, nil, true},
		{"nil perms, cert opt-out non-empty value", certOptOutNonEmpty, nil, true},
		{"nil perms, cert no ext", certNoExt, nil, false},
		{"perms opt-out, cert no ext", certNoExt, permsOptOut, true},
		{"empty perms, cert opt-out", certOptOut, permsEmpty, true},
		// Negative controls: CriticalOptions must not waive UP.
		{"critical-options only, raw key", sk, permsCritOnly, false},
		{"critical-options only, cert", certCritOnly, nil, false},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			if got := noTouchAllowed(tc.pub, tc.perms); got != tc.want {
				t.Errorf("noTouchAllowed = %v, want %v", got, tc.want)
			}
		})
	}
}

func TestSKUserPresenceEd25519(t *testing.T) {
	pub, priv, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	h := skTestHarness{
		format:      "sk-ssh-ed25519@openssh.com",
		application: "ssh:",
		data:        []byte("test data"),
		signBlob: func(blob []byte) []byte {
			return ed25519.Sign(priv, blob)
		},
	}

	pk := &skEd25519PublicKey{
		application: h.application,
		PublicKey:   pub,
	}

	if err := pk.Verify(h.data, h.sign(t, flagUserPresence)); err != nil {
		t.Errorf("Verify failed with UP=1: %v", err)
	}
	sigNoUP := h.sign(t, 0)
	if err := pk.Verify(h.data, sigNoUP); !errors.Is(err, errSKMissingUserPresence) {
		t.Errorf("expected errSKMissingUserPresence, got: %v", err)
	}
	pk.noTouchRequired = true
	if err := pk.Verify(h.data, sigNoUP); err != nil {
		t.Errorf("Verify with noTouchRequired failed: %v", err)
	}
}

func TestNewSignerWithAlgos(t *testing.T) {
	algorithSigner, ok := testSigners["rsa"].(AlgorithmSigner)
	if !ok {
		t.Fatal("rsa test signer does not implement the AlgorithmSigner interface")
	}
	_, err := NewSignerWithAlgorithms(algorithSigner, nil)
	if err == nil {
		t.Error("signer with algos created with no algorithms")
	}

	_, err = NewSignerWithAlgorithms(algorithSigner, []string{KeyAlgoED25519})
	if err == nil {
		t.Error("signer with algos created with invalid algorithms")
	}

	_, err = NewSignerWithAlgorithms(algorithSigner, []string{CertAlgoRSASHA256v01})
	if err == nil {
		t.Error("signer with algos created with certificate algorithms")
	}

	mas, err := NewSignerWithAlgorithms(algorithSigner, []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512})
	if err != nil {
		t.Errorf("unable to create signer with valid algorithms: %v", err)
	}

	_, err = NewSignerWithAlgorithms(mas, []string{KeyAlgoRSA})
	if err == nil {
		t.Error("signer with algos created with restricted algorithms")
	}
}

func TestCryptoPublicKey(t *testing.T) {
	for _, priv := range testSigners {
		p1 := priv.PublicKey()
		key, ok := p1.(CryptoPublicKey)
		if !ok {
			continue
		}
		p2, err := NewPublicKey(key.CryptoPublicKey())
		if err != nil {
			t.Fatalf("NewPublicKey(CryptoPublicKey) failed for %s, got: %v", p1.Type(), err)
		}
		if !reflect.DeepEqual(p1, p2) {
			t.Errorf("got %#v in NewPublicKey, want %#v", p2, p1)
		}
	}
	for _, d := range testdata.SKData {
		p1, _, _, _, err := ParseAuthorizedKey(d.PubKey)
		if err != nil {
			t.Fatalf("parseAuthorizedKey returned error: %v", err)
		}
		k1, ok := p1.(CryptoPublicKey)
		if !ok {
			t.Fatalf("%T does not implement CryptoPublicKey", p1)
		}

		var p2 PublicKey
		switch pub := k1.CryptoPublicKey().(type) {
		case *ecdsa.PublicKey:
			p2 = &skECDSAPublicKey{
				application: "ssh:",
				PublicKey:   *pub,
			}
		case ed25519.PublicKey:
			p2 = &skEd25519PublicKey{
				application: "ssh:",
				PublicKey:   pub,
			}
		default:
			t.Fatalf("unexpected type %T from CryptoPublicKey()", pub)
		}
		if !reflect.DeepEqual(p1, p2) {
			t.Errorf("got %#v, want %#v", p2, p1)
		}
	}
}

func TestParseCertWithCertSignatureKey(t *testing.T) {
	certBytes := []byte(`-----BEGIN SSH CERTIFICATE-----
AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIPSp27hvNSB0
IotJnVhjC4zxNgNS8BHlUCxD0VJi4D/eAAAAIIJMi1e5qfx+IFuKD/p/Ssqcb3os
CpOw/4wBs1pQ53zwAAAAAAAAAAEAAAACAAAAAAAAABMAAAAPZm9vLmV4YW1wbGUu
Y29tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0AAAAgc3NoLWVkMjU1
MTktY2VydC12MDFAb3BlbnNzaC5jb20AAAAg+sNYhCO35mQT1UBMpmMk8ey+culd
IU8vBlPEl4B07swAAAAggiv+RLnboS4znGCVl/n1jDg2uD0h15tW4s/04eS2mLQA
AAAAAAAAAQAAAAIAAAAAAAAAEwAAAA9mb28uZXhhbXBsZS5jb20AAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACCV2wETgLKL
Kt0bRl3YUnd/ZYSlq0xJMbn4Jj3cdPWykQAAAFMAAAALc3NoLWVkMjU1MTkAAABA
WOdbRGEzyRAhiIK227CLUQD5caXYMV8FvSIB7toEE2M/8HnWdG9H3Rsg/v3unruQ
JrQldnuPJNe7KOP2+zvUDgAAAFMAAAALc3NoLWVkMjU1MTkAAABAm3bIPp85ZpIe
D+izJcUqlcAOri7HO8bULFNHT6LVegvB06xQ5TLwMlrxWUF4cafl1tSe8JQck4a6
cLYUOHfQDw==
-----END SSH CERTIFICATE-----
	`)
	block, _ := pem.Decode(certBytes)
	if block == nil {
		t.Fatal("invalid test certificate")
	}

	if _, err := ParsePublicKey(block.Bytes); err == nil {
		t.Fatal("parsing an SSH certificate using another certificate as signature key succeeded; expected failure")
	}
}
