mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 17:52:25 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			786 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			786 lines
		
	
	
	
		
			22 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"
 | |
| 	kexAlgoDH16SHA512             = "diffie-hellman-group16-sha512"
 | |
| 	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,
 | |
| 	}
 | |
| 
 | |
| 	// This is the group called diffie-hellman-group16-sha512 in RFC
 | |
| 	// 8268 and Oakley Group 16 in RFC 3526.
 | |
| 	p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF", 16)
 | |
| 
 | |
| 	kexAlgoMap[kexAlgoDH16SHA512] = &dhGroup{
 | |
| 		g:        new(big.Int).SetInt64(2),
 | |
| 		p:        p,
 | |
| 		pMinus1:  new(big.Int).Sub(p, bigOne),
 | |
| 		hashFunc: crypto.SHA512,
 | |
| 	}
 | |
| 
 | |
| 	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
 | |
| }
 |