go.crypto/ssh: introduce Signer method, an abstraction of

private keys.

R=agl, jpsugar, jonathan.mark.pittman
CC=golang-dev
https://golang.org/cl/13338044
This commit is contained in:
Han-Wen Nienhuys
2013-09-19 14:45:31 -04:00
committed by Adam Langley
parent 28dc961a18
commit 934c14ffe9
9 changed files with 394 additions and 250 deletions
-4
View File
@@ -68,10 +68,6 @@ func (c *OpenSSHCertV01) PublicKeyAlgo() string {
return algo
}
func (c *OpenSSHCertV01) RawKey() interface{} {
return c.Key.RawKey()
}
func (c *OpenSSHCertV01) PrivateKeyAlgo() string {
return c.Key.PrivateKeyAlgo()
}
+34 -48
View File
@@ -6,12 +6,7 @@ package ssh
import (
"bytes"
"crypto"
"crypto/dsa"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"io"
"io/ioutil"
"math/big"
@@ -62,32 +57,23 @@ rrxx26itVhJmcvoUhOjwuzSlP2bE5VHAvkGB352YBg==
// keychain implements the ClientKeyring interface
type keychain struct {
keys []interface{}
keys []Signer
}
func (k *keychain) Key(i int) (PublicKey, error) {
if i < 0 || i >= len(k.keys) {
return nil, nil
}
switch key := k.keys[i].(type) {
case *rsa.PrivateKey:
return NewRSAPublicKey(&key.PublicKey), nil
case *dsa.PrivateKey:
return NewDSAPublicKey(&key.PublicKey), nil
}
panic("unknown key type")
return k.keys[i].PublicKey(), nil
}
func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) {
hashFunc := crypto.SHA1
h := hashFunc.New()
h.Write(data)
digest := h.Sum(nil)
switch key := k.keys[i].(type) {
case *rsa.PrivateKey:
return rsa.SignPKCS1v15(rand, key, hashFunc, digest)
}
return nil, errors.New("ssh: unknown key type")
return k.keys[i].Sign(rand, data)
}
func (k *keychain) add(key Signer) {
k.keys = append(k.keys, key)
}
func (k *keychain) loadPEM(file string) error {
@@ -95,15 +81,11 @@ func (k *keychain) loadPEM(file string) error {
if err != nil {
return err
}
block, _ := pem.Decode(buf)
if block == nil {
return errors.New("ssh: no key found")
}
r, err := x509.ParsePKCS1PrivateKey(block.Bytes)
key, err := ParsePrivateKey(buf)
if err != nil {
return err
}
k.keys = append(k.keys, r)
k.add(key)
return nil
}
@@ -126,8 +108,8 @@ func (cr *keyboardInteractive) Challenge(user string, instruction string, questi
// reused internally by tests
var (
rsakey *rsa.PrivateKey
dsakey *dsa.PrivateKey
rsaKey Signer
dsaKey Signer
clientKeychain = new(keychain)
clientPassword = password("tiger")
serverConfig = &ServerConfig{
@@ -135,8 +117,7 @@ var (
return user == "testuser" && pass == string(clientPassword)
},
PublicKeyCallback: func(conn *ServerConn, user, algo string, pubkey []byte) bool {
rsaKey := &clientKeychain.keys[0].(*rsa.PrivateKey).PublicKey
key := NewRSAPublicKey(rsaKey)
key, _ := clientKeychain.Key(0)
expected := MarshalPublicKey(key)
algoname := key.PublicKeyAlgo()
return user == "testuser" && algo == algoname && bytes.Equal(pubkey, expected)
@@ -157,21 +138,26 @@ var (
)
func init() {
if err := serverConfig.SetRSAPrivateKey([]byte(testServerPrivateKey)); err != nil {
var err error
rsaKey, err = ParsePrivateKey([]byte(testServerPrivateKey))
if err != nil {
panic("unable to set private key: " + err.Error())
}
rawDSAKey := new(dsa.PrivateKey)
block, _ := pem.Decode([]byte(testClientPrivateKey))
rsakey, _ = x509.ParsePKCS1PrivateKey(block.Bytes)
clientKeychain.keys = append(clientKeychain.keys, rsakey)
dsakey = new(dsa.PrivateKey)
// taken from crypto/dsa/dsa_test.go
dsakey.P, _ = new(big.Int).SetString("A9B5B793FB4785793D246BAE77E8FF63CA52F442DA763C440259919FE1BC1D6065A9350637A04F75A2F039401D49F08E066C4D275A5A65DA5684BC563C14289D7AB8A67163BFBF79D85972619AD2CFF55AB0EE77A9002B0EF96293BDD0F42685EBB2C66C327079F6C98000FBCB79AACDE1BC6F9D5C7B1A97E3D9D54ED7951FEF", 16)
dsakey.Q, _ = new(big.Int).SetString("E1D3391245933D68A0714ED34BBCB7A1F422B9C1", 16)
dsakey.G, _ = new(big.Int).SetString("634364FC25248933D01D1993ECABD0657CC0CB2CEED7ED2E3E8AECDFCDC4A25C3B15E9E3B163ACA2984B5539181F3EFF1A5E8903D71D5B95DA4F27202B77D2C44B430BB53741A8D59A8F86887525C9F2A6A5980A195EAA7F2FF910064301DEF89D3AA213E1FAC7768D89365318E370AF54A112EFBA9246D9158386BA1B4EEFDA", 16)
dsakey.Y, _ = new(big.Int).SetString("32969E5780CFE1C849A1C276D7AEB4F38A23B591739AA2FE197349AEEBD31366AEE5EB7E6C6DDB7C57D02432B30DB5AA66D9884299FAA72568944E4EEDC92EA3FBC6F39F53412FBCC563208F7C15B737AC8910DBC2D9C9B8C001E72FDC40EB694AB1F06A5A2DBD18D9E36C66F31F566742F11EC0A52E9F7B89355C02FB5D32D2", 16)
dsakey.X, _ = new(big.Int).SetString("5078D4D29795CBE76D3AACFE48C9AF0BCDBEE91A", 16)
rawDSAKey.P, _ = new(big.Int).SetString("A9B5B793FB4785793D246BAE77E8FF63CA52F442DA763C440259919FE1BC1D6065A9350637A04F75A2F039401D49F08E066C4D275A5A65DA5684BC563C14289D7AB8A67163BFBF79D85972619AD2CFF55AB0EE77A9002B0EF96293BDD0F42685EBB2C66C327079F6C98000FBCB79AACDE1BC6F9D5C7B1A97E3D9D54ED7951FEF", 16)
rawDSAKey.Q, _ = new(big.Int).SetString("E1D3391245933D68A0714ED34BBCB7A1F422B9C1", 16)
rawDSAKey.G, _ = new(big.Int).SetString("634364FC25248933D01D1993ECABD0657CC0CB2CEED7ED2E3E8AECDFCDC4A25C3B15E9E3B163ACA2984B5539181F3EFF1A5E8903D71D5B95DA4F27202B77D2C44B430BB53741A8D59A8F86887525C9F2A6A5980A195EAA7F2FF910064301DEF89D3AA213E1FAC7768D89365318E370AF54A112EFBA9246D9158386BA1B4EEFDA", 16)
rawDSAKey.Y, _ = new(big.Int).SetString("32969E5780CFE1C849A1C276D7AEB4F38A23B591739AA2FE197349AEEBD31366AEE5EB7E6C6DDB7C57D02432B30DB5AA66D9884299FAA72568944E4EEDC92EA3FBC6F39F53412FBCC563208F7C15B737AC8910DBC2D9C9B8C001E72FDC40EB694AB1F06A5A2DBD18D9E36C66F31F566742F11EC0A52E9F7B89355C02FB5D32D2", 16)
rawDSAKey.X, _ = new(big.Int).SetString("5078D4D29795CBE76D3AACFE48C9AF0BCDBEE91A", 16)
dsaKey, err = NewSignerFromKey(rawDSAKey)
if err != nil {
panic("NewSignerFromKey: " + err.Error())
}
clientKeychain.add(rsaKey)
serverConfig.AddHostKey(rsaKey)
}
// newMockAuthServer creates a new Server bound to
@@ -287,7 +273,8 @@ func TestClientAuthWrongKeyboardInteractive(t *testing.T) {
// the mock server will only authenticate ssh-rsa keys
func TestClientAuthInvalidPublicKey(t *testing.T) {
kc := new(keychain)
kc.keys = append(kc.keys, dsakey)
kc.add(dsaKey)
config := &ClientConfig{
User: "testuser",
Auth: []ClientAuth{
@@ -305,7 +292,8 @@ func TestClientAuthInvalidPublicKey(t *testing.T) {
// the client should authenticate with the second key
func TestClientAuthRSAandDSA(t *testing.T) {
kc := new(keychain)
kc.keys = append(kc.keys, dsakey, rsakey)
kc.add(dsaKey)
kc.add(rsaKey)
config := &ClientConfig{
User: "testuser",
Auth: []ClientAuth{
@@ -321,7 +309,7 @@ func TestClientAuthRSAandDSA(t *testing.T) {
func TestClientHMAC(t *testing.T) {
kc := new(keychain)
kc.keys = append(kc.keys, rsakey)
kc.add(rsaKey)
for _, mac := range DefaultMACOrder {
config := &ClientConfig{
User: "testuser",
@@ -343,7 +331,6 @@ func TestClientHMAC(t *testing.T) {
// issue 4285.
func TestClientUnsupportedCipher(t *testing.T) {
kc := new(keychain)
kc.keys = append(kc.keys, rsakey)
config := &ClientConfig{
User: "testuser",
Auth: []ClientAuth{
@@ -362,7 +349,6 @@ func TestClientUnsupportedCipher(t *testing.T) {
func TestClientUnsupportedKex(t *testing.T) {
kc := new(keychain)
kc.keys = append(kc.keys, rsakey)
config := &ClientConfig{
User: "testuser",
Auth: []ClientAuth{
-14
View File
@@ -6,7 +6,6 @@ package ssh
import (
"crypto"
"crypto/elliptic"
"errors"
"fmt"
"math/big"
@@ -223,19 +222,6 @@ func (c *CryptoConfig) macs() []string {
return c.MACs
}
// ecHash returns the hash to match the given elliptic curve, see RFC
// 5656, section 6.2.1
func ecHash(curve elliptic.Curve) crypto.Hash {
bitSize := curve.Params().BitSize
switch {
case bitSize <= 256:
return crypto.SHA256
case bitSize <= 384:
return crypto.SHA384
}
return crypto.SHA512
}
// serialize a signed slice according to RFC 4254 6.6. The name should
// be a key type name, rather than a cert type name.
func serializeSignature(name string, sig []byte) []byte {
+6 -2
View File
@@ -23,14 +23,18 @@ func ExampleListen() {
},
}
pemBytes, err := ioutil.ReadFile("id_rsa")
privateBytes, err := ioutil.ReadFile("id_rsa")
if err != nil {
panic("Failed to load private key")
}
if err = config.SetRSAPrivateKey(pemBytes); err != nil {
private, err := ParsePrivateKey(privateBytes)
if err != nil {
panic("Failed to parse private key")
}
config.AddHostKey(private)
// Once a ServerConfig has been configured, connections can be
// accepted.
listener, err := Listen("tcp", "0.0.0.0:2022", config)
+166 -46
View File
@@ -11,7 +11,12 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"io"
"math/big"
)
@@ -209,12 +214,17 @@ type PublicKey interface {
// Verify that sig is a signature on the given data using this
// key. This function will hash the data appropriately first.
Verify(data []byte, sigBlob []byte) bool
// RawKey returns the underlying object, eg. *rsa.PublicKey.
RawKey() interface{}
}
// TODO(hanwen): define PrivateKey too.
// A Signer is can create signatures that verify against a public key.
type Signer interface {
// PublicKey returns an associated PublicKey instance.
PublicKey() PublicKey
// Sign returns raw signature for the given data. This method
// will apply the hash specified for the keytype to the data.
Sign(rand io.Reader, data []byte) ([]byte, error)
}
type rsaPublicKey rsa.PublicKey
@@ -223,11 +233,7 @@ func (r *rsaPublicKey) PrivateKeyAlgo() string {
}
func (r *rsaPublicKey) PublicKeyAlgo() string {
return "ssh-rsa"
}
func (r *rsaPublicKey) RawKey() interface{} {
return (*rsa.PublicKey)(r)
return r.PrivateKeyAlgo()
}
// parseRSA parses an RSA key according to RFC 4253, section 6.6.
@@ -250,7 +256,7 @@ func parseRSA(in []byte) (out PublicKey, rest []byte, ok bool) {
}
ok = true
return NewRSAPublicKey(key), in, ok
return (*rsaPublicKey)(key), in, ok
}
func (r *rsaPublicKey) Marshal() []byte {
@@ -273,8 +279,19 @@ func (r *rsaPublicKey) Verify(data []byte, sig []byte) bool {
return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig) == nil
}
func NewRSAPublicKey(k *rsa.PublicKey) PublicKey {
return (*rsaPublicKey)(k)
type rsaPrivateKey struct {
*rsa.PrivateKey
}
func (r *rsaPrivateKey) PublicKey() PublicKey {
return (*rsaPublicKey)(&r.PrivateKey.PublicKey)
}
func (r *rsaPrivateKey) Sign(rand io.Reader, data []byte) ([]byte, error) {
h := crypto.SHA1.New()
h.Write(data)
digest := h.Sum(nil)
return rsa.SignPKCS1v15(rand, r.PrivateKey, crypto.SHA1, digest)
}
type dsaPublicKey dsa.PublicKey
@@ -282,11 +299,9 @@ type dsaPublicKey dsa.PublicKey
func (r *dsaPublicKey) PrivateKeyAlgo() string {
return "ssh-dss"
}
func (r *dsaPublicKey) PublicKeyAlgo() string {
return "ssh-dss"
}
func (r *dsaPublicKey) RawKey() interface{} {
return (*dsa.PublicKey)(r)
return r.PrivateKeyAlgo()
}
// parseDSA parses an DSA key according to RFC 4253, section 6.6.
@@ -310,7 +325,7 @@ func parseDSA(in []byte) (out PublicKey, rest []byte, ok bool) {
}
ok = true
return NewDSAPublicKey(key), in, ok
return (*dsaPublicKey)(key), in, ok
}
func (r *dsaPublicKey) Marshal() []byte {
@@ -347,21 +362,33 @@ func (k *dsaPublicKey) Verify(data []byte, sigBlob []byte) bool {
return dsa.Verify((*dsa.PublicKey)(k), digest, r, s)
}
func NewDSAPublicKey(k *dsa.PublicKey) PublicKey {
return (*dsaPublicKey)(k)
type dsaPrivateKey struct {
*dsa.PrivateKey
}
func (k *dsaPrivateKey) PublicKey() PublicKey {
return (*dsaPublicKey)(&k.PrivateKey.PublicKey)
}
func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) ([]byte, error) {
h := crypto.SHA1.New()
h.Write(data)
digest := h.Sum(nil)
r, s, err := dsa.Sign(rand, k.PrivateKey, digest)
if err != nil {
return nil, err
}
sig := make([]byte, 40)
copy(sig[:20], r.Bytes())
copy(sig[20:], s.Bytes())
return sig, nil
}
type ecdsaPublicKey ecdsa.PublicKey
func NewECDSAPublicKey(k *ecdsa.PublicKey) PublicKey {
return (*ecdsaPublicKey)(k)
}
func (r *ecdsaPublicKey) RawKey() interface{} {
return (*ecdsa.PublicKey)(r)
}
func (key *ecdsaPublicKey) PrivateKeyAlgo() string {
return "ecdh-sha2-" + key.nistID()
return "ecdsa-sha2-" + key.nistID()
}
func (key *ecdsaPublicKey) nistID() string {
@@ -376,29 +403,25 @@ func (key *ecdsaPublicKey) nistID() string {
panic("ssh: unsupported ecdsa key size")
}
// RFC 5656, section 6.2.1 (for ECDSA).
func (key *ecdsaPublicKey) hash() crypto.Hash {
switch key.Params().BitSize {
case 256:
func supportedEllipticCurve(curve elliptic.Curve) bool {
return (curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521())
}
// ecHash returns the hash to match the given elliptic curve, see RFC
// 5656, section 6.2.1
func ecHash(curve elliptic.Curve) crypto.Hash {
bitSize := curve.Params().BitSize
switch {
case bitSize <= 256:
return crypto.SHA256
case 384:
case bitSize <= 384:
return crypto.SHA384
case 521:
return crypto.SHA512
}
panic("ssh: unsupported ecdsa key size")
return crypto.SHA512
}
func (key *ecdsaPublicKey) PublicKeyAlgo() string {
switch key.Params().BitSize {
case 256:
return KeyAlgoECDSA256
case 384:
return KeyAlgoECDSA384
case 521:
return KeyAlgoECDSA521
}
panic("ssh: unsupported ecdsa key size")
return key.PrivateKeyAlgo()
}
// parseECDSA parses an ECDSA key according to RFC 5656, section 3.1.
@@ -432,7 +455,7 @@ func parseECDSA(in []byte) (out PublicKey, rest []byte, ok bool) {
ok = false
return
}
return NewECDSAPublicKey(key), in, ok
return (*ecdsaPublicKey)(key), in, ok
}
func (key *ecdsaPublicKey) Marshal() []byte {
@@ -450,7 +473,7 @@ func (key *ecdsaPublicKey) Marshal() []byte {
}
func (key *ecdsaPublicKey) Verify(data []byte, sigBlob []byte) bool {
h := key.hash().New()
h := ecHash(key.Curve).New()
h.Write(data)
digest := h.Sum(nil)
@@ -468,3 +491,100 @@ func (key *ecdsaPublicKey) Verify(data []byte, sigBlob []byte) bool {
}
return ecdsa.Verify((*ecdsa.PublicKey)(key), digest, r, s)
}
type ecdsaPrivateKey struct {
*ecdsa.PrivateKey
}
func (k *ecdsaPrivateKey) PublicKey() PublicKey {
return (*ecdsaPublicKey)(&k.PrivateKey.PublicKey)
}
func (k *ecdsaPrivateKey) Sign(rand io.Reader, data []byte) ([]byte, error) {
h := ecHash(k.PrivateKey.PublicKey.Curve).New()
h.Write(data)
digest := h.Sum(nil)
r, s, err := ecdsa.Sign(rand, k.PrivateKey, digest)
if err != nil {
return nil, err
}
sig := make([]byte, intLength(r)+intLength(s))
rest := marshalInt(sig, r)
marshalInt(rest, s)
return sig, nil
}
// NewPrivateKey takes a pointer to rsa, dsa or ecdsa PrivateKey
// returns a corresponding Signer instance. EC keys should use P256,
// P384 or P521.
func NewSignerFromKey(k interface{}) (Signer, error) {
var sshKey Signer
switch t := k.(type) {
case *rsa.PrivateKey:
sshKey = &rsaPrivateKey{t}
case *dsa.PrivateKey:
sshKey = &dsaPrivateKey{t}
case *ecdsa.PrivateKey:
if !supportedEllipticCurve(t.Curve) {
return nil, errors.New("ssh: only P256, P384 and P521 EC keys are supported.")
}
sshKey = &ecdsaPrivateKey{t}
default:
return nil, fmt.Errorf("ssh: unsupported key type %T", k)
}
return sshKey, nil
}
// NewPublicKey takes a pointer to rsa, dsa or ecdsa PublicKey
// and returns a corresponding ssh PublicKey instance. EC keys should use P256, P384 or P521.
func NewPublicKey(k interface{}) (PublicKey, error) {
var sshKey PublicKey
switch t := k.(type) {
case *rsa.PublicKey:
sshKey = (*rsaPublicKey)(t)
case *ecdsa.PublicKey:
if !supportedEllipticCurve(t.Curve) {
return nil, errors.New("ssh: only P256, P384 and P521 EC keys are supported.")
}
sshKey = (*ecdsaPublicKey)(t)
case *dsa.PublicKey:
sshKey = (*dsaPublicKey)(t)
default:
return nil, fmt.Errorf("ssh: unsupported key type %T", k)
}
return sshKey, nil
}
// ParsePublicKey parses a PEM encoded private key. Currently, only
// PKCS#1, RSA and ECDSA private keys are supported.
func ParsePrivateKey(pemBytes []byte) (Signer, error) {
block, _ := pem.Decode(pemBytes)
if block == nil {
return nil, errors.New("ssh: no key found")
}
var rawkey interface{}
switch block.Type {
case "RSA PRIVATE KEY":
rsa, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
rawkey = rsa
case "EC PRIVATE KEY":
ec, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
return nil, err
}
rawkey = ec
// TODO(hanwen): find doc for format and implement PEM parsing
// for DSA keys.
default:
return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
}
return NewSignerFromKey(rawkey)
}
+113 -35
View File
@@ -1,60 +1,138 @@
package ssh
import (
"crypto"
"crypto/dsa"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"reflect"
"strings"
"testing"
)
func TestRSAMarshal(t *testing.T) {
k0 := &rsakey.PublicKey
k1 := NewRSAPublicKey(k0)
k2, rest, ok := ParsePublicKey(MarshalPublicKey(k1))
if !ok {
t.Errorf("could not parse back Blob output")
var ecdsaKey Signer
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)
}
if len(rest) > 0 {
t.Errorf("trailing junk in RSA Blob() output")
}
if !reflect.DeepEqual(k0, k2.RawKey().(*rsa.PublicKey)) {
t.Errorf("got %#v in roundtrip, want %#v", k2.RawKey(), k0)
panic("unknown key type")
}
func TestKeyMarshalParse(t *testing.T) {
keys := []Signer{rsaKey, dsaKey, ecdsaKey}
for _, priv := range keys {
pub := priv.PublicKey()
roundtrip, rest, ok := ParsePublicKey(MarshalPublicKey(pub))
if !ok {
t.Errorf("ParsePublicKey(%T) failed", pub)
}
if len(rest) > 0 {
t.Errorf("ParsePublicKey(%T): trailing junk", pub)
}
k1 := rawKey(pub)
k2 := rawKey(roundtrip)
if !reflect.DeepEqual(k1, k2) {
t.Errorf("got %#v in roundtrip, want %#v", k2, k1)
}
}
}
func TestRSAKeyVerify(t *testing.T) {
pub := NewRSAPublicKey(&rsakey.PublicKey)
data := []byte("sign me")
h := crypto.SHA1.New()
h.Write(data)
digest := h.Sum(nil)
sig, err := rsa.SignPKCS1v15(rand.Reader, rsakey, crypto.SHA1, digest)
func TestUnsupportedCurves(t *testing.T) {
raw, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
if err != nil {
t.Fatalf("SignPKCS1v15: %v", err)
t.Fatalf("GenerateKey: %v", err)
}
if !pub.Verify(data, sig) {
t.Errorf("publicKey.Verify failed")
if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P256") {
t.Fatalf("NewPrivateKey should not succeed with P224, got: %v", err)
}
if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P256") {
t.Fatalf("NewPublicKey should not succeed with P224, got: %v", err)
}
}
func TestDSAMarshal(t *testing.T) {
k0 := &dsakey.PublicKey
k1 := NewDSAPublicKey(k0)
k2, rest, ok := ParsePublicKey(MarshalPublicKey(k1))
func TestNewPublicKey(t *testing.T) {
keys := []Signer{rsaKey, dsaKey, ecdsaKey}
for _, k := range keys {
raw := rawKey(k.PublicKey())
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) {
keys := []Signer{rsaKey, dsaKey, ecdsaKey}
for _, priv := range keys {
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 !pub.Verify(data, sig) {
t.Errorf("publicKey.Verify(%T) failed", priv)
}
}
}
func TestParseRSAPrivateKey(t *testing.T) {
key, err := ParsePrivateKey([]byte(testServerPrivateKey))
if err != nil {
t.Fatalf("ParsePrivateKey: %v", err)
}
rsa, ok := key.(*rsaPrivateKey)
if !ok {
t.Errorf("could not parse back Blob output")
t.Fatalf("got %T, want *rsa.PrivateKey", rsa)
}
if len(rest) > 0 {
t.Errorf("trailing junk in DSA Blob() output")
}
if !reflect.DeepEqual(k0, k2.RawKey().(*dsa.PublicKey)) {
t.Errorf("got %#v in roundtrip, want %#v", k2.RawKey(), k0)
if err := rsa.Validate(); err != nil {
t.Errorf("Validate: %v", err)
}
}
// TODO(hanwen): test for ECDSA marshal.
func TestParseECPrivateKey(t *testing.T) {
// Taken from the data in test/ .
pem := []byte(`-----BEGIN EC PRIVATE KEY-----
MHcCAQEEINGWx0zo6fhJ/0EAfrPzVFyFC9s18lBt3cRoEDhS3ARooAoGCCqGSM49
AwEHoUQDQgAEi9Hdw6KvZcWxfg2IDhA7UkpDtzzt6ZqJXSsFdLd+Kx4S3Sx4cVO+
6/ZOXRnPmNAlLUqjShUsUBBngG0u2fqEqA==
-----END EC PRIVATE KEY-----`)
key, err := ParsePrivateKey(pem)
if err != nil {
t.Fatalf("ParsePrivateKey: %v", err)
}
ecKey, ok := key.(*ecdsaPrivateKey)
if !ok {
t.Fatalf("got %T, want *ecdsaPrivateKey", ecKey)
}
if !validateECPublicKey(ecKey.Curve, ecKey.X, ecKey.Y) {
t.Fatalf("public key does not validate.")
}
}
func init() {
raw, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
ecdsaKey, _ = NewSignerFromKey(raw)
}
+50 -45
View File
@@ -10,10 +10,7 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/binary"
"encoding/pem"
"errors"
"io"
"math/big"
@@ -24,11 +21,7 @@ import (
)
type ServerConfig struct {
rsa *rsa.PrivateKey
// rsaSerialized is the serialized form of the public key that
// corresponds to the private key held in the rsa field.
rsaSerialized []byte
hostKeys []Signer
// Rand provides the source of entropy for key exchange. If Rand is
// nil, the cryptographic random reader in package crypto/rand will
@@ -69,21 +62,29 @@ func (c *ServerConfig) rand() io.Reader {
return c.Rand
}
// AddHostKey adds a private key as a host key. If an existing host
// key exists with the same algorithm, it is overwritten.
func (s *ServerConfig) AddHostKey(key Signer) {
for i, k := range s.hostKeys {
if k.PublicKey().PublicKeyAlgo() == key.PublicKey().PublicKeyAlgo() {
s.hostKeys[i] = key
return
}
}
s.hostKeys = append(s.hostKeys, key)
}
// SetRSAPrivateKey sets the private key for a Server. A Server must have a
// private key configured in order to accept connections. The private key must
// be in the form of a PEM encoded, PKCS#1, RSA private key. The file "id_rsa"
// typically contains such a key.
func (s *ServerConfig) SetRSAPrivateKey(pemBytes []byte) error {
block, _ := pem.Decode(pemBytes)
if block == nil {
return errors.New("ssh: no key found")
}
rsa, err := x509.ParsePKCS1PrivateKey(block.Bytes)
priv, err := ParsePrivateKey(pemBytes)
if err != nil {
return err
}
s.rsa = rsa
s.rsaSerialized = MarshalPublicKey(NewRSAPublicKey(&rsa.PublicKey))
s.AddHostKey(priv)
return nil
}
@@ -141,7 +142,7 @@ func Server(c net.Conn, config *ServerConfig) *ServerConn {
// kexECDH performs Elliptic Curve Diffie-Hellman key agreement on a
// ServerConnection, as documented in RFC 5656, section 4.
func (s *ServerConn) kexECDH(curve elliptic.Curve, magics *handshakeMagics, hostKeyAlgo string) (result *kexResult, err error) {
func (s *ServerConn) kexECDH(curve elliptic.Curve, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
packet, err := s.readPacket()
if err != nil {
return
@@ -169,7 +170,7 @@ func (s *ServerConn) kexECDH(curve elliptic.Curve, magics *handshakeMagics, host
return nil, err
}
hostKeyBytes := s.config.rsaSerialized
hostKeyBytes := MarshalPublicKey(priv.PublicKey())
serializedEphKey := elliptic.Marshal(curve, ephKey.PublicKey.X, ephKey.PublicKey.Y)
@@ -192,7 +193,9 @@ func (s *ServerConn) kexECDH(curve elliptic.Curve, magics *handshakeMagics, host
H := h.Sum(nil)
sig, err := s.serializedHostKeySignature(hostKeyAlgo, H)
// H is already a hash, but the hostkey signing will apply its
// own key specific hash algorithm.
sig, err := signAndMarshal(priv, s.config.rand(), H)
if err != nil {
return nil, err
}
@@ -247,7 +250,7 @@ func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool {
}
// kexDH performs Diffie-Hellman key agreement on a ServerConnection.
func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handshakeMagics, hostKeyAlgo string) (result *kexResult, err error) {
func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
packet, err := s.readPacket()
if err != nil {
return
@@ -268,7 +271,7 @@ func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha
return nil, err
}
hostKeyBytes := s.config.rsaSerialized
hostKeyBytes := MarshalPublicKey(priv.PublicKey())
h := hashFunc.New()
writeString(h, magics.clientVersion)
@@ -285,7 +288,9 @@ func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha
H := h.Sum(nil)
sig, err := s.serializedHostKeySignature(hostKeyAlgo, H)
// H is already a hash, but the hostkey signing will apply its
// own key specific hash algorithm.
sig, err := signAndMarshal(priv, s.config.rand(), H)
if err != nil {
return nil, err
}
@@ -306,25 +311,15 @@ func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha
}, nil
}
// serializedHostKeySignature signs the hashed data, and serializes
// the signature according to SSH conventions.
func (s *ServerConn) serializedHostKeySignature(hostKeyAlgo string, hashed []byte) ([]byte, error) {
var sig []byte
switch hostKeyAlgo {
case hostAlgoRSA:
hashFunc := crypto.SHA1
hh := hashFunc.New()
hh.Write(hashed)
var err error
sig, err = rsa.SignPKCS1v15(s.config.rand(), s.config.rsa, hashFunc, hh.Sum(nil))
if err != nil {
return nil, err
}
default:
return nil, errors.New("ssh: internal error")
// signAndMarshal signs the data with the appropriate algorithm,
// and serializes the result in SSH wire format.
func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) {
sig, err := k.Sign(rand, data)
if err != nil {
return nil, err
}
return serializeSignature(hostKeyAlgo, sig), nil
return serializeSignature(k.PublicKey().PrivateKeyAlgo(), sig), nil
}
// serverVersion is the fixed identification string that Server will use.
@@ -374,7 +369,6 @@ func (s *ServerConn) Handshake() (err error) {
func (s *ServerConn) clientInitHandshake(clientKexInit *kexInitMsg, clientKexInitPacket []byte) (err error) {
serverKexInit := kexInitMsg{
KexAlgos: s.config.Crypto.kexes(),
ServerHostKeyAlgos: supportedHostKeyAlgos,
CiphersClientServer: s.config.Crypto.ciphers(),
CiphersServerClient: s.config.Crypto.ciphers(),
MACsClientServer: s.config.Crypto.macs(),
@@ -382,8 +376,12 @@ func (s *ServerConn) clientInitHandshake(clientKexInit *kexInitMsg, clientKexIni
CompressionClientServer: supportedCompressions,
CompressionServerClient: supportedCompressions,
}
serverKexInitPacket := marshal(msgKexInit, serverKexInit)
for _, k := range s.config.hostKeys {
serverKexInit.ServerHostKeyAlgos = append(
serverKexInit.ServerHostKeyAlgos, k.PublicKey().PublicKeyAlgo())
}
serverKexInitPacket := marshal(msgKexInit, serverKexInit)
if err = s.writePacket(serverKexInitPacket); err != nil {
return
}
@@ -410,6 +408,13 @@ func (s *ServerConn) clientInitHandshake(clientKexInit *kexInitMsg, clientKexIni
}
}
var hostKey Signer
for _, k := range s.config.hostKeys {
if hostKeyAlgo == k.PublicKey().PublicKeyAlgo() {
hostKey = k
}
}
var magics handshakeMagics
magics.serverVersion = serverVersion[:len(serverVersion)-2]
magics.clientVersion = s.ClientVersion
@@ -419,17 +424,17 @@ func (s *ServerConn) clientInitHandshake(clientKexInit *kexInitMsg, clientKexIni
var result *kexResult
switch kexAlgo {
case kexAlgoECDH256:
result, err = s.kexECDH(elliptic.P256(), &magics, hostKeyAlgo)
result, err = s.kexECDH(elliptic.P256(), &magics, hostKey)
case kexAlgoECDH384:
result, err = s.kexECDH(elliptic.P384(), &magics, hostKeyAlgo)
result, err = s.kexECDH(elliptic.P384(), &magics, hostKey)
case kexAlgoECDH521:
result, err = s.kexECDH(elliptic.P521(), &magics, hostKeyAlgo)
result, err = s.kexECDH(elliptic.P521(), &magics, hostKey)
case kexAlgoDH14SHA1:
dhGroup14Once.Do(initDHGroup14)
result, err = s.kexDH(dhGroup14, crypto.SHA1, &magics, hostKeyAlgo)
result, err = s.kexDH(dhGroup14, crypto.SHA1, &magics, hostKey)
case kexAlgoDH1SHA1:
dhGroup1Once.Do(initDHGroup1)
result, err = s.kexDH(dhGroup1, crypto.SHA1, &magics, hostKeyAlgo)
result, err = s.kexDH(dhGroup1, crypto.SHA1, &magics, hostKey)
default:
err = errors.New("ssh: unexpected key exchange algorithm " + kexAlgo)
}
+3 -9
View File
@@ -1,8 +1,6 @@
package test
import (
"crypto/x509"
"encoding/pem"
"reflect"
"strings"
"testing"
@@ -171,16 +169,12 @@ func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []authResult) {
}
func getTestPublicKey(t *testing.T) ssh.PublicKey {
block, _ := pem.Decode([]byte(testClientPrivateKey))
if block == nil {
t.Fatalf("pem.Decode: %v", testClientPrivateKey)
}
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
priv, err := ssh.ParsePrivateKey([]byte(testClientPrivateKey))
if err != nil {
t.Fatalf("x509.ParsePKCS1PrivateKey: %v", err)
t.Fatalf("ParsePrivateKey: %v", err)
}
return ssh.NewRSAPublicKey(&priv.PublicKey)
return priv.PublicKey()
}
func TestAuth(t *testing.T) {
+22 -47
View File
@@ -10,12 +10,7 @@ package test
import (
"bytes"
"crypto"
"crypto/dsa"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
@@ -53,25 +48,23 @@ HostbasedAuthentication no
`
var (
configTmpl template.Template
rsakey *rsa.PrivateKey
serializedHostKey []byte
configTmpl template.Template
privateKey ssh.Signer
hostKey ssh.Signer
)
func init() {
template.Must(configTmpl.Parse(sshd_config))
block, _ := pem.Decode([]byte(testClientPrivateKey))
rsakey, _ = x509.ParsePKCS1PrivateKey(block.Bytes)
block, _ = pem.Decode([]byte(keys["ssh_host_rsa_key"]))
if block == nil {
panic("pem.Decode ssh_host_rsa_key")
}
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
var err error
hostKey, err = ssh.ParsePrivateKey([]byte(keys["ssh_host_rsa_key"]))
if err != nil {
panic("ParsePKCS1PrivateKey: " + err.Error())
panic("ParsePrivateKey: " + err.Error())
}
privateKey, err = ssh.ParsePrivateKey([]byte(testClientPrivateKey))
if err != nil {
panic("ParsePrivateKey: " + err.Error())
}
serializedHostKey = ssh.MarshalPublicKey(ssh.NewRSAPublicKey(&priv.PublicKey))
}
type server struct {
@@ -106,26 +99,26 @@ type storedHostKey struct {
keys map[string][]byte
}
func (k *storedHostKey) Add(algo string, public []byte) {
func (k *storedHostKey) Add(key ssh.PublicKey) {
if k.keys == nil {
k.keys = map[string][]byte{}
}
k.keys[algo] = append([]byte(nil), public...)
k.keys[key.PublicKeyAlgo()] = append([]byte(nil), ssh.MarshalPublicKey(key)...)
}
func (k *storedHostKey) Check(addr string, remote net.Addr, algo string, key []byte) error {
if k.keys == nil || bytes.Compare(key, k.keys[algo]) != 0 {
return errors.New("host key mismatch")
return fmt.Errorf("host key mismatch. Got %q, want %q", key, k.keys[algo])
}
return nil
}
func clientConfig() *ssh.ClientConfig {
keyChecker := storedHostKey{}
keyChecker.Add("ssh-rsa", serializedHostKey)
keyChecker.Add(hostKey.PublicKey())
kc := new(keychain)
kc.keys = append(kc.keys, rsakey)
kc.keys = append(kc.keys, privateKey)
config := &ssh.ClientConfig{
User: username(),
Auth: []ssh.ClientAuth{
@@ -261,34 +254,20 @@ func newServer(t *testing.T) *server {
}
}
// keychain implements the ClientKeyring interface
// keychain implements the ClientKeyring interface.
type keychain struct {
keys []interface{}
keys []ssh.Signer
}
func (k *keychain) Key(i int) (ssh.PublicKey, error) {
if i < 0 || i >= len(k.keys) {
return nil, nil
}
switch key := k.keys[i].(type) {
case *rsa.PrivateKey:
return ssh.NewRSAPublicKey(&key.PublicKey), nil
case *dsa.PrivateKey:
return ssh.NewDSAPublicKey(&key.PublicKey), nil
}
panic("unknown key type")
return k.keys[i].PublicKey(), nil
}
func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) {
hashFunc := crypto.SHA1
h := hashFunc.New()
h.Write(data)
digest := h.Sum(nil)
switch key := k.keys[i].(type) {
case *rsa.PrivateKey:
return rsa.SignPKCS1v15(rand, key, hashFunc, digest)
}
return nil, errors.New("ssh: unknown key type")
return k.keys[i].Sign(rand, data)
}
func (k *keychain) loadPEM(file string) error {
@@ -296,14 +275,10 @@ func (k *keychain) loadPEM(file string) error {
if err != nil {
return err
}
block, _ := pem.Decode(buf)
if block == nil {
return errors.New("ssh: no key found")
}
r, err := x509.ParsePKCS1PrivateKey(block.Bytes)
key, err := ssh.ParsePrivateKey(buf)
if err != nil {
return err
}
k.keys = append(k.keys, r)
k.keys = append(k.keys, key)
return nil
}