zgrab2/lib/ssh/kex.go

771 lines
21 KiB
Go

// 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"
"crypto/sha256"
"crypto/subtle"
"encoding/hex"
"encoding/json"
"errors"
"io"
"math/big"
ztoolsKeys "github.com/zmap/zgrab2/tools/keys"
"golang.org/x/crypto/curve25519"
)
const (
kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
kexAlgoECDH256 = "ecdh-sha2-nistp256"
kexAlgoECDH384 = "ecdh-sha2-nistp384"
kexAlgoECDH521 = "ecdh-sha2-nistp521"
kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org"
)
// kexResult captures the outcome of a key exchange.
type kexResult struct {
// Session hash. See also RFC 4253, section 8.
H []byte `json:"H,omitempty"`
// Shared secret. See also RFC 4253, section 8.
K []byte `json:"K,omitempty"`
// Host key as hashed into H.
HostKey []byte `json:"-"`
// Signature of H.
Signature []byte `json:"-"`
// A cryptographic hash function that matches the security
// level of the key exchange algorithm. It is used for
// calculating H, and for deriving keys from H and K.
Hash crypto.Hash `json:"-"`
// The session ID, which is the first H computed. This is used
// to derive key material inside the transport.
SessionID []byte `json:"session_id,omitempty"`
}
// 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)
}
type PublicKeyJsonLog struct {
RSAHostKey *PublicKey `json:"rsa_public_key,omitempty"`
DSAHostKey *PublicKey `json:"dsa_public_key,omitempty"`
ECDSAHostKey *PublicKey `json:"ecdsa_public_key,omitempty"`
Ed25519HostKey *PublicKey `json:"ed25519_public_key,omitempty"`
CertKeyHostKey *PublicKey `json:"certkey_public_key,omitempty"`
}
func (pkLog *PublicKeyJsonLog) AddPublicKey(pubKey PublicKey) bool {
switch pubKey.Type() {
case KeyAlgoRSA:
pkLog.RSAHostKey = &pubKey
return true
case KeyAlgoDSA:
pkLog.DSAHostKey = &pubKey
return true
case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
pkLog.ECDSAHostKey = &pubKey
return true
case KeyAlgoED25519:
pkLog.Ed25519HostKey = &pubKey
return true
case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01:
pkLog.CertKeyHostKey = &pubKey
return true
default:
return false
}
}
type ServerHostKeyJsonLog struct {
PublicKeyJsonLog
Raw []byte `json:"raw"`
Algorithm string `json:"algorithm"`
Fingerprint string `json:"fingerprint_sha256,omitempty"`
TrailingData []byte `json:"trailing_data,omitempty"`
ParseError string `json:"parse_error,omitempty"`
}
func LogServerHostKey(sshRawKey []byte) *ServerHostKeyJsonLog {
ret := new(ServerHostKeyJsonLog)
ret.Raw = sshRawKey
tempHash := sha256.Sum256(sshRawKey)
ret.Fingerprint = hex.EncodeToString(tempHash[:])
keyAlgorithm, keyBytes, ok := parseString(sshRawKey)
if !ok {
ret.Algorithm = "unknown"
return ret
}
ret.Algorithm = string(keyAlgorithm)
keyObj, rest, err := parsePubKey(keyBytes, ret.Algorithm)
if err != nil {
ret.ParseError = err.Error()
return ret
}
ret.TrailingData = rest
ok = ret.PublicKeyJsonLog.AddPublicKey(keyObj)
if !ok {
ret.ParseError = "Cannot parse to JSON"
}
return ret
}
// 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, c *Config) (*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, c *Config) (*kexResult, error)
// Create a JSON object for the kexAlgorithm group
MarshalJSON() ([]byte, error)
// Get a new instance of this interface
// Because the base x/crypto package passes the same object to each connection
GetNew(keyType string) kexAlgorithm
}
// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement.
type dhGroup struct {
g, p, pMinus1 *big.Int
JsonLog dhGroupJsonLog
}
type dhGroupJsonLog struct {
Parameters *ztoolsKeys.DHParams `json:"dh_params,omitempty"`
ServerSignature *JsonSignature `json:"server_signature,omitempty"`
ServerHostKey *ServerHostKeyJsonLog `json:"server_host_key,omitempty"`
}
func (group *dhGroup) MarshalJSON() ([]byte, error) {
group.JsonLog.Parameters.Generator = group.g
group.JsonLog.Parameters.Prime = group.p
return json.Marshal(group.JsonLog)
}
func (group *dhGroup) GetNew(keyType string) kexAlgorithm {
ret := new(dhGroup)
ret.g = new(big.Int).SetInt64(2)
switch keyType {
case kexAlgoDH1SHA1:
ret.p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
ret.pMinus1 = new(big.Int).Sub(ret.p, bigOne)
break
case kexAlgoDH14SHA1:
ret.p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
ret.pMinus1 = new(big.Int).Sub(ret.p, bigOne)
break
default:
panic("Unimplemented DH KEX selected")
}
return ret
}
func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) {
if theirPublic.Cmp(bigOne) <= 0 || theirPublic.Cmp(group.pMinus1) >= 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, config *Config) (*kexResult, error) {
group.JsonLog.Parameters = new(ztoolsKeys.DHParams)
hashFunc := crypto.SHA1
var x *big.Int
for {
var err error
if x, err = rand.Int(randSource, group.pMinus1); err != nil {
return nil, err
}
if x.Sign() > 0 {
break
}
}
if config.Verbose {
group.JsonLog.Parameters.ClientPrivate = x
}
X := new(big.Int).Exp(group.g, x, group.p)
if config.Verbose {
group.JsonLog.Parameters.ClientPublic = X
}
kexDHInit := kexDHInitMsg{
X: X,
}
if err := c.writePacket(Marshal(&kexDHInit)); err != nil {
return nil, err
}
packet, err := c.readPacket()
if err != nil {
return nil, err
}
var kexDHReply kexDHReplyMsg
if err = Unmarshal(packet, &kexDHReply); err != nil {
return nil, err
}
group.JsonLog.Parameters.ServerPublic = kexDHReply.Y
group.JsonLog.ServerSignature = new(JsonSignature)
group.JsonLog.ServerSignature.Raw = kexDHReply.Signature
group.JsonLog.ServerSignature.Parsed, _, _ = parseSignatureBody(kexDHReply.Signature)
group.JsonLog.ServerHostKey = LogServerHostKey(kexDHReply.HostKey)
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)
H := h.Sum(nil)
group.JsonLog.ServerSignature.H = H
return &kexResult{
H: H,
K: K,
HostKey: kexDHReply.HostKey,
Signature: kexDHReply.Signature,
Hash: crypto.SHA1,
}, nil
}
func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer, config *Config) (result *kexResult, err error) {
hashFunc := crypto.SHA1
packet, err := c.readPacket()
if err != nil {
return
}
var kexDHInit kexDHInitMsg
if err = Unmarshal(packet, &kexDHInit); err != nil {
return
}
var y *big.Int
for {
if y, err = rand.Int(randSource, group.pMinus1); err != nil {
return
}
if y.Sign() > 0 {
break
}
}
Y := new(big.Int).Exp(group.g, y, group.p)
kInt, err := group.diffieHellman(kexDHInit.X, y)
if err != nil {
return nil, err
}
hostKeyBytes := priv.PublicKey().Marshal()
h := hashFunc.New()
magics.write(h)
writeString(h, hostKeyBytes)
writeInt(h, kexDHInit.X)
writeInt(h, Y)
K := make([]byte, intLength(kInt))
marshalInt(K, kInt)
h.Write(K)
H := h.Sum(nil)
// H is already a hash, but the hostkey signing will apply its
// own key-specific hash algorithm.
sig, err := signAndMarshal(priv, randSource, H)
if err != nil {
return nil, err
}
kexDHReply := kexDHReplyMsg{
HostKey: hostKeyBytes,
Y: Y,
Signature: sig,
}
packet = Marshal(&kexDHReply)
err = c.writePacket(packet)
return &kexResult{
H: H,
K: K,
HostKey: hostKeyBytes,
Signature: sig,
Hash: crypto.SHA1,
}, nil
}
// ecdh performs Elliptic Curve Diffie-Hellman key exchange as
// described in RFC 5656, section 4.
type ecdh struct {
curve elliptic.Curve
JsonLog ecdhJsonLog
}
type ecdhJsonLog struct {
Parameters *ztoolsKeys.ECDHParams `json:"ecdh_params,omitempty"`
ServerSignature *JsonSignature `json:"server_signature,omitempty"`
ServerHostKey *ServerHostKeyJsonLog `json:"server_host_key,omitempty"`
}
func (kex *ecdh) MarshalJSON() ([]byte, error) {
return json.Marshal(kex.JsonLog)
}
func (kex *ecdh) GetNew(keyType string) kexAlgorithm {
ret := new(ecdh)
switch keyType {
case kexAlgoECDH521:
ret.curve = elliptic.P521()
break
case kexAlgoECDH384:
ret.curve = elliptic.P384()
break
case kexAlgoECDH256:
ret.curve = elliptic.P256()
break
default:
panic("Unimplemented ECDH KEX selected")
}
return ret
}
func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics, config *Config) (*kexResult, error) {
kex.JsonLog.Parameters = new(ztoolsKeys.ECDHParams)
kex.JsonLog.Parameters.ServerPublic = new(ztoolsKeys.ECPoint)
if config.Verbose {
kex.JsonLog.Parameters.ClientPublic = new(ztoolsKeys.ECPoint)
kex.JsonLog.Parameters.ClientPrivate = new(ztoolsKeys.ECDHPrivateParams)
}
ephKey, err := ecdsa.GenerateKey(kex.curve, rand)
if err != nil {
return nil, err
}
if config.Verbose {
kex.JsonLog.Parameters.ClientPublic.X = ephKey.PublicKey.X
kex.JsonLog.Parameters.ClientPublic.Y = ephKey.PublicKey.Y
kex.JsonLog.Parameters.ClientPrivate.Value = ephKey.D.Bytes()
kex.JsonLog.Parameters.ClientPrivate.Length = ephKey.D.BitLen()
}
kexInit := kexECDHInitMsg{
ClientPubKey: elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y),
}
serialized := Marshal(&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(packet, &reply); err != nil {
return nil, err
}
x, y, err := unmarshalECKey(kex.curve, reply.EphemeralPubKey)
kex.JsonLog.Parameters.ServerPublic.X = x
kex.JsonLog.Parameters.ServerPublic.Y = y
kex.JsonLog.ServerHostKey = LogServerHostKey(reply.HostKey)
kex.JsonLog.ServerSignature = new(JsonSignature)
kex.JsonLog.ServerSignature.Raw = reply.Signature
kex.JsonLog.ServerSignature.Parsed, _, _ = parseSignatureBody(reply.Signature)
if err != nil {
return nil, err
}
// generate shared secret
secret, _ := kex.curve.ScalarMult(x, y, ephKey.D.Bytes())
h := ecHash(kex.curve).New()
magics.write(h)
writeString(h, reply.HostKey)
writeString(h, kexInit.ClientPubKey)
writeString(h, reply.EphemeralPubKey)
K := make([]byte, intLength(secret))
marshalInt(K, secret)
h.Write(K)
H := h.Sum(nil)
kex.JsonLog.ServerSignature.H = H
return &kexResult{
H: H,
K: K,
HostKey: reply.HostKey,
Signature: reply.Signature,
Hash: ecHash(kex.curve),
}, nil
}
// unmarshalECKey parses and checks an EC key.
func unmarshalECKey(curve elliptic.Curve, pubkey []byte) (x, y *big.Int, err error) {
x, y = elliptic.Unmarshal(curve, pubkey)
if x == nil {
return nil, nil, errors.New("ssh: elliptic.Unmarshal failure")
}
if !validateECPublicKey(curve, x, y) {
return nil, nil, errors.New("ssh: public key not on curve")
}
return x, y, nil
}
// validateECPublicKey checks that the point is a valid public key for
// the given curve. See [SEC1], 3.2.2
func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool {
if x.Sign() == 0 && y.Sign() == 0 {
return false
}
if x.Cmp(curve.Params().P) >= 0 {
return false
}
if y.Cmp(curve.Params().P) >= 0 {
return false
}
if !curve.IsOnCurve(x, y) {
return false
}
// We don't check if N * PubKey == 0, since
//
// - the NIST curves have cofactor = 1, so this is implicit.
// (We don't foresee an implementation that supports non NIST
// curves)
//
// - for ephemeral keys, we don't need to worry about small
// subgroup attacks.
return true
}
func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer, config *Config) (result *kexResult, err error) {
packet, err := c.readPacket()
if err != nil {
return nil, err
}
var kexECDHInit kexECDHInitMsg
if err = Unmarshal(packet, &kexECDHInit); err != nil {
return nil, err
}
clientX, clientY, err := unmarshalECKey(kex.curve, kexECDHInit.ClientPubKey)
if err != nil {
return nil, err
}
// We could cache this key across multiple users/multiple
// connection attempts, but the benefit is small. OpenSSH
// generates a new key for each incoming connection.
ephKey, err := ecdsa.GenerateKey(kex.curve, rand)
if err != nil {
return nil, err
}
hostKeyBytes := priv.PublicKey().Marshal()
serializedEphKey := elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y)
// generate shared secret
secret, _ := kex.curve.ScalarMult(clientX, clientY, ephKey.D.Bytes())
h := ecHash(kex.curve).New()
magics.write(h)
writeString(h, hostKeyBytes)
writeString(h, kexECDHInit.ClientPubKey)
writeString(h, serializedEphKey)
K := make([]byte, intLength(secret))
marshalInt(K, secret)
h.Write(K)
H := h.Sum(nil)
// H is already a hash, but the hostkey signing will apply its
// own key-specific hash algorithm.
sig, err := signAndMarshal(priv, rand, H)
if err != nil {
return nil, err
}
reply := kexECDHReplyMsg{
EphemeralPubKey: serializedEphKey,
HostKey: hostKeyBytes,
Signature: sig,
}
serialized := Marshal(&reply)
if err := c.writePacket(serialized); err != nil {
return nil, err
}
return &kexResult{
H: H,
K: K,
HostKey: reply.HostKey,
Signature: sig,
Hash: ecHash(kex.curve),
}, nil
}
var kexAlgoMap = map[string]kexAlgorithm{}
func init() {
// This is the group called diffie-hellman-group1-sha1 in RFC
// 4253 and Oakley Group 2 in RFC 2409.
p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{
g: new(big.Int).SetInt64(2),
p: p,
pMinus1: new(big.Int).Sub(p, bigOne),
}
// This is the group called diffie-hellman-group14-sha1 in RFC
// 4253 and Oakley Group 14 in RFC 3526.
p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{
g: new(big.Int).SetInt64(2),
p: p,
pMinus1: new(big.Int).Sub(p, bigOne),
}
kexAlgoMap[kexAlgoECDH521] = &ecdh{curve: elliptic.P521()}
kexAlgoMap[kexAlgoECDH384] = &ecdh{curve: elliptic.P384()}
kexAlgoMap[kexAlgoECDH256] = &ecdh{curve: elliptic.P256()}
kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{}
kexAlgoMap[kexAlgoDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1}
kexAlgoMap[kexAlgoDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256}
}
// curve25519sha256 implements the curve25519-sha256@libssh.org key
// agreement protocol, as described in
// https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt
type curve25519sha256 struct {
JsonLog curve25519sha256JsonLog
}
type curve25519sha256JsonLog struct {
Parameters curve25519sha256JsonLogParameters `json:"curve25519_sha256_params"`
ServerSignature *JsonSignature `json:"server_signature,omitempty"`
ServerHostKey *ServerHostKeyJsonLog `json:"server_host_key,omitempty"`
}
type curve25519sha256JsonLogParameters struct {
ClientPublic []byte `json:"client_public,omitempty"`
ClientPrivate []byte `json:"client_private,omitempty"`
ServerPublic []byte `json:"server_public,omitempty"`
}
func (kex *curve25519sha256) MarshalJSON() ([]byte, error) {
return json.Marshal(kex.JsonLog)
}
func (kex *curve25519sha256) GetNew(keyType string) kexAlgorithm {
return new(curve25519sha256)
}
type curve25519KeyPair struct {
priv [32]byte
pub [32]byte
}
func (kp *curve25519KeyPair) generate(rand io.Reader) error {
if _, err := io.ReadFull(rand, kp.priv[:]); err != nil {
return err
}
curve25519.ScalarBaseMult(&kp.pub, &kp.priv)
return nil
}
// curve25519Zeros is just an array of 32 zero bytes so that we have something
// convenient to compare against in order to reject curve25519 points with the
// wrong order.
var curve25519Zeros [32]byte
func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics, config *Config) (*kexResult, error) {
var kp curve25519KeyPair
if err := kp.generate(rand); err != nil {
return nil, err
}
if config.Verbose {
kex.JsonLog.Parameters.ClientPublic = kp.pub[:]
kex.JsonLog.Parameters.ClientPrivate = kp.priv[:]
}
if err := c.writePacket(Marshal(&kexECDHInitMsg{kp.pub[:]})); err != nil {
return nil, err
}
packet, err := c.readPacket()
if err != nil {
return nil, err
}
var reply kexECDHReplyMsg
if err = Unmarshal(packet, &reply); err != nil {
return nil, err
}
kex.JsonLog.Parameters.ServerPublic = reply.EphemeralPubKey
kex.JsonLog.ServerHostKey = LogServerHostKey(reply.HostKey)
kex.JsonLog.ServerSignature = new(JsonSignature)
kex.JsonLog.ServerSignature.Raw = reply.Signature
kex.JsonLog.ServerSignature.Parsed, _, _ = parseSignatureBody(reply.Signature)
if len(reply.EphemeralPubKey) != 32 {
return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
}
var servPub, secret [32]byte
copy(servPub[:], reply.EphemeralPubKey)
curve25519.ScalarMult(&secret, &kp.priv, &servPub)
if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
}
h := crypto.SHA256.New()
magics.write(h)
writeString(h, reply.HostKey)
writeString(h, kp.pub[:])
writeString(h, reply.EphemeralPubKey)
kInt := new(big.Int).SetBytes(secret[:])
K := make([]byte, intLength(kInt))
marshalInt(K, kInt)
h.Write(K)
H := h.Sum(nil)
kex.JsonLog.ServerSignature.H = H
return &kexResult{
H: H,
K: K,
HostKey: reply.HostKey,
Signature: reply.Signature,
Hash: crypto.SHA256,
}, nil
}
func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer, config *Config) (result *kexResult, err error) {
packet, err := c.readPacket()
if err != nil {
return
}
var kexInit kexECDHInitMsg
if err = Unmarshal(packet, &kexInit); err != nil {
return
}
if len(kexInit.ClientPubKey) != 32 {
return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
}
var kp curve25519KeyPair
if err := kp.generate(rand); err != nil {
return nil, err
}
var clientPub, secret [32]byte
copy(clientPub[:], kexInit.ClientPubKey)
curve25519.ScalarMult(&secret, &kp.priv, &clientPub)
if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
}
hostKeyBytes := priv.PublicKey().Marshal()
h := crypto.SHA256.New()
magics.write(h)
writeString(h, hostKeyBytes)
writeString(h, kexInit.ClientPubKey)
writeString(h, kp.pub[:])
kInt := new(big.Int).SetBytes(secret[:])
K := make([]byte, intLength(kInt))
marshalInt(K, kInt)
h.Write(K)
H := h.Sum(nil)
sig, err := signAndMarshal(priv, rand, H)
if err != nil {
return nil, err
}
reply := kexECDHReplyMsg{
EphemeralPubKey: kp.pub[:],
HostKey: hostKeyBytes,
Signature: sig,
}
if err := c.writePacket(Marshal(&reply)); err != nil {
return nil, err
}
return &kexResult{
H: H,
K: K,
HostKey: hostKeyBytes,
Signature: sig,
Hash: crypto.SHA256,
}, nil
}