Commit 0d859f32 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

go.crypto/ssh: separate kex algorithms into kexAlgorithm class.

Adds readPacket() to conn, and renames conn to packetConn.
Key exchanges operate on packetConn, so they can be
unittested.

R=agl, jpsugar, dave
CC=golang-dev
https://codereview.appspot.com/13352055

Committer: Adam Langley <agl@golang.org>
parent 40b64dbf
......@@ -78,7 +78,7 @@ const (
)
type channel struct {
conn // the underlying transport
packetConn // the underlying transport
localId, remoteId uint32
remoteWin window
maxPacket uint32
......@@ -102,7 +102,7 @@ func (c *channel) sendEOF() error {
// sendClose informs the remote side of our intent to close the channel.
func (c *channel) sendClose() error {
return c.conn.writePacket(marshal(msgChannelClose, channelCloseMsg{
return c.packetConn.writePacket(marshal(msgChannelClose, channelCloseMsg{
PeersId: c.remoteId,
}))
}
......@@ -124,7 +124,7 @@ func (c *channel) writePacket(b []byte) error {
if uint32(len(b)) > c.maxPacket {
return fmt.Errorf("ssh: cannot write %d bytes, maxPacket is %d bytes", len(b), c.maxPacket)
}
return c.conn.writePacket(b)
return c.packetConn.writePacket(b)
}
func (c *channel) closed() bool {
......@@ -447,12 +447,12 @@ type clientChan struct {
// newClientChan returns a partially constructed *clientChan
// using the local id provided. To be usable clientChan.remoteId
// needs to be assigned once known.
func newClientChan(cc conn, id uint32) *clientChan {
func newClientChan(cc packetConn, id uint32) *clientChan {
c := &clientChan{
channel: channel{
conn: cc,
localId: id,
remoteWin: window{Cond: newCond()},
packetConn: cc,
localId: id,
remoteWin: window{Cond: newCond()},
},
msg: make(chan interface{}, 16),
}
......
......@@ -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 @@ func clientWithAddress(c net.Conn, addr string, config *ClientConfig) (*ClientCo
// handshake performs the client side key exchange. See RFC 4253 Section 7.
func (c *ClientConn) handshake() error {
var magics handshakeMagics
var version []byte
var myVersion []byte
if len(c.config.ClientVersion) > 0 {
version = []byte(c.config.ClientVersion)
myVersion = []byte(c.config.ClientVersion)
} else {
version = clientVersion
myVersion = 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 @@ func (c *ClientConn) handshake() error {
}
// 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 @@ func (c *ClientConn) handshake() error {
CompressionServerClient: supportedCompressions,
}
kexInitPacket := marshal(msgKexInit, clientKexInit)
magics.clientKexInit = kexInitPacket
if err := c.writePacket(kexInitPacket); err != nil {
return err
}
......@@ -108,8 +99,6 @@ func (c *ClientConn) handshake() error {
return err
}
magics.serverKexInit = packet
var serverKexInit kexInitMsg
if err = unmarshal(&serverKexInit, packet, msgKexInit); err != nil {
return err
......@@ -128,23 +117,18 @@ func (c *ClientConn) handshake() error {
}
}
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 @@ func (c *ClientConn) handshake() error {
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 @@ func (c *ClientConn) handshake() error {
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 @@ func verifyHostKeySignature(hostKeyAlgo string, hostKeyBytes []byte, data []byte
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 @@ type chanList struct {
}
// 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
}
......
......@@ -81,7 +81,7 @@ type ClientAuth interface {
// Returns true if authentication is successful.
// If authentication is not successful, a []string of alternative
// method names is returned.
auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error)
auth(session []byte, user string, p packetConn, rand io.Reader) (bool, []string, error)
// method returns the RFC 4252 method name.
method() string
......@@ -90,8 +90,8 @@ type ClientAuth interface {
// "none" authentication, RFC 4252 section 5.2.
type noneAuth int
func (n *noneAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) {
if err := t.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{
func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
if err := c.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{
User: user,
Service: serviceSSH,
Method: "none",
......@@ -99,7 +99,7 @@ func (n *noneAuth) auth(session []byte, user string, t *transport, rand io.Reade
return false, nil, err
}
return handleAuthResponse(t)
return handleAuthResponse(c)
}
func (n *noneAuth) method() string {
......@@ -111,7 +111,7 @@ type passwordAuth struct {
ClientPassword
}
func (p *passwordAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) {
func (p *passwordAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
type passwordAuthMsg struct {
User string
Service string
......@@ -125,7 +125,7 @@ func (p *passwordAuth) auth(session []byte, user string, t *transport, rand io.R
return false, nil, err
}
if err := t.writePacket(marshal(msgUserAuthRequest, passwordAuthMsg{
if err := c.writePacket(marshal(msgUserAuthRequest, passwordAuthMsg{
User: user,
Service: serviceSSH,
Method: "password",
......@@ -135,7 +135,7 @@ func (p *passwordAuth) auth(session []byte, user string, t *transport, rand io.R
return false, nil, err
}
return handleAuthResponse(t)
return handleAuthResponse(c)
}
func (p *passwordAuth) method() string {
......@@ -181,7 +181,7 @@ type publickeyAuthMsg struct {
Sig []byte `ssh:"rest"`
}
func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) {
func (p *publickeyAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
// Authentication is performed in two stages. The first stage sends an
// enquiry to test if each key is acceptable to the remote. The second
// stage attempts to authenticate with the valid keys obtained in the
......@@ -200,7 +200,7 @@ func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.
break
}
if ok, err := p.validateKey(key, user, t); ok {
if ok, err := p.validateKey(key, user, c); ok {
validKeys[index] = key
} else {
if err != nil {
......@@ -237,10 +237,10 @@ func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.
Sig: sig,
}
p := marshal(msgUserAuthRequest, msg)
if err := t.writePacket(p); err != nil {
if err := c.writePacket(p); err != nil {
return false, nil, err
}
success, methods, err := handleAuthResponse(t)
success, methods, err := handleAuthResponse(c)
if err != nil {
return false, nil, err
}
......@@ -252,7 +252,7 @@ func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.
}
// validateKey validates the key provided it is acceptable to the server.
func (p *publickeyAuth) validateKey(key PublicKey, user string, t *transport) (bool, error) {
func (p *publickeyAuth) validateKey(key PublicKey, user string, c packetConn) (bool, error) {
pubkey := MarshalPublicKey(key)
algoname := key.PublicKeyAlgo()
msg := publickeyAuthMsg{
......@@ -263,19 +263,19 @@ func (p *publickeyAuth) validateKey(key PublicKey, user string, t *transport) (b
Algoname: algoname,
Pubkey: string(pubkey),
}
if err := t.writePacket(marshal(msgUserAuthRequest, msg)); err != nil {
if err := c.writePacket(marshal(msgUserAuthRequest, msg)); err != nil {
return false, err
}
return p.confirmKeyAck(key, t)
return p.confirmKeyAck(key, c)
}
func (p *publickeyAuth) confirmKeyAck(key PublicKey, t *transport) (bool, error) {
func (p *publickeyAuth) confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
pubkey := MarshalPublicKey(key)
algoname := key.PublicKeyAlgo()
for {
packet, err := t.readPacket()
packet, err := c.readPacket()
if err != nil {
return false, err
}
......@@ -312,9 +312,9 @@ func ClientAuthKeyring(impl ClientKeyring) ClientAuth {
// handleAuthResponse returns whether the preceding authentication request succeeded
// along with a list of remaining authentication methods to try next and
// an error if an unexpected response was received.
func handleAuthResponse(t *transport) (bool, []string, error) {
func handleAuthResponse(c packetConn) (bool, []string, error) {
for {
packet, err := t.readPacket()
packet, err := c.readPacket()
if err != nil {
return false, nil, err
}
......@@ -411,11 +411,11 @@ type keyboardInteractiveAuth struct {
ClientKeyboardInteractive
}
func (c *keyboardInteractiveAuth) method() string {
func (k *keyboardInteractiveAuth) method() string {
return "keyboard-interactive"
}
func (c *keyboardInteractiveAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) {
func (k *keyboardInteractiveAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
type initiateMsg struct {
User string
Service string
......@@ -424,7 +424,7 @@ func (c *keyboardInteractiveAuth) auth(session []byte, user string, t *transport
Submethods string
}
if err := t.writePacket(marshal(msgUserAuthRequest, initiateMsg{
if err := c.writePacket(marshal(msgUserAuthRequest, initiateMsg{
User: user,
Service: serviceSSH,
Method: "keyboard-interactive",
......@@ -433,7 +433,7 @@ func (c *keyboardInteractiveAuth) auth(session []byte, user string, t *transport
}
for {
packet, err := t.readPacket()
packet, err := c.readPacket()
if err != nil {
return false, nil, err
}
......@@ -480,7 +480,7 @@ func (c *keyboardInteractiveAuth) auth(session []byte, user string, t *transport
return false, nil, fmt.Errorf("ssh: junk following message %q", rest)
}
answers, err := c.Challenge(msg.User, msg.Instruction, prompts, echos)
answers, err := k.Challenge(msg.User, msg.Instruction, prompts, echos)
if err != nil {
return false, nil, err
}
......@@ -501,7 +501,7 @@ func (c *keyboardInteractiveAuth) auth(session []byte, user string, t *transport
p = marshalString(p, []byte(a))
}
if err := t.writePacket(serialized); err != nil {
if err := c.writePacket(serialized); err != nil {
return false, nil, err
}
}
......
......@@ -6,9 +6,7 @@ package ssh
import (
"crypto"
"errors"
"fmt"
"math/big"
"sync"
_ "crypto/sha1"
......@@ -18,11 +16,6 @@ import (
// These are string constants in the SSH protocol.
const (
kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
kexAlgoECDH256 = "ecdh-sha2-nistp256"
kexAlgoECDH384 = "ecdh-sha2-nistp384"
kexAlgoECDH521 = "ecdh-sha2-nistp521"
hostAlgoRSA = "ssh-rsa"
hostAlgoDSA = "ssh-dss"
compressionNone = "none"
......@@ -53,48 +46,6 @@ var hashFuncs = map[string]crypto.Hash{
CertAlgoECDSA521v01: crypto.SHA512,
}
// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement.
type dhGroup struct {
g, p *big.Int
}
func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) {
if theirPublic.Sign() <= 0 || theirPublic.Cmp(group.p) >= 0 {
return nil, errors.New("ssh: DH parameter out of bounds")
}
return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil
}
// dhGroup1 is the group called diffie-hellman-group1-sha1 in RFC 4253 and
// Oakley Group 2 in RFC 2409.
var dhGroup1 *dhGroup
var dhGroup1Once sync.Once
func initDHGroup1() {
p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
dhGroup1 = &dhGroup{
g: new(big.Int).SetInt64(2),
p: p,
}
}
// dhGroup14 is the group called diffie-hellman-group14-sha1 in RFC 4253 and
// Oakley Group 14 in RFC 3526.
var dhGroup14 *dhGroup
var dhGroup14Once sync.Once
func initDHGroup14() {
p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
dhGroup14 = &dhGroup{
g: new(big.Int).SetInt64(2),
p: p,
}
}
// UnexpectedMessageError results when the SSH message that we received didn't
// match what we wanted.
type UnexpectedMessageError struct {
......@@ -114,11 +65,6 @@ func (p ParseError) Error() string {
return fmt.Sprintf("ssh: parse error in message type %d", p.msgType)
}
type handshakeMagics struct {
clientVersion, serverVersion []byte
clientKexInit, serverKexInit []byte
}
func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo string, ok bool) {
for _, clientAlgo := range clientAlgos {
for _, serverAlgo := range serverAlgos {
......
// Copyright 2013 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 (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"errors"
"io"
"math/big"
)
const (
kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
kexAlgoECDH256 = "ecdh-sha2-nistp256"
kexAlgoECDH384 = "ecdh-sha2-nistp384"
kexAlgoECDH521 = "ecdh-sha2-nistp521"
)
// 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
}
// handshakeMagics contains data that is always included in the
// session hash.
type handshakeMagics struct {
clientVersion, serverVersion []byte
clientKexInit, serverKexInit []byte
}
func (m *handshakeMagics) write(w io.Writer) {
writeString(w, m.clientVersion)
writeString(w, m.serverVersion)
writeString(w, m.clientKexInit)
writeString(w, m.serverKexInit)
}
// kexAlgorithm abstracts different key exchange algorithms.
type kexAlgorithm interface {
// Server runs server-side key agreement, signing the result
// with a hostkey.
Server(p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error)
// Client runs the client-side key agreement. Caller is
// responsible for verifying the host key signature.
Client(p packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error)
// Hash returns a cryptographic hash function that matches the
// security level of the key exchange algorithm. It is used
// for calculating kexResult.H, and for deriving keys from
// data in kexResult.
Hash() crypto.Hash
}
// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement.
type dhGroup struct {
g, p *big.Int
}
func (group *dhGroup) Hash() crypto.Hash {
return crypto.SHA1
}
func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) {
if theirPublic.Sign() <= 0 || theirPublic.Cmp(group.p) >= 0 {
return nil, errors.New("ssh: DH parameter out of bounds")
}
return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil
}
func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) {
hashFunc := crypto.SHA1
x, err := rand.Int(randSource, 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()
magics.write(h)
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,
}, nil
}
func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
hashFunc := crypto.SHA1
packet, err := c.readPacket()
if err != nil {
return
}
var kexDHInit kexDHInitMsg
if err = unmarshal(&kexDHInit, packet, msgKexDHInit); err != nil {
return
}
y, err := rand.Int(randSource, group.p)
if err != nil {
return
}
Y := new(big.Int).Exp(group.g, y, group.p)
kInt, err := group.diffieHellman(kexDHInit.X, y)
if err != nil {
return nil, err
}