1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package ssh
6
7import (
8	"crypto"
9	"crypto/ecdsa"
10	"crypto/elliptic"
11	"crypto/rand"
12	"crypto/subtle"
13	"encoding/binary"
14	"errors"
15	"fmt"
16	"io"
17	"math/big"
18
19	"golang.org/x/crypto/curve25519"
20)
21
22const (
23	kexAlgoDH1SHA1          = "diffie-hellman-group1-sha1"
24	kexAlgoDH14SHA1         = "diffie-hellman-group14-sha1"
25	kexAlgoECDH256          = "ecdh-sha2-nistp256"
26	kexAlgoECDH384          = "ecdh-sha2-nistp384"
27	kexAlgoECDH521          = "ecdh-sha2-nistp521"
28	kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org"
29
30	// For the following kex only the client half contains a production
31	// ready implementation. The server half only consists of a minimal
32	// implementation to satisfy the automated tests.
33	kexAlgoDHGEXSHA1   = "diffie-hellman-group-exchange-sha1"
34	kexAlgoDHGEXSHA256 = "diffie-hellman-group-exchange-sha256"
35)
36
37// kexResult captures the outcome of a key exchange.
38type kexResult struct {
39	// Session hash. See also RFC 4253, section 8.
40	H []byte
41
42	// Shared secret. See also RFC 4253, section 8.
43	K []byte
44
45	// Host key as hashed into H.
46	HostKey []byte
47
48	// Signature of H.
49	Signature []byte
50
51	// A cryptographic hash function that matches the security
52	// level of the key exchange algorithm. It is used for
53	// calculating H, and for deriving keys from H and K.
54	Hash crypto.Hash
55
56	// The session ID, which is the first H computed. This is used
57	// to derive key material inside the transport.
58	SessionID []byte
59}
60
61// handshakeMagics contains data that is always included in the
62// session hash.
63type handshakeMagics struct {
64	clientVersion, serverVersion []byte
65	clientKexInit, serverKexInit []byte
66}
67
68func (m *handshakeMagics) write(w io.Writer) {
69	writeString(w, m.clientVersion)
70	writeString(w, m.serverVersion)
71	writeString(w, m.clientKexInit)
72	writeString(w, m.serverKexInit)
73}
74
75// kexAlgorithm abstracts different key exchange algorithms.
76type kexAlgorithm interface {
77	// Server runs server-side key agreement, signing the result
78	// with a hostkey.
79	Server(p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error)
80
81	// Client runs the client-side key agreement. Caller is
82	// responsible for verifying the host key signature.
83	Client(p packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error)
84}
85
86// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement.
87type dhGroup struct {
88	g, p, pMinus1 *big.Int
89}
90
91func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) {
92	if theirPublic.Cmp(bigOne) <= 0 || theirPublic.Cmp(group.pMinus1) >= 0 {
93		return nil, errors.New("ssh: DH parameter out of bounds")
94	}
95	return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil
96}
97
98func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) {
99	hashFunc := crypto.SHA1
100
101	var x *big.Int
102	for {
103		var err error
104		if x, err = rand.Int(randSource, group.pMinus1); err != nil {
105			return nil, err
106		}
107		if x.Sign() > 0 {
108			break
109		}
110	}
111
112	X := new(big.Int).Exp(group.g, x, group.p)
113	kexDHInit := kexDHInitMsg{
114		X: X,
115	}
116	if err := c.writePacket(Marshal(&kexDHInit)); err != nil {
117		return nil, err
118	}
119
120	packet, err := c.readPacket()
121	if err != nil {
122		return nil, err
123	}
124
125	var kexDHReply kexDHReplyMsg
126	if err = Unmarshal(packet, &kexDHReply); err != nil {
127		return nil, err
128	}
129
130	ki, err := group.diffieHellman(kexDHReply.Y, x)
131	if err != nil {
132		return nil, err
133	}
134
135	h := hashFunc.New()
136	magics.write(h)
137	writeString(h, kexDHReply.HostKey)
138	writeInt(h, X)
139	writeInt(h, kexDHReply.Y)
140	K := make([]byte, intLength(ki))
141	marshalInt(K, ki)
142	h.Write(K)
143
144	return &kexResult{
145		H:         h.Sum(nil),
146		K:         K,
147		HostKey:   kexDHReply.HostKey,
148		Signature: kexDHReply.Signature,
149		Hash:      crypto.SHA1,
150	}, nil
151}
152
153func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
154	hashFunc := crypto.SHA1
155	packet, err := c.readPacket()
156	if err != nil {
157		return
158	}
159	var kexDHInit kexDHInitMsg
160	if err = Unmarshal(packet, &kexDHInit); err != nil {
161		return
162	}
163
164	var y *big.Int
165	for {
166		if y, err = rand.Int(randSource, group.pMinus1); err != nil {
167			return
168		}
169		if y.Sign() > 0 {
170			break
171		}
172	}
173
174	Y := new(big.Int).Exp(group.g, y, group.p)
175	ki, err := group.diffieHellman(kexDHInit.X, y)
176	if err != nil {
177		return nil, err
178	}
179
180	hostKeyBytes := priv.PublicKey().Marshal()
181
182	h := hashFunc.New()
183	magics.write(h)
184	writeString(h, hostKeyBytes)
185	writeInt(h, kexDHInit.X)
186	writeInt(h, Y)
187
188	K := make([]byte, intLength(ki))
189	marshalInt(K, ki)
190	h.Write(K)
191
192	H := h.Sum(nil)
193
194	// H is already a hash, but the hostkey signing will apply its
195	// own key-specific hash algorithm.
196	sig, err := signAndMarshal(priv, randSource, H)
197	if err != nil {
198		return nil, err
199	}
200
201	kexDHReply := kexDHReplyMsg{
202		HostKey:   hostKeyBytes,
203		Y:         Y,
204		Signature: sig,
205	}
206	packet = Marshal(&kexDHReply)
207
208	err = c.writePacket(packet)
209	return &kexResult{
210		H:         H,
211		K:         K,
212		HostKey:   hostKeyBytes,
213		Signature: sig,
214		Hash:      crypto.SHA1,
215	}, err
216}
217
218// ecdh performs Elliptic Curve Diffie-Hellman key exchange as
219// described in RFC 5656, section 4.
220type ecdh struct {
221	curve elliptic.Curve
222}
223
224func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
225	ephKey, err := ecdsa.GenerateKey(kex.curve, rand)
226	if err != nil {
227		return nil, err
228	}
229
230	kexInit := kexECDHInitMsg{
231		ClientPubKey: elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y),
232	}
233
234	serialized := Marshal(&kexInit)
235	if err := c.writePacket(serialized); err != nil {
236		return nil, err
237	}
238
239	packet, err := c.readPacket()
240	if err != nil {
241		return nil, err
242	}
243
244	var reply kexECDHReplyMsg
245	if err = Unmarshal(packet, &reply); err != nil {
246		return nil, err
247	}
248
249	x, y, err := unmarshalECKey(kex.curve, reply.EphemeralPubKey)
250	if err != nil {
251		return nil, err
252	}
253
254	// generate shared secret
255	secret, _ := kex.curve.ScalarMult(x, y, ephKey.D.Bytes())
256
257	h := ecHash(kex.curve).New()
258	magics.write(h)
259	writeString(h, reply.HostKey)
260	writeString(h, kexInit.ClientPubKey)
261	writeString(h, reply.EphemeralPubKey)
262	K := make([]byte, intLength(secret))
263	marshalInt(K, secret)
264	h.Write(K)
265
266	return &kexResult{
267		H:         h.Sum(nil),
268		K:         K,
269		HostKey:   reply.HostKey,
270		Signature: reply.Signature,
271		Hash:      ecHash(kex.curve),
272	}, nil
273}
274
275// unmarshalECKey parses and checks an EC key.
276func unmarshalECKey(curve elliptic.Curve, pubkey []byte) (x, y *big.Int, err error) {
277	x, y = elliptic.Unmarshal(curve, pubkey)
278	if x == nil {
279		return nil, nil, errors.New("ssh: elliptic.Unmarshal failure")
280	}
281	if !validateECPublicKey(curve, x, y) {
282		return nil, nil, errors.New("ssh: public key not on curve")
283	}
284	return x, y, nil
285}
286
287// validateECPublicKey checks that the point is a valid public key for
288// the given curve. See [SEC1], 3.2.2
289func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool {
290	if x.Sign() == 0 && y.Sign() == 0 {
291		return false
292	}
293
294	if x.Cmp(curve.Params().P) >= 0 {
295		return false
296	}
297
298	if y.Cmp(curve.Params().P) >= 0 {
299		return false
300	}
301
302	if !curve.IsOnCurve(x, y) {
303		return false
304	}
305
306	// We don't check if N * PubKey == 0, since
307	//
308	// - the NIST curves have cofactor = 1, so this is implicit.
309	// (We don't foresee an implementation that supports non NIST
310	// curves)
311	//
312	// - for ephemeral keys, we don't need to worry about small
313	// subgroup attacks.
314	return true
315}
316
317func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
318	packet, err := c.readPacket()
319	if err != nil {
320		return nil, err
321	}
322
323	var kexECDHInit kexECDHInitMsg
324	if err = Unmarshal(packet, &kexECDHInit); err != nil {
325		return nil, err
326	}
327
328	clientX, clientY, err := unmarshalECKey(kex.curve, kexECDHInit.ClientPubKey)
329	if err != nil {
330		return nil, err
331	}
332
333	// We could cache this key across multiple users/multiple
334	// connection attempts, but the benefit is small. OpenSSH
335	// generates a new key for each incoming connection.
336	ephKey, err := ecdsa.GenerateKey(kex.curve, rand)
337	if err != nil {
338		return nil, err
339	}
340
341	hostKeyBytes := priv.PublicKey().Marshal()
342
343	serializedEphKey := elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y)
344
345	// generate shared secret
346	secret, _ := kex.curve.ScalarMult(clientX, clientY, ephKey.D.Bytes())
347
348	h := ecHash(kex.curve).New()
349	magics.write(h)
350	writeString(h, hostKeyBytes)
351	writeString(h, kexECDHInit.ClientPubKey)
352	writeString(h, serializedEphKey)
353
354	K := make([]byte, intLength(secret))
355	marshalInt(K, secret)
356	h.Write(K)
357
358	H := h.Sum(nil)
359
360	// H is already a hash, but the hostkey signing will apply its
361	// own key-specific hash algorithm.
362	sig, err := signAndMarshal(priv, rand, H)
363	if err != nil {
364		return nil, err
365	}
366
367	reply := kexECDHReplyMsg{
368		EphemeralPubKey: serializedEphKey,
369		HostKey:         hostKeyBytes,
370		Signature:       sig,
371	}
372
373	serialized := Marshal(&reply)
374	if err := c.writePacket(serialized); err != nil {
375		return nil, err
376	}
377
378	return &kexResult{
379		H:         H,
380		K:         K,
381		HostKey:   reply.HostKey,
382		Signature: sig,
383		Hash:      ecHash(kex.curve),
384	}, nil
385}
386
387var kexAlgoMap = map[string]kexAlgorithm{}
388
389func init() {
390	// This is the group called diffie-hellman-group1-sha1 in RFC
391	// 4253 and Oakley Group 2 in RFC 2409.
392	p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
393	kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{
394		g:       new(big.Int).SetInt64(2),
395		p:       p,
396		pMinus1: new(big.Int).Sub(p, bigOne),
397	}
398
399	// This is the group called diffie-hellman-group14-sha1 in RFC
400	// 4253 and Oakley Group 14 in RFC 3526.
401	p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
402
403	kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{
404		g:       new(big.Int).SetInt64(2),
405		p:       p,
406		pMinus1: new(big.Int).Sub(p, bigOne),
407	}
408
409	kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}
410	kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
411	kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
412	kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{}
413	kexAlgoMap[kexAlgoDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1}
414	kexAlgoMap[kexAlgoDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256}
415}
416
417// curve25519sha256 implements the curve25519-sha256@libssh.org key
418// agreement protocol, as described in
419// https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt
420type curve25519sha256 struct{}
421
422type curve25519KeyPair struct {
423	priv [32]byte
424	pub  [32]byte
425}
426
427func (kp *curve25519KeyPair) generate(rand io.Reader) error {
428	if _, err := io.ReadFull(rand, kp.priv[:]); err != nil {
429		return err
430	}
431	curve25519.ScalarBaseMult(&kp.pub, &kp.priv)
432	return nil
433}
434
435// curve25519Zeros is just an array of 32 zero bytes so that we have something
436// convenient to compare against in order to reject curve25519 points with the
437// wrong order.
438var curve25519Zeros [32]byte
439
440func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
441	var kp curve25519KeyPair
442	if err := kp.generate(rand); err != nil {
443		return nil, err
444	}
445	if err := c.writePacket(Marshal(&kexECDHInitMsg{kp.pub[:]})); err != nil {
446		return nil, err
447	}
448
449	packet, err := c.readPacket()
450	if err != nil {
451		return nil, err
452	}
453
454	var reply kexECDHReplyMsg
455	if err = Unmarshal(packet, &reply); err != nil {
456		return nil, err
457	}
458	if len(reply.EphemeralPubKey) != 32 {
459		return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
460	}
461
462	var servPub, secret [32]byte
463	copy(servPub[:], reply.EphemeralPubKey)
464	curve25519.ScalarMult(&secret, &kp.priv, &servPub)
465	if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
466		return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
467	}
468
469	h := crypto.SHA256.New()
470	magics.write(h)
471	writeString(h, reply.HostKey)
472	writeString(h, kp.pub[:])
473	writeString(h, reply.EphemeralPubKey)
474
475	ki := new(big.Int).SetBytes(secret[:])
476	K := make([]byte, intLength(ki))
477	marshalInt(K, ki)
478	h.Write(K)
479
480	return &kexResult{
481		H:         h.Sum(nil),
482		K:         K,
483		HostKey:   reply.HostKey,
484		Signature: reply.Signature,
485		Hash:      crypto.SHA256,
486	}, nil
487}
488
489func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
490	packet, err := c.readPacket()
491	if err != nil {
492		return
493	}
494	var kexInit kexECDHInitMsg
495	if err = Unmarshal(packet, &kexInit); err != nil {
496		return
497	}
498
499	if len(kexInit.ClientPubKey) != 32 {
500		return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
501	}
502
503	var kp curve25519KeyPair
504	if err := kp.generate(rand); err != nil {
505		return nil, err
506	}
507
508	var clientPub, secret [32]byte
509	copy(clientPub[:], kexInit.ClientPubKey)
510	curve25519.ScalarMult(&secret, &kp.priv, &clientPub)
511	if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
512		return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
513	}
514
515	hostKeyBytes := priv.PublicKey().Marshal()
516
517	h := crypto.SHA256.New()
518	magics.write(h)
519	writeString(h, hostKeyBytes)
520	writeString(h, kexInit.ClientPubKey)
521	writeString(h, kp.pub[:])
522
523	ki := new(big.Int).SetBytes(secret[:])
524	K := make([]byte, intLength(ki))
525	marshalInt(K, ki)
526	h.Write(K)
527
528	H := h.Sum(nil)
529
530	sig, err := signAndMarshal(priv, rand, H)
531	if err != nil {
532		return nil, err
533	}
534
535	reply := kexECDHReplyMsg{
536		EphemeralPubKey: kp.pub[:],
537		HostKey:         hostKeyBytes,
538		Signature:       sig,
539	}
540	if err := c.writePacket(Marshal(&reply)); err != nil {
541		return nil, err
542	}
543	return &kexResult{
544		H:         H,
545		K:         K,
546		HostKey:   hostKeyBytes,
547		Signature: sig,
548		Hash:      crypto.SHA256,
549	}, nil
550}
551
552// dhGEXSHA implements the diffie-hellman-group-exchange-sha1 and
553// diffie-hellman-group-exchange-sha256 key agreement protocols,
554// as described in RFC 4419
555type dhGEXSHA struct {
556	g, p     *big.Int
557	hashFunc crypto.Hash
558}
559
560const (
561	dhGroupExchangeMinimumBits   = 2048
562	dhGroupExchangePreferredBits = 2048
563	dhGroupExchangeMaximumBits   = 8192
564)
565
566func (gex *dhGEXSHA) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) {
567	if theirPublic.Sign() <= 0 || theirPublic.Cmp(gex.p) >= 0 {
568		return nil, fmt.Errorf("ssh: DH parameter out of bounds")
569	}
570	return new(big.Int).Exp(theirPublic, myPrivate, gex.p), nil
571}
572
573func (gex dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) {
574	// Send GexRequest
575	kexDHGexRequest := kexDHGexRequestMsg{
576		MinBits:      dhGroupExchangeMinimumBits,
577		PreferedBits: dhGroupExchangePreferredBits,
578		MaxBits:      dhGroupExchangeMaximumBits,
579	}
580	if err := c.writePacket(Marshal(&kexDHGexRequest)); err != nil {
581		return nil, err
582	}
583
584	// Receive GexGroup
585	packet, err := c.readPacket()
586	if err != nil {
587		return nil, err
588	}
589
590	var kexDHGexGroup kexDHGexGroupMsg
591	if err = Unmarshal(packet, &kexDHGexGroup); err != nil {
592		return nil, err
593	}
594
595	// reject if p's bit length < dhGroupExchangeMinimumBits or > dhGroupExchangeMaximumBits
596	if kexDHGexGroup.P.BitLen() < dhGroupExchangeMinimumBits || kexDHGexGroup.P.BitLen() > dhGroupExchangeMaximumBits {
597		return nil, fmt.Errorf("ssh: server-generated gex p is out of range (%d bits)", kexDHGexGroup.P.BitLen())
598	}
599
600	gex.p = kexDHGexGroup.P
601	gex.g = kexDHGexGroup.G
602
603	// Check if g is safe by verifing that g > 1 and g < p - 1
604	one := big.NewInt(1)
605	var pMinusOne = &big.Int{}
606	pMinusOne.Sub(gex.p, one)
607	if gex.g.Cmp(one) != 1 && gex.g.Cmp(pMinusOne) != -1 {
608		return nil, fmt.Errorf("ssh: server provided gex g is not safe")
609	}
610
611	// Send GexInit
612	var pHalf = &big.Int{}
613	pHalf.Rsh(gex.p, 1)
614	x, err := rand.Int(randSource, pHalf)
615	if err != nil {
616		return nil, err
617	}
618	X := new(big.Int).Exp(gex.g, x, gex.p)
619	kexDHGexInit := kexDHGexInitMsg{
620		X: X,
621	}
622	if err := c.writePacket(Marshal(&kexDHGexInit)); err != nil {
623		return nil, err
624	}
625
626	// Receive GexReply
627	packet, err = c.readPacket()
628	if err != nil {
629		return nil, err
630	}
631
632	var kexDHGexReply kexDHGexReplyMsg
633	if err = Unmarshal(packet, &kexDHGexReply); err != nil {
634		return nil, err
635	}
636
637	kInt, err := gex.diffieHellman(kexDHGexReply.Y, x)
638	if err != nil {
639		return nil, err
640	}
641
642	// Check if k is safe by verifing that k > 1 and k < p - 1
643	if kInt.Cmp(one) != 1 && kInt.Cmp(pMinusOne) != -1 {
644		return nil, fmt.Errorf("ssh: derived k is not safe")
645	}
646
647	h := gex.hashFunc.New()
648	magics.write(h)
649	writeString(h, kexDHGexReply.HostKey)
650	binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMinimumBits))
651	binary.Write(h, binary.BigEndian, uint32(dhGroupExchangePreferredBits))
652	binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMaximumBits))
653	writeInt(h, gex.p)
654	writeInt(h, gex.g)
655	writeInt(h, X)
656	writeInt(h, kexDHGexReply.Y)
657	K := make([]byte, intLength(kInt))
658	marshalInt(K, kInt)
659	h.Write(K)
660
661	return &kexResult{
662		H:         h.Sum(nil),
663		K:         K,
664		HostKey:   kexDHGexReply.HostKey,
665		Signature: kexDHGexReply.Signature,
666		Hash:      gex.hashFunc,
667	}, nil
668}
669
670// Server half implementation of the Diffie Hellman Key Exchange with SHA1 and SHA256.
671//
672// This is a minimal implementation to satisfy the automated tests.
673func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
674	// Receive GexRequest
675	packet, err := c.readPacket()
676	if err != nil {
677		return
678	}
679	var kexDHGexRequest kexDHGexRequestMsg
680	if err = Unmarshal(packet, &kexDHGexRequest); err != nil {
681		return
682	}
683
684	// smoosh the user's preferred size into our own limits
685	if kexDHGexRequest.PreferedBits > dhGroupExchangeMaximumBits {
686		kexDHGexRequest.PreferedBits = dhGroupExchangeMaximumBits
687	}
688	if kexDHGexRequest.PreferedBits < dhGroupExchangeMinimumBits {
689		kexDHGexRequest.PreferedBits = dhGroupExchangeMinimumBits
690	}
691	// fix min/max if they're inconsistent.  technically, we could just pout
692	// and hang up, but there's no harm in giving them the benefit of the
693	// doubt and just picking a bitsize for them.
694	if kexDHGexRequest.MinBits > kexDHGexRequest.PreferedBits {
695		kexDHGexRequest.MinBits = kexDHGexRequest.PreferedBits
696	}
697	if kexDHGexRequest.MaxBits < kexDHGexRequest.PreferedBits {
698		kexDHGexRequest.MaxBits = kexDHGexRequest.PreferedBits
699	}
700
701	// Send GexGroup
702	// This is the group called diffie-hellman-group14-sha1 in RFC
703	// 4253 and Oakley Group 14 in RFC 3526.
704	p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
705	gex.p = p
706	gex.g = big.NewInt(2)
707
708	kexDHGexGroup := kexDHGexGroupMsg{
709		P: gex.p,
710		G: gex.g,
711	}
712	if err := c.writePacket(Marshal(&kexDHGexGroup)); err != nil {
713		return nil, err
714	}
715
716	// Receive GexInit
717	packet, err = c.readPacket()
718	if err != nil {
719		return
720	}
721	var kexDHGexInit kexDHGexInitMsg
722	if err = Unmarshal(packet, &kexDHGexInit); err != nil {
723		return
724	}
725
726	var pHalf = &big.Int{}
727	pHalf.Rsh(gex.p, 1)
728
729	y, err := rand.Int(randSource, pHalf)
730	if err != nil {
731		return
732	}
733
734	Y := new(big.Int).Exp(gex.g, y, gex.p)
735	kInt, err := gex.diffieHellman(kexDHGexInit.X, y)
736	if err != nil {
737		return nil, err
738	}
739
740	hostKeyBytes := priv.PublicKey().Marshal()
741
742	h := gex.hashFunc.New()
743	magics.write(h)
744	writeString(h, hostKeyBytes)
745	binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMinimumBits))
746	binary.Write(h, binary.BigEndian, uint32(dhGroupExchangePreferredBits))
747	binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMaximumBits))
748	writeInt(h, gex.p)
749	writeInt(h, gex.g)
750	writeInt(h, kexDHGexInit.X)
751	writeInt(h, Y)
752
753	K := make([]byte, intLength(kInt))
754	marshalInt(K, kInt)
755	h.Write(K)
756
757	H := h.Sum(nil)
758
759	// H is already a hash, but the hostkey signing will apply its
760	// own key-specific hash algorithm.
761	sig, err := signAndMarshal(priv, randSource, H)
762	if err != nil {
763		return nil, err
764	}
765
766	kexDHGexReply := kexDHGexReplyMsg{
767		HostKey:   hostKeyBytes,
768		Y:         Y,
769		Signature: sig,
770	}
771	packet = Marshal(&kexDHGexReply)
772
773	err = c.writePacket(packet)
774
775	return &kexResult{
776		H:         H,
777		K:         K,
778		HostKey:   hostKeyBytes,
779		Signature: sig,
780		Hash:      gex.hashFunc,
781	}, err
782}
783