mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-01-24 05:36:19 +02:00
7b302d8c29
Afero is a package that lets you mock out a filesystem with an in-memory filesystem. It allows us to easily create the files required for a given test without worrying about a cleanup step or different tests tripping on eachother when run in parallel. Later on I'll standardise on using afero over the vanilla os package
775 lines
21 KiB
Go
775 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/subtle"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
|
|
"golang.org/x/crypto/curve25519"
|
|
)
|
|
|
|
const (
|
|
kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
|
|
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
|
|
kexAlgoDH14SHA256 = "diffie-hellman-group14-sha256"
|
|
kexAlgoECDH256 = "ecdh-sha2-nistp256"
|
|
kexAlgoECDH384 = "ecdh-sha2-nistp384"
|
|
kexAlgoECDH521 = "ecdh-sha2-nistp521"
|
|
kexAlgoCurve25519SHA256LibSSH = "curve25519-sha256@libssh.org"
|
|
kexAlgoCurve25519SHA256 = "curve25519-sha256"
|
|
|
|
// For the following kex only the client half contains a production
|
|
// ready implementation. The server half only consists of a minimal
|
|
// implementation to satisfy the automated tests.
|
|
kexAlgoDHGEXSHA1 = "diffie-hellman-group-exchange-sha1"
|
|
kexAlgoDHGEXSHA256 = "diffie-hellman-group-exchange-sha256"
|
|
)
|
|
|
|
// 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
|
|
|
|
// 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
|
|
|
|
// The session ID, which is the first H computed. This is used
|
|
// to derive key material inside the transport.
|
|
SessionID []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. algo is the negotiated algorithm, and may
|
|
// be a certificate type.
|
|
Server(p packetConn, rand io.Reader, magics *handshakeMagics, s AlgorithmSigner, algo string) (*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)
|
|
}
|
|
|
|
// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement.
|
|
type dhGroup struct {
|
|
g, p, pMinus1 *big.Int
|
|
hashFunc crypto.Hash
|
|
}
|
|
|
|
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) (*kexResult, error) {
|
|
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
|
|
}
|
|
}
|
|
|
|
X := new(big.Int).Exp(group.g, x, group.p)
|
|
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
|
|
}
|
|
|
|
ki, err := group.diffieHellman(kexDHReply.Y, x)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
h := group.hashFunc.New()
|
|
magics.write(h)
|
|
writeString(h, kexDHReply.HostKey)
|
|
writeInt(h, X)
|
|
writeInt(h, kexDHReply.Y)
|
|
K := make([]byte, intLength(ki))
|
|
marshalInt(K, ki)
|
|
h.Write(K)
|
|
|
|
return &kexResult{
|
|
H: h.Sum(nil),
|
|
K: K,
|
|
HostKey: kexDHReply.HostKey,
|
|
Signature: kexDHReply.Signature,
|
|
Hash: group.hashFunc,
|
|
}, nil
|
|
}
|
|
|
|
func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv AlgorithmSigner, algo string) (result *kexResult, err error) {
|
|
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)
|
|
ki, err := group.diffieHellman(kexDHInit.X, y)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
hostKeyBytes := priv.PublicKey().Marshal()
|
|
|
|
h := group.hashFunc.New()
|
|
magics.write(h)
|
|
writeString(h, hostKeyBytes)
|
|
writeInt(h, kexDHInit.X)
|
|
writeInt(h, Y)
|
|
|
|
K := make([]byte, intLength(ki))
|
|
marshalInt(K, ki)
|
|
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, algo)
|
|
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: group.hashFunc,
|
|
}, err
|
|
}
|
|
|
|
// ecdh performs Elliptic Curve Diffie-Hellman key exchange as
|
|
// described in RFC 5656, section 4.
|
|
type ecdh struct {
|
|
curve elliptic.Curve
|
|
}
|
|
|
|
func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
|
|
ephKey, err := ecdsa.GenerateKey(kex.curve, rand)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
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)
|
|
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)
|
|
|
|
return &kexResult{
|
|
H: h.Sum(nil),
|
|
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 AlgorithmSigner, algo string) (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, algo)
|
|
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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
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),
|
|
hashFunc: crypto.SHA1,
|
|
}
|
|
|
|
// This are the groups called diffie-hellman-group14-sha1 and
|
|
// diffie-hellman-group14-sha256 in RFC 4253 and RFC 8268,
|
|
// and Oakley Group 14 in RFC 3526.
|
|
p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
|
|
group14 := &dhGroup{
|
|
g: new(big.Int).SetInt64(2),
|
|
p: p,
|
|
pMinus1: new(big.Int).Sub(p, bigOne),
|
|
}
|
|
|
|
kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{
|
|
g: group14.g, p: group14.p, pMinus1: group14.pMinus1,
|
|
hashFunc: crypto.SHA1,
|
|
}
|
|
kexAlgoMap[kexAlgoDH14SHA256] = &dhGroup{
|
|
g: group14.g, p: group14.p, pMinus1: group14.pMinus1,
|
|
hashFunc: crypto.SHA256,
|
|
}
|
|
|
|
kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}
|
|
kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
|
|
kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
|
|
kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{}
|
|
kexAlgoMap[kexAlgoCurve25519SHA256LibSSH] = &curve25519sha256{}
|
|
kexAlgoMap[kexAlgoDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1}
|
|
kexAlgoMap[kexAlgoDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256}
|
|
}
|
|
|
|
// curve25519sha256 implements the curve25519-sha256 (formerly known as
|
|
// curve25519-sha256@libssh.org) key exchange method, as described in RFC 8731.
|
|
type curve25519sha256 struct{}
|
|
|
|
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) (*kexResult, error) {
|
|
var kp curve25519KeyPair
|
|
if err := kp.generate(rand); err != nil {
|
|
return nil, err
|
|
}
|
|
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
|
|
}
|
|
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)
|
|
|
|
ki := new(big.Int).SetBytes(secret[:])
|
|
K := make([]byte, intLength(ki))
|
|
marshalInt(K, ki)
|
|
h.Write(K)
|
|
|
|
return &kexResult{
|
|
H: h.Sum(nil),
|
|
K: K,
|
|
HostKey: reply.HostKey,
|
|
Signature: reply.Signature,
|
|
Hash: crypto.SHA256,
|
|
}, nil
|
|
}
|
|
|
|
func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv AlgorithmSigner, algo string) (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[:])
|
|
|
|
ki := new(big.Int).SetBytes(secret[:])
|
|
K := make([]byte, intLength(ki))
|
|
marshalInt(K, ki)
|
|
h.Write(K)
|
|
|
|
H := h.Sum(nil)
|
|
|
|
sig, err := signAndMarshal(priv, rand, H, algo)
|
|
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
|
|
}
|
|
|
|
// dhGEXSHA implements the diffie-hellman-group-exchange-sha1 and
|
|
// diffie-hellman-group-exchange-sha256 key agreement protocols,
|
|
// as described in RFC 4419
|
|
type dhGEXSHA struct {
|
|
hashFunc crypto.Hash
|
|
}
|
|
|
|
const (
|
|
dhGroupExchangeMinimumBits = 2048
|
|
dhGroupExchangePreferredBits = 2048
|
|
dhGroupExchangeMaximumBits = 8192
|
|
)
|
|
|
|
func (gex *dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) {
|
|
// Send GexRequest
|
|
kexDHGexRequest := kexDHGexRequestMsg{
|
|
MinBits: dhGroupExchangeMinimumBits,
|
|
PreferedBits: dhGroupExchangePreferredBits,
|
|
MaxBits: dhGroupExchangeMaximumBits,
|
|
}
|
|
if err := c.writePacket(Marshal(&kexDHGexRequest)); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Receive GexGroup
|
|
packet, err := c.readPacket()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var msg kexDHGexGroupMsg
|
|
if err = Unmarshal(packet, &msg); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// reject if p's bit length < dhGroupExchangeMinimumBits or > dhGroupExchangeMaximumBits
|
|
if msg.P.BitLen() < dhGroupExchangeMinimumBits || msg.P.BitLen() > dhGroupExchangeMaximumBits {
|
|
return nil, fmt.Errorf("ssh: server-generated gex p is out of range (%d bits)", msg.P.BitLen())
|
|
}
|
|
|
|
// Check if g is safe by verifying that 1 < g < p-1
|
|
pMinusOne := new(big.Int).Sub(msg.P, bigOne)
|
|
if msg.G.Cmp(bigOne) <= 0 || msg.G.Cmp(pMinusOne) >= 0 {
|
|
return nil, fmt.Errorf("ssh: server provided gex g is not safe")
|
|
}
|
|
|
|
// Send GexInit
|
|
pHalf := new(big.Int).Rsh(msg.P, 1)
|
|
x, err := rand.Int(randSource, pHalf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
X := new(big.Int).Exp(msg.G, x, msg.P)
|
|
kexDHGexInit := kexDHGexInitMsg{
|
|
X: X,
|
|
}
|
|
if err := c.writePacket(Marshal(&kexDHGexInit)); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Receive GexReply
|
|
packet, err = c.readPacket()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var kexDHGexReply kexDHGexReplyMsg
|
|
if err = Unmarshal(packet, &kexDHGexReply); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if kexDHGexReply.Y.Cmp(bigOne) <= 0 || kexDHGexReply.Y.Cmp(pMinusOne) >= 0 {
|
|
return nil, errors.New("ssh: DH parameter out of bounds")
|
|
}
|
|
kInt := new(big.Int).Exp(kexDHGexReply.Y, x, msg.P)
|
|
|
|
// Check if k is safe by verifying that k > 1 and k < p - 1
|
|
if kInt.Cmp(bigOne) <= 0 || kInt.Cmp(pMinusOne) >= 0 {
|
|
return nil, fmt.Errorf("ssh: derived k is not safe")
|
|
}
|
|
|
|
h := gex.hashFunc.New()
|
|
magics.write(h)
|
|
writeString(h, kexDHGexReply.HostKey)
|
|
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMinimumBits))
|
|
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangePreferredBits))
|
|
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMaximumBits))
|
|
writeInt(h, msg.P)
|
|
writeInt(h, msg.G)
|
|
writeInt(h, X)
|
|
writeInt(h, kexDHGexReply.Y)
|
|
K := make([]byte, intLength(kInt))
|
|
marshalInt(K, kInt)
|
|
h.Write(K)
|
|
|
|
return &kexResult{
|
|
H: h.Sum(nil),
|
|
K: K,
|
|
HostKey: kexDHGexReply.HostKey,
|
|
Signature: kexDHGexReply.Signature,
|
|
Hash: gex.hashFunc,
|
|
}, nil
|
|
}
|
|
|
|
// Server half implementation of the Diffie Hellman Key Exchange with SHA1 and SHA256.
|
|
//
|
|
// This is a minimal implementation to satisfy the automated tests.
|
|
func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv AlgorithmSigner, algo string) (result *kexResult, err error) {
|
|
// Receive GexRequest
|
|
packet, err := c.readPacket()
|
|
if err != nil {
|
|
return
|
|
}
|
|
var kexDHGexRequest kexDHGexRequestMsg
|
|
if err = Unmarshal(packet, &kexDHGexRequest); err != nil {
|
|
return
|
|
}
|
|
|
|
// Send GexGroup
|
|
// 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)
|
|
g := big.NewInt(2)
|
|
|
|
msg := &kexDHGexGroupMsg{
|
|
P: p,
|
|
G: g,
|
|
}
|
|
if err := c.writePacket(Marshal(msg)); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Receive GexInit
|
|
packet, err = c.readPacket()
|
|
if err != nil {
|
|
return
|
|
}
|
|
var kexDHGexInit kexDHGexInitMsg
|
|
if err = Unmarshal(packet, &kexDHGexInit); err != nil {
|
|
return
|
|
}
|
|
|
|
pHalf := new(big.Int).Rsh(p, 1)
|
|
|
|
y, err := rand.Int(randSource, pHalf)
|
|
if err != nil {
|
|
return
|
|
}
|
|
Y := new(big.Int).Exp(g, y, p)
|
|
|
|
pMinusOne := new(big.Int).Sub(p, bigOne)
|
|
if kexDHGexInit.X.Cmp(bigOne) <= 0 || kexDHGexInit.X.Cmp(pMinusOne) >= 0 {
|
|
return nil, errors.New("ssh: DH parameter out of bounds")
|
|
}
|
|
kInt := new(big.Int).Exp(kexDHGexInit.X, y, p)
|
|
|
|
hostKeyBytes := priv.PublicKey().Marshal()
|
|
|
|
h := gex.hashFunc.New()
|
|
magics.write(h)
|
|
writeString(h, hostKeyBytes)
|
|
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMinimumBits))
|
|
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangePreferredBits))
|
|
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMaximumBits))
|
|
writeInt(h, p)
|
|
writeInt(h, g)
|
|
writeInt(h, kexDHGexInit.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, algo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
kexDHGexReply := kexDHGexReplyMsg{
|
|
HostKey: hostKeyBytes,
|
|
Y: Y,
|
|
Signature: sig,
|
|
}
|
|
packet = Marshal(&kexDHGexReply)
|
|
|
|
err = c.writePacket(packet)
|
|
|
|
return &kexResult{
|
|
H: H,
|
|
K: K,
|
|
HostKey: hostKeyBytes,
|
|
Signature: sig,
|
|
Hash: gex.hashFunc,
|
|
}, err
|
|
}
|