RFC 8332, Section 2 sets up two overlapping namespaces: public key formats and public key algorithms. * The formats are what we currently have KeyAlgo constants for, and they appear in PublicKey.Type. * The algorithms are the set of both KeyAlgo and SigAlgo constants, and they appear in Signature.Format (amongst other places). This is incoherent, because that means Signature.Format can be both a KeyAlgo (like KeyAlgoECDSA256) or a SigAlgo (like SigAlgoRSASHA2256). One solution would be to duplicate all the KeyAlgo constants into the SigAlgo namespace, but that would be confusing because applications are currently using KeyAlgos where they'd be supposed to use the new SigAlgos (while we can't deprecate the KeyAlgos because they are still necessary for the PublicKey.Type namespace). Instead, drop the separate namespaces, and use KeyAlgos throughout. There are simply some KeyAlgos that can't be a PublicKey.Type. Take the opportunity to fix the stuttering SHA22565/SHA2512 names. It's totally ok to call those hashes SHA-256 and SHA-512 without the family infix. For golang/go#49952 Change-Id: Ia1fce3912a7e60aa70a88f75ed311be331fd19d5 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/392354 Trust: Filippo Valsorda <filippo@golang.org> Run-TryBot: Filippo Valsorda <filippo@golang.org> Reviewed-by: Roland Shoemaker <roland@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org>
619 lines
17 KiB
Go
619 lines
17 KiB
Go
// 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"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"golang.org/x/crypto/ed25519"
|
|
"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 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 _, priv := range testSigners {
|
|
if algorithmSigner, ok := priv.(AlgorithmSigner); !ok {
|
|
t.Errorf("Signers constructed by ssh package should always implement the AlgorithmSigner interface: %T", 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 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 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 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)
|
|
}
|
|
}
|
|
|
|
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 := "fb:61:6d:1a:e3:f0:95:45:3c:a0:79:be:4a:93:63:66" // 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:Anr3LjZK8YVpjrxu79myrW9Hrb/wpcMNpVvTq/RcBm8" // 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)
|
|
}
|
|
}
|
|
}
|