github.com/geph-official/geph2@v0.22.6-0.20210211030601-f527cb59b0df/libs/cshirt2/handshake.go (about)

     1  package cshirt2
     2  
     3  import (
     4  	"crypto/rand"
     5  	"crypto/subtle"
     6  	"encoding/binary"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"log"
    11  	"sync"
    12  
    13  	"net"
    14  	"time"
    15  
    16  	"github.com/geph-official/geph2/libs/erand"
    17  	"github.com/minio/blake2b-simd"
    18  	"github.com/patrickmn/go-cache"
    19  	"golang.org/x/crypto/chacha20poly1305"
    20  	"golang.org/x/crypto/curve25519"
    21  )
    22  
    23  // ErrAttackDetected denotes an error that can only happen when active probing attempts are made.
    24  var ErrAttackDetected = errors.New("active probing attack detected")
    25  
    26  // ErrBadHandshakeMAC denotes a bad handshake mac.
    27  var ErrBadHandshakeMAC = errors.New("bad MAC in handshake")
    28  
    29  func mac256(m, k []byte) []byte {
    30  	mac := blake2b.NewMAC(32, k)
    31  	mac.Write(m)
    32  	return mac.Sum(nil)
    33  }
    34  
    35  func mac128(m, k []byte) []byte {
    36  	mac := blake2b.NewMAC(16, k)
    37  	mac.Write(m)
    38  	return mac.Sum(nil)
    39  }
    40  
    41  var (
    42  	globCache     = cache.New(time.Hour*3, time.Minute*30)
    43  	globCacheLock sync.Mutex
    44  )
    45  
    46  const (
    47  	pkSize = 192
    48  )
    49  
    50  func readPK(compatibility bool, secret []byte, isDown bool, transport net.Conn) (pubKey, int64, error) {
    51  	epoch := time.Now().Unix() / 30
    52  	// Read their public key
    53  	theirPublic := make([]byte, pkSize)
    54  	_, err := io.ReadFull(transport, theirPublic)
    55  	if err != nil {
    56  		return nil, 0, err
    57  	}
    58  	// new PK format: chacha20poly1305-encrypted ed25519 public key, with following two bytes denoting padding
    59  	// try to decode as new PK format
    60  	var edpk []byte
    61  	var padlen uint16
    62  	for e := epoch - 30; e < epoch+30; e++ {
    63  		hsKey := mac256(secret, []byte(fmt.Sprintf("handshake-%v-%v", e, isDown)))
    64  		crypt, _ := chacha20poly1305.New(hsKey)
    65  		plain, er := crypt.Open(nil, make([]byte, 12), theirPublic, nil)
    66  		if er != nil {
    67  			err = er
    68  			continue
    69  		}
    70  		edpk = plain[:32]
    71  		padlen = binary.LittleEndian.Uint16(plain[32:][:2])
    72  		epoch = e
    73  	}
    74  	if edpk != nil {
    75  		if !replayFilter(edpk) {
    76  			return nil, 0, ErrAttackDetected
    77  		}
    78  		// read past padding
    79  		_, err = io.ReadFull(transport, make([]byte, padlen))
    80  		if err != nil {
    81  			err = fmt.Errorf("couldn't read past padding: %w", err)
    82  			return nil, 0, err
    83  		}
    84  		return edpk, epoch, nil
    85  	}
    86  	if !compatibility {
    87  		return nil, 0, errors.New("unrecognizable handshake")
    88  	}
    89  	// Read their public key MAC
    90  	theirPublicMAC := make([]byte, 32)
    91  	_, err = io.ReadFull(transport, theirPublicMAC)
    92  	if err != nil {
    93  		return nil, 0, err
    94  	}
    95  	macOK := false
    96  	//shift := 0
    97  	// shift one byte at a time until we find the mac
    98  	for i := 0; i < 1024+(erand.Int(1024)); i++ {
    99  		for e := epoch - 3; e < epoch+3; e++ {
   100  			macKey := mac256(secret, []byte(fmt.Sprintf("%v", e)))
   101  			if i > 0 && subtle.ConstantTimeCompare(theirPublicMAC, mac256(theirPublic, macKey)) == 1 {
   102  				log.Printf("*** ΔE = %v sec, shift = %v ***", (e-epoch)*30, i)
   103  				macOK = true
   104  				epoch = e
   105  				//shift = i
   106  				goto out
   107  			}
   108  		}
   109  		// read another byte
   110  		oneBytes := make([]byte, 1)
   111  		_, err = io.ReadFull(transport, oneBytes)
   112  		if err != nil {
   113  			return nil, 0, err
   114  		}
   115  		theirPublicMAC = append(theirPublicMAC, oneBytes...)[1:]
   116  	}
   117  	log.Println("** zero shift **", transport.RemoteAddr())
   118  	return nil, 0, errors.New("zero shift")
   119  out:
   120  	if !replayFilter(theirPublic) {
   121  		log.Printf("** replay attack detected ** %x %v", theirPublic[:10], transport.RemoteAddr())
   122  		return nil, 0, ErrAttackDetected
   123  	}
   124  	log.Printf("-- GOOD %x %v", theirPublic[:10], transport.RemoteAddr())
   125  	if !macOK {
   126  		log.Println("** bad pk mac **", transport.RemoteAddr())
   127  		return nil, 0, ErrBadHandshakeMAC
   128  	}
   129  	return theirPublic, epoch, nil
   130  }
   131  
   132  func replayFilter(pk []byte) bool {
   133  	globCacheLock.Lock()
   134  	defer globCacheLock.Unlock()
   135  	if _, ok := globCache.Get(string(pk)); ok {
   136  		return false
   137  	}
   138  	// Reject if bad
   139  	globCache.SetDefault(string(pk), true)
   140  	return true
   141  }
   142  
   143  func writePKLegacy(epoch int64, shift int, secret []byte, myPublic pubKey, transport net.Conn) error {
   144  	if epoch == 0 {
   145  		epoch = time.Now().Unix() / 30
   146  	}
   147  	macKey := mac256(secret, []byte(fmt.Sprintf("%v", epoch)))
   148  	myPublicMAC := mac256(myPublic, macKey)
   149  	padding := make([]byte, shift)
   150  	rand.Read(padding)
   151  	_, err := transport.Write(append(append(myPublic, padding...), myPublicMAC...))
   152  	if err != nil {
   153  		return err
   154  	}
   155  	return nil
   156  }
   157  
   158  func writePK(secret []byte, epoch int64, myPublic pubKey, isDown bool, transport net.Conn) error {
   159  	if epoch == 0 {
   160  		epoch = time.Now().Unix() / 30
   161  	}
   162  	hsPlain := make([]byte, 192-16)
   163  	copy(hsPlain, myPublic)
   164  	paddingAmount := erand.Int(65536)
   165  	binary.LittleEndian.PutUint16(hsPlain[32:][:2], uint16(paddingAmount))
   166  	hsKey := mac256(secret, []byte(fmt.Sprintf("handshake-%v-%v", epoch, isDown)))
   167  	hsCrypter, _ := chacha20poly1305.New(hsKey)
   168  	hsCrypt := hsCrypter.Seal(nil, make([]byte, 12), hsPlain, nil)
   169  	padding := make([]byte, paddingAmount)
   170  	rand.Read(padding)
   171  	_, err := transport.Write(append(hsCrypt, padding...))
   172  	return err
   173  }
   174  
   175  // Server negotiates obfuscation on a network connection, acting as the server. The secret must be provided.
   176  func Server(secret []byte, compatibility bool, transport net.Conn) (net.Conn, error) {
   177  	theirPK, epoch, err := readPK(compatibility, secret, false, transport)
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  	if len(theirPK) == 32 {
   182  		mySK := make([]byte, 32)
   183  		rand.Read(mySK)
   184  		log.Printf("mySK = %x", mySK)
   185  		myPK, err := curve25519.X25519(mySK, curve25519.Basepoint)
   186  		if err != nil {
   187  			panic(err)
   188  		}
   189  		writePK(secret, epoch, myPK, true, transport)
   190  		log.Printf("myPK = %x, theirPK = %x", myPK, theirPK)
   191  		shSecret, err := curve25519.X25519(mySK, theirPK)
   192  		if err != nil {
   193  			return nil, err
   194  		}
   195  		return newTransport(transport, shSecret, true), nil
   196  	}
   197  	myPK, mySK := dhGenKey()
   198  	// if shift > 0 {
   199  	// 	shift = erand.Int(1024)
   200  	// }
   201  	err = writePKLegacy(epoch, erand.Int(1000)+1, secret, myPK, transport)
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  	// Compute shared secret
   206  	shSecret := udhSecret(mySK, theirPK)
   207  	return newLegacyTransport(transport, shSecret, true), nil
   208  }
   209  
   210  // Client negotiates low-level obfuscation as a client, using the new protocol.
   211  func Client(secret []byte, transport net.Conn) (net.Conn, error) {
   212  	mySK := make([]byte, 32)
   213  	rand.Read(mySK)
   214  	myPK, err := curve25519.X25519(mySK, curve25519.Basepoint)
   215  	if err != nil {
   216  		panic(err)
   217  	}
   218  	err = writePK(secret, 0, myPK, false, transport)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  	theirPK, _, err := readPK(false, secret, true, transport)
   223  	if err != nil {
   224  		return nil, err
   225  	}
   226  	if len(theirPK) != 32 {
   227  		err = errors.New("wrong length for theirPK")
   228  		return nil, err
   229  	}
   230  	shSecret, err := curve25519.X25519(mySK, theirPK)
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  	return newTransport(transport, shSecret, false), nil
   235  }
   236  
   237  // ClientLegacy negotiates low-level obfuscation as a client, using the legacy protocol.. The server
   238  // secret must be given so that the client can prove knowledge.
   239  func ClientLegacy(secret []byte, transport net.Conn) (net.Conn, error) {
   240  	myPK, mySK := dhGenKey()
   241  	err := writePKLegacy(0, erand.Int(1000)+1, secret, myPK, transport)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  	theirPK, _, err := readPK(true, secret, true, transport)
   246  	if err != nil {
   247  		return nil, err
   248  	}
   249  	// Compute shared secret
   250  	shSecret := udhSecret(mySK, theirPK)
   251  	return newLegacyTransport(transport, shSecret, false), nil
   252  }