Index: ssh/client.go |
=================================================================== |
--- a/ssh/client.go |
+++ b/ssh/client.go |
@@ -5,15 +5,11 @@ |
package ssh |
import ( |
- "crypto" |
- "crypto/ecdsa" |
- "crypto/elliptic" |
"crypto/rand" |
"encoding/binary" |
"errors" |
"fmt" |
"io" |
- "math/big" |
"net" |
"sync" |
) |
@@ -63,17 +59,14 @@ |
// handshake performs the client side key exchange. See RFC 4253 Section 7. |
func (c *ClientConn) handshake() error { |
- var magics handshakeMagics |
+ var myVersion []byte |
+ if len(c.config.ClientVersion) > 0 { |
+ myVersion = []byte(c.config.ClientVersion) |
+ } else { |
+ myVersion = clientVersion |
+ } |
- var version []byte |
- if len(c.config.ClientVersion) > 0 { |
- version = []byte(c.config.ClientVersion) |
- } else { |
- version = clientVersion |
- } |
- magics.clientVersion = version |
- version = append(version, '\r', '\n') |
- if _, err := c.Write(version); err != nil { |
+ if _, err := c.Write(append(myVersion, '\r', '\n')); err != nil { |
return err |
} |
if err := c.Flush(); err != nil { |
@@ -81,12 +74,12 @@ |
} |
// read remote server version |
- version, err := readVersion(c) |
+ serverVersion, err := readVersion(c) |
if err != nil { |
return err |
} |
- magics.serverVersion = version |
- c.serverVersion = string(version) |
+ c.serverVersion = string(serverVersion) |
+ |
clientKexInit := kexInitMsg{ |
KexAlgos: c.config.Crypto.kexes(), |
ServerHostKeyAlgos: supportedHostKeyAlgos, |
@@ -98,8 +91,6 @@ |
CompressionServerClient: supportedCompressions, |
} |
kexInitPacket := marshal(msgKexInit, clientKexInit) |
- magics.clientKexInit = kexInitPacket |
- |
if err := c.writePacket(kexInitPacket); err != nil { |
return err |
} |
@@ -108,8 +99,6 @@ |
return err |
} |
- magics.serverKexInit = packet |
- |
var serverKexInit kexInitMsg |
if err = unmarshal(&serverKexInit, packet, msgKexInit); err != nil { |
return err |
@@ -128,23 +117,18 @@ |
} |
} |
- var result *kexResult |
- switch kexAlgo { |
- case kexAlgoECDH256: |
- result, err = c.kexECDH(elliptic.P256(), &magics, hostKeyAlgo) |
- case kexAlgoECDH384: |
- result, err = c.kexECDH(elliptic.P384(), &magics, hostKeyAlgo) |
- case kexAlgoECDH521: |
- result, err = c.kexECDH(elliptic.P521(), &magics, hostKeyAlgo) |
- case kexAlgoDH14SHA1: |
- dhGroup14Once.Do(initDHGroup14) |
- result, err = c.kexDH(crypto.SHA1, dhGroup14, &magics, hostKeyAlgo) |
- case kexAlgoDH1SHA1: |
- dhGroup1Once.Do(initDHGroup1) |
- result, err = c.kexDH(crypto.SHA1, dhGroup1, &magics, hostKeyAlgo) |
- default: |
- err = fmt.Errorf("ssh: unexpected key exchange algorithm %v", kexAlgo) |
+ kex, ok := kexAlgoMap[kexAlgo] |
+ if !ok { |
+ return fmt.Errorf("ssh: unexpected key exchange algorithm %v", kexAlgo) |
} |
+ |
+ magics := handshakeMagics{ |
+ clientVersion: myVersion, |
+ serverVersion: serverVersion, |
+ clientKexInit: kexInitPacket, |
+ serverKexInit: packet, |
+ } |
+ result, err := kex.Client(c, c.config.rand(), &magics) |
if err != nil { |
return err |
} |
@@ -164,7 +148,8 @@ |
if err = c.writePacket([]byte{msgNewKeys}); err != nil { |
return err |
} |
- if err = c.transport.writer.setupKeys(clientKeys, result.K, result.H, result.H, result.Hash); err != nil { |
+ |
+ if err = c.transport.writer.setupKeys(clientKeys, result.K, result.H, result.H, kex.Hash()); err != nil { |
return err |
} |
if packet, err = c.readPacket(); err != nil { |
@@ -173,72 +158,12 @@ |
if packet[0] != msgNewKeys { |
return UnexpectedMessageError{msgNewKeys, packet[0]} |
} |
- if err := c.transport.reader.setupKeys(serverKeys, result.K, result.H, result.H, result.Hash); err != nil { |
+ if err := c.transport.reader.setupKeys(serverKeys, result.K, result.H, result.H, kex.Hash()); err != nil { |
return err |
} |
return c.authenticate(result.H) |
} |
-// kexECDH performs Elliptic Curve Diffie-Hellman key exchange as |
-// described in RFC 5656, section 4. |
-func (c *ClientConn) kexECDH(curve elliptic.Curve, magics *handshakeMagics, hostKeyAlgo string) (*kexResult, error) { |
- ephKey, err := ecdsa.GenerateKey(curve, c.config.rand()) |
- if err != nil { |
- return nil, err |
- } |
- |
- kexInit := kexECDHInitMsg{ |
- ClientPubKey: elliptic.Marshal(curve, ephKey.PublicKey.X, ephKey.PublicKey.Y), |
- } |
- |
- serialized := marshal(msgKexECDHInit, kexInit) |
- if err := c.writePacket(serialized); err != nil { |
- return nil, err |
- } |
- |
- packet, err := c.readPacket() |
- if err != nil { |
- return nil, err |
- } |
- |
- var reply kexECDHReplyMsg |
- if err = unmarshal(&reply, packet, msgKexECDHReply); err != nil { |
- return nil, err |
- } |
- |
- x, y := elliptic.Unmarshal(curve, reply.EphemeralPubKey) |
- if x == nil { |
- return nil, errors.New("ssh: elliptic.Unmarshal failure") |
- } |
- if !validateECPublicKey(curve, x, y) { |
- return nil, errors.New("ssh: ephemeral server key not on curve") |
- } |
- |
- // generate shared secret |
- secret, _ := curve.ScalarMult(x, y, ephKey.D.Bytes()) |
- |
- hashFunc := ecHash(curve) |
- h := hashFunc.New() |
- writeString(h, magics.clientVersion) |
- writeString(h, magics.serverVersion) |
- writeString(h, magics.clientKexInit) |
- writeString(h, magics.serverKexInit) |
- writeString(h, reply.HostKey) |
- writeString(h, kexInit.ClientPubKey) |
- writeString(h, reply.EphemeralPubKey) |
- K := make([]byte, intLength(secret)) |
- marshalInt(K, secret) |
- h.Write(K) |
- |
- return &kexResult{ |
- H: h.Sum(nil), |
- K: K, |
- HostKey: reply.HostKey, |
- Signature: reply.Signature, |
- Hash: hashFunc, |
- }, nil |
-} |
- |
// Verify the host key obtained in the key exchange. |
func verifyHostKeySignature(hostKeyAlgo string, hostKeyBytes []byte, data []byte, signature []byte) error { |
hostKey, rest, ok := ParsePublicKey(hostKeyBytes) |
@@ -260,74 +185,6 @@ |
return nil |
} |
-// kexResult captures the outcome of a key exchange. |
-type kexResult struct { |
- // Session hash. See also RFC 4253, section 8. |
- H []byte |
- |
- // Shared secret. See also RFC 4253, section 8. |
- K []byte |
- |
- // Host key as hashed into H |
- HostKey []byte |
- |
- // Signature of H |
- Signature []byte |
- |
- // Hash function that was used. |
- Hash crypto.Hash |
-} |
- |
-// kexDH performs Diffie-Hellman key agreement on a ClientConn. |
-func (c *ClientConn) kexDH(hashFunc crypto.Hash, group *dhGroup, magics *handshakeMagics, hostKeyAlgo string) (*kexResult, error) { |
- x, err := rand.Int(c.config.rand(), group.p) |
- if err != nil { |
- return nil, err |
- } |
- X := new(big.Int).Exp(group.g, x, group.p) |
- kexDHInit := kexDHInitMsg{ |
- X: X, |
- } |
- if err := c.writePacket(marshal(msgKexDHInit, kexDHInit)); err != nil { |
- return nil, err |
- } |
- |
- packet, err := c.readPacket() |
- if err != nil { |
- return nil, err |
- } |
- |
- var kexDHReply kexDHReplyMsg |
- if err = unmarshal(&kexDHReply, packet, msgKexDHReply); err != nil { |
- return nil, err |
- } |
- |
- kInt, err := group.diffieHellman(kexDHReply.Y, x) |
- if err != nil { |
- return nil, err |
- } |
- |
- h := hashFunc.New() |
- writeString(h, magics.clientVersion) |
- writeString(h, magics.serverVersion) |
- writeString(h, magics.clientKexInit) |
- writeString(h, magics.serverKexInit) |
- writeString(h, kexDHReply.HostKey) |
- writeInt(h, X) |
- writeInt(h, kexDHReply.Y) |
- K := make([]byte, intLength(kInt)) |
- marshalInt(K, kInt) |
- h.Write(K) |
- |
- return &kexResult{ |
- H: h.Sum(nil), |
- K: K, |
- HostKey: kexDHReply.HostKey, |
- Signature: kexDHReply.Signature, |
- Hash: hashFunc, |
- }, nil |
-} |
- |
// mainLoop reads incoming messages and routes channel messages |
// to their respective ClientChans. |
func (c *ClientConn) mainLoop() { |
@@ -633,18 +490,18 @@ |
} |
// Allocate a new ClientChan with the next avail local id. |
-func (c *chanList) newChan(t *transport) *clientChan { |
+func (c *chanList) newChan(p packetConn) *clientChan { |
c.Lock() |
defer c.Unlock() |
for i := range c.chans { |
if c.chans[i] == nil { |
- ch := newClientChan(t, uint32(i)) |
+ ch := newClientChan(p, uint32(i)) |
c.chans[i] = ch |
return ch |
} |
} |
i := len(c.chans) |
- ch := newClientChan(t, uint32(i)) |
+ ch := newClientChan(p, uint32(i)) |
c.chans = append(c.chans, ch) |
return ch |
} |