github.com/glycerine/xcryptossh@v7.0.4+incompatible/kex.go (about)

     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  
     5  package ssh
     6  
     7  import (
     8  	"context"
     9  	"crypto"
    10  	"crypto/ecdsa"
    11  	"crypto/elliptic"
    12  	"crypto/rand"
    13  	"crypto/subtle"
    14  	"errors"
    15  	"io"
    16  	"math/big"
    17  
    18  	"golang.org/x/crypto/curve25519"
    19  )
    20  
    21  const (
    22  	kexAlgoDH1SHA1          = "diffie-hellman-group1-sha1"
    23  	kexAlgoDH14SHA1         = "diffie-hellman-group14-sha1"
    24  	kexAlgoECDH256          = "ecdh-sha2-nistp256"
    25  	kexAlgoECDH384          = "ecdh-sha2-nistp384"
    26  	kexAlgoECDH521          = "ecdh-sha2-nistp521"
    27  	kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org"
    28  )
    29  
    30  // kexResult captures the outcome of a key exchange.
    31  type kexResult struct {
    32  	// Session hash. See also RFC 4253, section 8.
    33  	H []byte
    34  
    35  	// Shared secret. See also RFC 4253, section 8.
    36  	K []byte
    37  
    38  	// Host key as hashed into H.
    39  	HostKey []byte
    40  
    41  	// Signature of H.
    42  	Signature []byte
    43  
    44  	// A cryptographic hash function that matches the security
    45  	// level of the key exchange algorithm. It is used for
    46  	// calculating H, and for deriving keys from H and K.
    47  	Hash crypto.Hash
    48  
    49  	// The session ID, which is the first H computed. This is used
    50  	// to derive key material inside the transport.
    51  	SessionID []byte
    52  }
    53  
    54  // handshakeMagics contains data that is always included in the
    55  // session hash.
    56  type handshakeMagics struct {
    57  	clientVersion, serverVersion []byte
    58  	clientKexInit, serverKexInit []byte
    59  }
    60  
    61  func (m *handshakeMagics) write(w io.Writer) {
    62  	writeString(w, m.clientVersion)
    63  	writeString(w, m.serverVersion)
    64  	writeString(w, m.clientKexInit)
    65  	writeString(w, m.serverKexInit)
    66  }
    67  
    68  // kexAlgorithm abstracts different key exchange algorithms.
    69  type kexAlgorithm interface {
    70  	// Server runs server-side key agreement, signing the result
    71  	// with a hostkey.
    72  	Server(ctx context.Context, p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error)
    73  
    74  	// Client runs the client-side key agreement. Caller is
    75  	// responsible for verifying the host key signature.
    76  	Client(ctx context.Context, p packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error)
    77  }
    78  
    79  // dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement.
    80  type dhGroup struct {
    81  	g, p, pMinus1 *big.Int
    82  }
    83  
    84  func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) {
    85  	if theirPublic.Cmp(bigOne) <= 0 || theirPublic.Cmp(group.pMinus1) >= 0 {
    86  		return nil, errors.New("ssh: DH parameter out of bounds")
    87  	}
    88  	return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil
    89  }
    90  
    91  func (group *dhGroup) Client(ctx context.Context, c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) {
    92  	hashFunc := crypto.SHA1
    93  
    94  	var x *big.Int
    95  	for {
    96  		var err error
    97  		if x, err = rand.Int(randSource, group.pMinus1); err != nil {
    98  			return nil, err
    99  		}
   100  		if x.Sign() > 0 {
   101  			break
   102  		}
   103  	}
   104  
   105  	X := new(big.Int).Exp(group.g, x, group.p)
   106  	kexDHInit := kexDHInitMsg{
   107  		X: X,
   108  	}
   109  	if err := c.writePacket(Marshal(&kexDHInit)); err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	packet, err := c.readPacket(ctx)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	var kexDHReply kexDHReplyMsg
   119  	if err = Unmarshal(packet, &kexDHReply); err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	kInt, err := group.diffieHellman(kexDHReply.Y, x)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	h := hashFunc.New()
   129  	magics.write(h)
   130  	writeString(h, kexDHReply.HostKey)
   131  	writeInt(h, X)
   132  	writeInt(h, kexDHReply.Y)
   133  	K := make([]byte, intLength(kInt))
   134  	marshalInt(K, kInt)
   135  	h.Write(K)
   136  
   137  	return &kexResult{
   138  		H:         h.Sum(nil),
   139  		K:         K,
   140  		HostKey:   kexDHReply.HostKey,
   141  		Signature: kexDHReply.Signature,
   142  		Hash:      crypto.SHA1,
   143  	}, nil
   144  }
   145  
   146  func (group *dhGroup) Server(ctx context.Context, c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
   147  	hashFunc := crypto.SHA1
   148  	packet, err := c.readPacket(ctx)
   149  	if err != nil {
   150  		return
   151  	}
   152  	var kexDHInit kexDHInitMsg
   153  	if err = Unmarshal(packet, &kexDHInit); err != nil {
   154  		return
   155  	}
   156  
   157  	var y *big.Int
   158  	for {
   159  		if y, err = rand.Int(randSource, group.pMinus1); err != nil {
   160  			return
   161  		}
   162  		if y.Sign() > 0 {
   163  			break
   164  		}
   165  	}
   166  
   167  	Y := new(big.Int).Exp(group.g, y, group.p)
   168  	kInt, err := group.diffieHellman(kexDHInit.X, y)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	hostKeyBytes := priv.PublicKey().Marshal()
   174  
   175  	h := hashFunc.New()
   176  	magics.write(h)
   177  	writeString(h, hostKeyBytes)
   178  	writeInt(h, kexDHInit.X)
   179  	writeInt(h, Y)
   180  
   181  	K := make([]byte, intLength(kInt))
   182  	marshalInt(K, kInt)
   183  	h.Write(K)
   184  
   185  	H := h.Sum(nil)
   186  
   187  	// H is already a hash, but the hostkey signing will apply its
   188  	// own key-specific hash algorithm.
   189  	sig, err := signAndMarshal(priv, randSource, H)
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  
   194  	kexDHReply := kexDHReplyMsg{
   195  		HostKey:   hostKeyBytes,
   196  		Y:         Y,
   197  		Signature: sig,
   198  	}
   199  	packet = Marshal(&kexDHReply)
   200  
   201  	err = c.writePacket(packet)
   202  	return &kexResult{
   203  		H:         H,
   204  		K:         K,
   205  		HostKey:   hostKeyBytes,
   206  		Signature: sig,
   207  		Hash:      crypto.SHA1,
   208  	}, nil
   209  }
   210  
   211  // ecdh performs Elliptic Curve Diffie-Hellman key exchange as
   212  // described in RFC 5656, section 4.
   213  type ecdh struct {
   214  	curve elliptic.Curve
   215  }
   216  
   217  func (kex *ecdh) Client(ctx context.Context, c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
   218  	ephKey, err := ecdsa.GenerateKey(kex.curve, rand)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	kexInit := kexECDHInitMsg{
   224  		ClientPubKey: elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y),
   225  	}
   226  
   227  	serialized := Marshal(&kexInit)
   228  	if err := c.writePacket(serialized); err != nil {
   229  		return nil, err
   230  	}
   231  
   232  	packet, err := c.readPacket(ctx)
   233  	if err != nil {
   234  		return nil, err
   235  	}
   236  
   237  	var reply kexECDHReplyMsg
   238  	if err = Unmarshal(packet, &reply); err != nil {
   239  		return nil, err
   240  	}
   241  
   242  	x, y, err := unmarshalECKey(kex.curve, reply.EphemeralPubKey)
   243  	if err != nil {
   244  		return nil, err
   245  	}
   246  
   247  	// generate shared secret
   248  	secret, _ := kex.curve.ScalarMult(x, y, ephKey.D.Bytes())
   249  
   250  	h := ecHash(kex.curve).New()
   251  	magics.write(h)
   252  	writeString(h, reply.HostKey)
   253  	writeString(h, kexInit.ClientPubKey)
   254  	writeString(h, reply.EphemeralPubKey)
   255  	K := make([]byte, intLength(secret))
   256  	marshalInt(K, secret)
   257  	h.Write(K)
   258  
   259  	return &kexResult{
   260  		H:         h.Sum(nil),
   261  		K:         K,
   262  		HostKey:   reply.HostKey,
   263  		Signature: reply.Signature,
   264  		Hash:      ecHash(kex.curve),
   265  	}, nil
   266  }
   267  
   268  // unmarshalECKey parses and checks an EC key.
   269  func unmarshalECKey(curve elliptic.Curve, pubkey []byte) (x, y *big.Int, err error) {
   270  	x, y = elliptic.Unmarshal(curve, pubkey)
   271  	if x == nil {
   272  		return nil, nil, errors.New("ssh: elliptic.Unmarshal failure")
   273  	}
   274  	if !validateECPublicKey(curve, x, y) {
   275  		return nil, nil, errors.New("ssh: public key not on curve")
   276  	}
   277  	return x, y, nil
   278  }
   279  
   280  // validateECPublicKey checks that the point is a valid public key for
   281  // the given curve. See [SEC1], 3.2.2
   282  func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool {
   283  	if x.Sign() == 0 && y.Sign() == 0 {
   284  		return false
   285  	}
   286  
   287  	if x.Cmp(curve.Params().P) >= 0 {
   288  		return false
   289  	}
   290  
   291  	if y.Cmp(curve.Params().P) >= 0 {
   292  		return false
   293  	}
   294  
   295  	if !curve.IsOnCurve(x, y) {
   296  		return false
   297  	}
   298  
   299  	// We don't check if N * PubKey == 0, since
   300  	//
   301  	// - the NIST curves have cofactor = 1, so this is implicit.
   302  	// (We don't foresee an implementation that supports non NIST
   303  	// curves)
   304  	//
   305  	// - for ephemeral keys, we don't need to worry about small
   306  	// subgroup attacks.
   307  	return true
   308  }
   309  
   310  func (kex *ecdh) Server(ctx context.Context, c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
   311  	packet, err := c.readPacket(ctx)
   312  	if err != nil {
   313  		return nil, err
   314  	}
   315  
   316  	var kexECDHInit kexECDHInitMsg
   317  	if err = Unmarshal(packet, &kexECDHInit); err != nil {
   318  		return nil, err
   319  	}
   320  
   321  	clientX, clientY, err := unmarshalECKey(kex.curve, kexECDHInit.ClientPubKey)
   322  	if err != nil {
   323  		return nil, err
   324  	}
   325  
   326  	// We could cache this key across multiple users/multiple
   327  	// connection attempts, but the benefit is small. OpenSSH
   328  	// generates a new key for each incoming connection.
   329  	ephKey, err := ecdsa.GenerateKey(kex.curve, rand)
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  
   334  	hostKeyBytes := priv.PublicKey().Marshal()
   335  
   336  	serializedEphKey := elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y)
   337  
   338  	// generate shared secret
   339  	secret, _ := kex.curve.ScalarMult(clientX, clientY, ephKey.D.Bytes())
   340  
   341  	h := ecHash(kex.curve).New()
   342  	magics.write(h)
   343  	writeString(h, hostKeyBytes)
   344  	writeString(h, kexECDHInit.ClientPubKey)
   345  	writeString(h, serializedEphKey)
   346  
   347  	K := make([]byte, intLength(secret))
   348  	marshalInt(K, secret)
   349  	h.Write(K)
   350  
   351  	H := h.Sum(nil)
   352  
   353  	// H is already a hash, but the hostkey signing will apply its
   354  	// own key-specific hash algorithm.
   355  	sig, err := signAndMarshal(priv, rand, H)
   356  	if err != nil {
   357  		return nil, err
   358  	}
   359  
   360  	reply := kexECDHReplyMsg{
   361  		EphemeralPubKey: serializedEphKey,
   362  		HostKey:         hostKeyBytes,
   363  		Signature:       sig,
   364  	}
   365  
   366  	serialized := Marshal(&reply)
   367  	if err := c.writePacket(serialized); err != nil {
   368  		return nil, err
   369  	}
   370  
   371  	return &kexResult{
   372  		H:         H,
   373  		K:         K,
   374  		HostKey:   reply.HostKey,
   375  		Signature: sig,
   376  		Hash:      ecHash(kex.curve),
   377  	}, nil
   378  }
   379  
   380  var kexAlgoMap = map[string]kexAlgorithm{}
   381  
   382  func init() {
   383  	// This is the group called diffie-hellman-group1-sha1 in RFC
   384  	// 4253 and Oakley Group 2 in RFC 2409.
   385  	p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
   386  	kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{
   387  		g:       new(big.Int).SetInt64(2),
   388  		p:       p,
   389  		pMinus1: new(big.Int).Sub(p, bigOne),
   390  	}
   391  
   392  	// This is the group called diffie-hellman-group14-sha1 in RFC
   393  	// 4253 and Oakley Group 14 in RFC 3526.
   394  	p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
   395  
   396  	kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{
   397  		g:       new(big.Int).SetInt64(2),
   398  		p:       p,
   399  		pMinus1: new(big.Int).Sub(p, bigOne),
   400  	}
   401  
   402  	kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}
   403  	kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
   404  	kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
   405  	kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{}
   406  }
   407  
   408  // curve25519sha256 implements the curve25519-sha256@libssh.org key
   409  // agreement protocol, as described in
   410  // https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt
   411  type curve25519sha256 struct{}
   412  
   413  type curve25519KeyPair struct {
   414  	priv [32]byte
   415  	pub  [32]byte
   416  }
   417  
   418  func (kp *curve25519KeyPair) generate(rand io.Reader) error {
   419  	if _, err := io.ReadFull(rand, kp.priv[:]); err != nil {
   420  		return err
   421  	}
   422  	curve25519.ScalarBaseMult(&kp.pub, &kp.priv)
   423  	return nil
   424  }
   425  
   426  // curve25519Zeros is just an array of 32 zero bytes so that we have something
   427  // convenient to compare against in order to reject curve25519 points with the
   428  // wrong order.
   429  var curve25519Zeros [32]byte
   430  
   431  func (kex *curve25519sha256) Client(ctx context.Context, c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
   432  	var kp curve25519KeyPair
   433  	if err := kp.generate(rand); err != nil {
   434  		return nil, err
   435  	}
   436  	if err := c.writePacket(Marshal(&kexECDHInitMsg{kp.pub[:]})); err != nil {
   437  		return nil, err
   438  	}
   439  
   440  	packet, err := c.readPacket(ctx)
   441  	if err != nil {
   442  		return nil, err
   443  	}
   444  
   445  	var reply kexECDHReplyMsg
   446  	if err = Unmarshal(packet, &reply); err != nil {
   447  		return nil, err
   448  	}
   449  	if len(reply.EphemeralPubKey) != 32 {
   450  		return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
   451  	}
   452  
   453  	var servPub, secret [32]byte
   454  	copy(servPub[:], reply.EphemeralPubKey)
   455  	curve25519.ScalarMult(&secret, &kp.priv, &servPub)
   456  	if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
   457  		return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
   458  	}
   459  
   460  	h := crypto.SHA256.New()
   461  	magics.write(h)
   462  	writeString(h, reply.HostKey)
   463  	writeString(h, kp.pub[:])
   464  	writeString(h, reply.EphemeralPubKey)
   465  
   466  	kInt := new(big.Int).SetBytes(secret[:])
   467  	K := make([]byte, intLength(kInt))
   468  	marshalInt(K, kInt)
   469  	h.Write(K)
   470  
   471  	return &kexResult{
   472  		H:         h.Sum(nil),
   473  		K:         K,
   474  		HostKey:   reply.HostKey,
   475  		Signature: reply.Signature,
   476  		Hash:      crypto.SHA256,
   477  	}, nil
   478  }
   479  
   480  func (kex *curve25519sha256) Server(ctx context.Context, c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
   481  	packet, err := c.readPacket(ctx)
   482  	if err != nil {
   483  		return
   484  	}
   485  	var kexInit kexECDHInitMsg
   486  	if err = Unmarshal(packet, &kexInit); err != nil {
   487  		return
   488  	}
   489  
   490  	if len(kexInit.ClientPubKey) != 32 {
   491  		return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
   492  	}
   493  
   494  	var kp curve25519KeyPair
   495  	if err := kp.generate(rand); err != nil {
   496  		return nil, err
   497  	}
   498  
   499  	var clientPub, secret [32]byte
   500  	copy(clientPub[:], kexInit.ClientPubKey)
   501  	curve25519.ScalarMult(&secret, &kp.priv, &clientPub)
   502  	if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
   503  		return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
   504  	}
   505  
   506  	hostKeyBytes := priv.PublicKey().Marshal()
   507  
   508  	h := crypto.SHA256.New()
   509  	magics.write(h)
   510  	writeString(h, hostKeyBytes)
   511  	writeString(h, kexInit.ClientPubKey)
   512  	writeString(h, kp.pub[:])
   513  
   514  	kInt := new(big.Int).SetBytes(secret[:])
   515  	K := make([]byte, intLength(kInt))
   516  	marshalInt(K, kInt)
   517  	h.Write(K)
   518  
   519  	H := h.Sum(nil)
   520  
   521  	sig, err := signAndMarshal(priv, rand, H)
   522  	if err != nil {
   523  		return nil, err
   524  	}
   525  
   526  	reply := kexECDHReplyMsg{
   527  		EphemeralPubKey: kp.pub[:],
   528  		HostKey:         hostKeyBytes,
   529  		Signature:       sig,
   530  	}
   531  	if err := c.writePacket(Marshal(&reply)); err != nil {
   532  		return nil, err
   533  	}
   534  	return &kexResult{
   535  		H:         H,
   536  		K:         K,
   537  		HostKey:   hostKeyBytes,
   538  		Signature: sig,
   539  		Hash:      crypto.SHA256,
   540  	}, nil
   541  }