@ -20,12 +20,14 @@ import (
)
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"
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
@ -75,8 +77,9 @@ func (m *handshakeMagics) write(w io.Writer) {
// 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 )
// 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.
@ -86,6 +89,7 @@ type kexAlgorithm interface {
// 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 ) {
@ -96,8 +100,6 @@ func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int,
}
func ( group * dhGroup ) Client ( c packetConn , randSource io . Reader , magics * handshakeMagics ) ( * kexResult , error ) {
hashFunc := crypto . SHA1
var x * big . Int
for {
var err error
@ -132,7 +134,7 @@ func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handsha
return nil , err
}
h := hashFunc. New ( )
h := group. hashFunc. New ( )
magics . write ( h )
writeString ( h , kexDHReply . HostKey )
writeInt ( h , X )
@ -146,12 +148,11 @@ func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handsha
K : K ,
HostKey : kexDHReply . HostKey ,
Signature : kexDHReply . Signature ,
Hash : crypto. SHA1 ,
Hash : group. hashFunc ,
} , nil
}
func ( group * dhGroup ) Server ( c packetConn , randSource io . Reader , magics * handshakeMagics , priv Signer ) ( result * kexResult , err error ) {
hashFunc := crypto . SHA1
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
@ -179,7 +180,7 @@ func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handsha
hostKeyBytes := priv . PublicKey ( ) . Marshal ( )
h := hashFunc. New ( )
h := group. hashFunc. New ( )
magics . write ( h )
writeString ( h , hostKeyBytes )
writeInt ( h , kexDHInit . X )
@ -193,7 +194,7 @@ func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handsha
// H is already a hash, but the hostkey signing will apply its
// own key-specific hash algorithm.
sig , err := signAndMarshal ( priv , randSource , H )
sig , err := signAndMarshal ( priv , randSource , H , algo )
if err != nil {
return nil , err
}
@ -211,7 +212,7 @@ func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handsha
K : K ,
HostKey : hostKeyBytes ,
Signature : sig ,
Hash : crypto. SHA1 ,
Hash : group. hashFunc ,
} , err
}
@ -314,7 +315,7 @@ func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool {
return true
}
func ( kex * ecdh ) Server ( c packetConn , rand io . Reader , magics * handshakeMagics , priv Signer) ( result * kexResult , err error ) {
func ( kex * ecdh ) Server ( c packetConn , rand io . Reader , magics * handshakeMagics , priv Algorithm Signer, algo string ) ( result * kexResult , err error ) {
packet , err := c . readPacket ( )
if err != nil {
return nil , err
@ -359,7 +360,7 @@ func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, p
// H is already a hash, but the hostkey signing will apply its
// own key-specific hash algorithm.
sig , err := signAndMarshal ( priv , rand , H )
sig , err := signAndMarshal ( priv , rand , H , algo )
if err != nil {
return nil , err
}
@ -384,39 +385,62 @@ func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, p
} , 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.
// 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 ) ,
g : new ( big . Int ) . SetInt64 ( 2 ) ,
p : p ,
pMinus1 : new ( big . Int ) . Sub ( p , bigOne ) ,
hashFunc : crypto . SHA1 ,
}
// This is the group called diffie-hellman-group14-sha1 in RFC
// 4253 and Oakley Group 14 in RFC 3526.
// 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 )
kexAlgoMap [ kexAlgoDH14SHA1 ] = & dhGroup {
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@libssh.org key
// agreement protocol, as described in
// https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt
// 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 {
@ -486,7 +510,7 @@ func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handsh
} , nil
}
func ( kex * curve25519sha256 ) Server ( c packetConn , rand io . Reader , magics * handshakeMagics , priv Signer) ( result * kexResult , err error ) {
func ( kex * curve25519sha256 ) Server ( c packetConn , rand io . Reader , magics * handshakeMagics , priv Algorithm Signer, algo string ) ( result * kexResult , err error ) {
packet , err := c . readPacket ( )
if err != nil {
return
@ -527,7 +551,7 @@ func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handsh
H := h . Sum ( nil )
sig , err := signAndMarshal ( priv , rand , H )
sig , err := signAndMarshal ( priv , rand , H , algo )
if err != nil {
return nil , err
}
@ -553,7 +577,6 @@ func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handsh
// diffie-hellman-group-exchange-sha256 key agreement protocols,
// as described in RFC 4419
type dhGEXSHA struct {
g , p * big . Int
hashFunc crypto . Hash
}
@ -563,14 +586,7 @@ const (
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 ) {
func ( gex * dhGEXSHA ) Client ( c packetConn , randSource io . Reader , magics * handshakeMagics ) ( * kexResult , error ) {
// Send GexRequest
kexDHGexRequest := kexDHGexRequestMsg {
MinBits : dhGroupExchangeMinimumBits ,
@ -587,35 +603,29 @@ func (gex dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshake
return nil , err
}
var kexDHGexGroup kexDHGexGroupMsg
if err = Unmarshal ( packet , & kexDHGexGroup ) ; err != nil {
var msg kexDHGexGroupMsg
if err = Unmarshal ( packet , & msg ) ; 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 ( ) )
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 ( ) )
}
gex . p = kexDHGexGroup . P
gex . g = kexDHGexGroup . G
// Check if g is safe by verifing that g > 1 and g < p - 1
one := big . NewInt ( 1 )
var pMinusOne = & big . Int { }
pMinusOne . Sub ( gex . p , one )
if gex . g . Cmp ( one ) != 1 && gex . g . Cmp ( pMinusOne ) != - 1 {
// 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
var pHalf = & big . Int { }
pHalf . Rsh ( gex . p , 1 )
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 ( gex. g , x , gex . p )
X := new ( big . Int ) . Exp ( msg. G , x , msg . P )
kexDHGexInit := kexDHGexInitMsg {
X : X ,
}
@ -634,13 +644,13 @@ func (gex dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshake
return nil , err
}
kInt , err := gex . diffieHellman ( kexDHGexReply . Y , x )
if 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 verif ing that k > 1 and k < p - 1
if kInt . Cmp ( one) != 1 && kInt . Cmp ( pMinusOne ) != - 1 {
// Check if k is safe by verif y ing 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" )
}
@ -650,8 +660,8 @@ func (gex dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshake
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 , msg. P )
writeInt ( h , msg. G )
writeInt ( h , X )
writeInt ( h , kexDHGexReply . Y )
K := make ( [ ] byte , intLength ( kInt ) )
@ -670,7 +680,7 @@ func (gex dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshake
// 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 ) {
func ( gex dhGEXSHA ) Server ( c packetConn , randSource io . Reader , magics * handshakeMagics , priv Algorithm Signer, algo string ) ( result * kexResult , err error ) {
// Receive GexRequest
packet , err := c . readPacket ( )
if err != nil {
@ -681,35 +691,17 @@ func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshake
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 )
g := big . NewInt ( 2 )
kexDHGexGroup := kexDHGexGroupMsg {
P : gex. p,
G : g ex. g ,
msg := & kexDHGexGroupMsg {
P : p ,
G : g ,
}
if err := c . writePacket ( Marshal ( & kexDHGexGroup ) ) ; err != nil {
if err := c . writePacket ( Marshal ( msg ) ) ; err != nil {
return nil , err
}
@ -723,19 +715,19 @@ func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshake
return
}
var pHalf = & big . Int { }
pHalf . Rsh ( gex . p , 1 )
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 )
Y := new ( big . Int ) . Exp ( gex . g , y , gex . p )
kInt , err := gex . diffieHellman ( kexDHGexInit . X , y )
if err != nil {
return nil , err
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 ( )
@ -745,8 +737,8 @@ func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshake
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 , g ex. g )
writeInt ( h , p)
writeInt ( h , g )
writeInt ( h , kexDHGexInit . X )
writeInt ( h , Y )
@ -758,7 +750,7 @@ func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshake
// H is already a hash, but the hostkey signing will apply its
// own key-specific hash algorithm.
sig , err := signAndMarshal ( priv , randSource , H )
sig , err := signAndMarshal ( priv , randSource , H , algo )
if err != nil {
return nil , err
}