2018-09-30 18:02:42 -07:00
// 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"
2019-10-12 06:48:45 +00:00
"encoding/binary"
2018-09-30 18:02:42 -07:00
"errors"
2019-10-12 06:48:45 +00:00
"fmt"
2018-09-30 18:02:42 -07:00
"io"
"math/big"
"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"
2019-10-12 06:48:45 +00:00
// 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"
2018-09-30 18:02:42 -07:00
)
// 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.
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 )
}
// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement.
type dhGroup struct {
g , p , pMinus1 * big . Int
}
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 ) {
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
}
}
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 := 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 : crypto . SHA1 ,
} , 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 ( 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 := 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 )
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
}
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 Signer ) ( 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 { elliptic . P521 ( ) }
kexAlgoMap [ kexAlgoECDH384 ] = & ecdh { elliptic . P384 ( ) }
kexAlgoMap [ kexAlgoECDH256 ] = & ecdh { elliptic . P256 ( ) }
kexAlgoMap [ kexAlgoCurve25519SHA256 ] = & curve25519sha256 { }
2019-10-12 06:48:45 +00:00
kexAlgoMap [ kexAlgoDHGEXSHA1 ] = & dhGEXSHA { hashFunc : crypto . SHA1 }
kexAlgoMap [ kexAlgoDHGEXSHA256 ] = & dhGEXSHA { hashFunc : crypto . SHA256 }
2018-09-30 18:02:42 -07:00
}
// 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 { }
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 Signer ) ( 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 )
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
}
2019-10-12 06:48:45 +00:00
// 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 {
g , p * big . Int
hashFunc crypto . Hash
}
const numMRTests = 64
const (
dhGroupExchangeMinimumBits = 2048
dhGroupExchangePreferredBits = 2048
dhGroupExchangeMaximumBits = 8192
)
func ( gex * dhGEXSHA ) diffieHellman ( theirPublic , myPrivate * big . Int ) ( * big . Int , error ) {
if theirPublic . Sign ( ) <= 0 || theirPublic . Cmp ( gex . p ) >= 0 {
return nil , fmt . Errorf ( "ssh: DH parameter out of bounds" )
}
return new ( big . Int ) . Exp ( theirPublic , myPrivate , gex . p ) , nil
}
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 kexDHGexGroup kexDHGexGroupMsg
if err = Unmarshal ( packet , & kexDHGexGroup ) ; err != nil {
return nil , err
}
// reject if p's bit length < dhGroupExchangeMinimumBits or > dhGroupExchangeMaximumBits
if kexDHGexGroup . P . BitLen ( ) < dhGroupExchangeMinimumBits || kexDHGexGroup . P . BitLen ( ) > dhGroupExchangeMaximumBits {
return nil , fmt . Errorf ( "ssh: server-generated gex p is out of range (%d bits)" , kexDHGexGroup . P . BitLen ( ) )
}
gex . p = kexDHGexGroup . P
gex . g = kexDHGexGroup . G
// Check if p is safe by verifing that p and (p-1)/2 are primes
one := big . NewInt ( 1 )
var pHalf = & big . Int { }
pHalf . Rsh ( gex . p , 1 )
if ! gex . p . ProbablyPrime ( numMRTests ) || ! pHalf . ProbablyPrime ( numMRTests ) {
return nil , fmt . Errorf ( "ssh: server provided gex p is not safe" )
}
// Check if g is safe by verifing that g > 1 and g < p - 1
var pMinusOne = & big . Int { }
pMinusOne . Sub ( gex . p , one )
if gex . g . Cmp ( one ) != 1 && gex . g . Cmp ( pMinusOne ) != - 1 {
return nil , fmt . Errorf ( "ssh: server provided gex g is not safe" )
}
// Send GexInit
x , err := rand . Int ( randSource , pHalf )
if err != nil {
return nil , err
}
X := new ( big . Int ) . Exp ( gex . g , x , gex . 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
}
kInt , err := gex . diffieHellman ( kexDHGexReply . Y , x )
if err != nil {
return nil , err
}
// Check if k is safe by verifing that k > 1 and k < p - 1
if kInt . Cmp ( one ) != 1 && kInt . Cmp ( pMinusOne ) != - 1 {
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 , gex . p )
writeInt ( h , gex . 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 Signer ) ( 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
}
// smoosh the user's preferred size into our own limits
if kexDHGexRequest . PreferedBits > dhGroupExchangeMaximumBits {
kexDHGexRequest . PreferedBits = dhGroupExchangeMaximumBits
}
if kexDHGexRequest . PreferedBits < dhGroupExchangeMinimumBits {
kexDHGexRequest . PreferedBits = dhGroupExchangeMinimumBits
}
// fix min/max if they're inconsistent. technically, we could just pout
// and hang up, but there's no harm in giving them the benefit of the
// doubt and just picking a bitsize for them.
if kexDHGexRequest . MinBits > kexDHGexRequest . PreferedBits {
kexDHGexRequest . MinBits = kexDHGexRequest . PreferedBits
}
if kexDHGexRequest . MaxBits < kexDHGexRequest . PreferedBits {
kexDHGexRequest . MaxBits = kexDHGexRequest . PreferedBits
}
// 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 )
gex . p = p
gex . g = big . NewInt ( 2 )
kexDHGexGroup := kexDHGexGroupMsg {
P : gex . p ,
G : gex . g ,
}
if err := c . writePacket ( Marshal ( & kexDHGexGroup ) ) ; 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
}
var pHalf = & big . Int { }
pHalf . Rsh ( gex . p , 1 )
y , err := rand . Int ( randSource , pHalf )
if err != nil {
return
}
Y := new ( big . Int ) . Exp ( gex . g , y , gex . p )
kInt , err := gex . diffieHellman ( kexDHGexInit . X , y )
if err != nil {
return nil , err
}
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 , gex . p )
writeInt ( h , gex . 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 )
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
}