| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- package tapdance
- import (
- "crypto/rand"
- "encoding/hex"
- "errors"
- "fmt"
- "os"
- "strconv"
- "time"
- "github.com/refraction-networking/gotapdance/ed25519/extra25519"
- "golang.org/x/crypto/curve25519"
- )
- const AES_GCM_TAG_SIZE = 16
- const timeoutMax = 30000
- const timeoutMin = 20000
- const sendLimitMax = 15614
- const sendLimitMin = 14400
- // timeout for sending TD request and getting a response
- const deadlineConnectTDStationMin = 11175
- const deadlineConnectTDStationMax = 14231
- // deadline to establish TCP connection to decoy
- const deadlineTCPtoDecoyMin = deadlineConnectTDStationMin
- const deadlineTCPtoDecoyMax = deadlineConnectTDStationMax
- // during reconnects we send FIN to server and wait until we get FIN back
- const waitForFINDieMin = 2 * deadlineConnectTDStationMin
- const waitForFINDieMax = 2 * deadlineConnectTDStationMax
- const maxInt16 = int16(^uint16(0) >> 1) // max msg size -> might have to chunk
- //const minInt16 = int16(-maxInt16 - 1)
- type flowType int8
- /*______________________TapdanceFlowConn Mode Chart _________________________________\
- |FlowType |Default Tag|Diff from old-school bidirectional | Engines spawned|
- |-------------|-----------|-----------------------------------------|----------------|
- |Bidirectional| HTTP GET | | Writer, Reader |
- |Upload | HTTP POST | acquires upload | Writer, Reader |
- |ReadOnly | HTTP GET | yields upload, writer sync ignored | Reader |
- |Rendezvous | HTTP GET | passes data in handshake and shuts down | |
- \_____________|___________|_________________________________________|_______________*/
- const (
- flowUpload flowType = 0x1
- flowReadOnly flowType = 0x2
- flowBidirectional flowType = 0x4
- flowRendezvous flowType = 0x0 // rendezvous flows shutdown after handshake
- )
- func (m *flowType) Str() string {
- switch *m {
- case flowUpload:
- return "FlowUpload"
- case flowReadOnly:
- return "FlowReadOnly"
- case flowBidirectional:
- return "FlowBidirectional"
- default:
- return strconv.Itoa(int(*m))
- }
- }
- type msgType int8
- const (
- msgRawData msgType = 1
- msgProtobuf msgType = 2
- )
- func (m *msgType) Str() string {
- switch *m {
- case msgRawData:
- return "msg raw_data"
- case msgProtobuf:
- return "msg protobuf"
- default:
- return strconv.Itoa(int(*m))
- }
- }
- var errMsgClose = errors.New("MSG CLOSE")
- var errNotImplemented = errors.New("Not implemented")
- type tdTagType int8
- const (
- tagHttpGetIncomplete tdTagType = 0
- tagHttpGetComplete tdTagType = 1
- tagHttpPostIncomplete tdTagType = 2
- )
- func (m *tdTagType) Str() string {
- switch *m {
- case tagHttpGetIncomplete:
- return "HTTP GET Incomplete"
- case tagHttpGetComplete:
- return "HTTP GET Complete"
- case tagHttpPostIncomplete:
- return "HTTP POST Incomplete"
- default:
- return strconv.Itoa(int(*m))
- }
- }
- // Fixed-Size-Payload has a 1 byte flags field.
- // bit 0 (1 << 7) determines if flow is bidirectional(0) or upload-only(1)
- // bit 1 (1 << 6) enables dark-decoys
- // bits 2-5 are unassigned
- // bit 6 determines whether PROXY-protocol-formatted string will be sent
- // bit 7 (1 << 0) signals to use TypeLen outer proto
- var (
- tdFlagUploadOnly = uint8(1 << 7)
- // tdFlagDarkDecoy = uint8(1 << 6)
- tdFlagProxyHeader = uint8(1 << 1)
- tdFlagUseTIL = uint8(1 << 0)
- )
- var default_flags = tdFlagUseTIL
- // Global EnableProxyProtocol() is deprecated,
- // use tapdance.Dialer with UseProxyHeader flag instead
- //
- // Requests station to send client's IP to covert in following form:
- // PROXY TCP4 x.x.x.x 127.0.0.1 1111 1234\r\n
- // ^__^ ^_____^ ^_________________^
- // proto clientIP garbage
- func EnableProxyProtocol() {
- Logger().Println("tapdance.EnableProxyProtocol() is deprecated, " +
- "use tapdance.Dialer with UseProxyHeader flag instead.")
- default_flags |= tdFlagProxyHeader
- return
- }
- var tlsSecretLog string
- func SetTlsLogFilename(filename string) error {
- tlsSecretLog = filename
- // Truncate file
- f, err := os.Create(filename)
- if err != nil {
- return err
- }
- return f.Close()
- }
- func WriteTlsLog(clientRandom, masterSecret []byte) error {
- if tlsSecretLog != "" {
- f, err := os.OpenFile(tlsSecretLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
- if err != nil {
- return err
- }
- _, err = fmt.Fprintf(f, "CLIENT_RANDOM %s %s\n",
- hex.EncodeToString(clientRandom),
- hex.EncodeToString(masterSecret))
- if err != nil {
- return err
- }
- return f.Close()
- }
- return nil
- }
- // How much time to sleep on trying to connect to decoys to prevent overwhelming them
- func sleepBeforeConnect(attempt int) (waitTime <-chan time.Time) {
- if attempt >= 6 { // return nil for first 6 attempts
- waitTime = time.After(time.Second * 1)
- }
- return
- }
- // takes Station's Public Key
- // returns Shared Secret, and Eligator Representative
- func generateEligatorTransformedKey(stationPubkey []byte) ([]byte, []byte, error) {
- if len(stationPubkey) != 32 {
- return nil, nil, errors.New("Unexpected station pubkey length. Expected: 32." +
- " Received: " + strconv.Itoa(len(stationPubkey)) + ".")
- }
- var sharedSecret, clientPrivate, clientPublic, representative [32]byte
- for ok := false; ok != true; {
- var sliceKeyPrivate []byte = clientPrivate[:]
- _, err := rand.Read(sliceKeyPrivate)
- if err != nil {
- return nil, nil, err
- }
- ok = extra25519.ScalarBaseMult(&clientPublic, &representative, &clientPrivate)
- }
- var stationPubkeyByte32 [32]byte
- copy(stationPubkeyByte32[:], stationPubkey)
- curve25519.ScalarMult(&sharedSecret, &clientPrivate, &stationPubkeyByte32)
- // extra25519.ScalarBaseMult does not randomize most significant bit(sign of y_coord?)
- // Other implementations of elligator may have up to 2 non-random bits.
- // Here we randomize the bit, expecting it to be flipped back to 0 on station
- randByte := make([]byte, 1)
- _, err := rand.Read(randByte)
- if err != nil {
- return nil, nil, err
- }
- representative[31] |= (0xC0 & randByte[0])
- return sharedSecret[:], representative[:], nil
- }
|