From 207fdcada8d015cf049e0a2a334db5aab45ff99c Mon Sep 17 00:00:00 2001 From: RPRX <63339210+rprx@users.noreply.github.com> Date: Sun, 1 Nov 2020 14:53:25 +0000 Subject: [PATCH] XTLS --- LICENSE | 3 + README.md | 2 + alert.go | 2 +- auth.go | 2 +- cipher_suites.go | 2 +- common.go | 5 +- common_string.go | 2 +- conn.go | 319 +++++++++++++++++++++++++++++++++++--- handshake_client.go | 4 +- handshake_client_tls13.go | 4 +- handshake_messages.go | 2 +- handshake_server.go | 4 +- handshake_server_tls13.go | 2 +- key_agreement.go | 2 +- key_schedule.go | 2 +- prf.go | 2 +- ticket.go | 2 +- tls.go | 14 +- 18 files changed, 332 insertions(+), 43 deletions(-) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..75bbdc6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,3 @@ +Copyright (c) 2020 RPRX. All rights reserved. + +Only for compiling executables usage for now. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a7b1e71 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# XTLS +THE FUTURE diff --git a/alert.go b/alert.go index bd6879e..ebdb704 100644 --- a/alert.go +++ b/alert.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-Go file. -package tls +package xtls import "strconv" diff --git a/auth.go b/auth.go index 6b45d19..2daa00e 100644 --- a/auth.go +++ b/auth.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-Go file. -package tls +package xtls import ( "bytes" diff --git a/cipher_suites.go b/cipher_suites.go index a5f4213..16a24df 100644 --- a/cipher_suites.go +++ b/cipher_suites.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-Go file. -package tls +package xtls import ( "crypto" diff --git a/common.go b/common.go index 2be2ab2..042685a 100644 --- a/common.go +++ b/common.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-Go file. -package tls +package xtls import ( "bytes" @@ -17,12 +17,13 @@ import ( "crypto/x509" "errors" "fmt" - "internal/cpu" "io" "net" "strings" "sync" "time" + + "golang.org/x/sys/cpu" ) const ( diff --git a/common_string.go b/common_string.go index 2381088..3963cc4 100644 --- a/common_string.go +++ b/common_string.go @@ -1,6 +1,6 @@ // Code generated by "stringer -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go"; DO NOT EDIT. -package tls +package xtls import "strconv" diff --git a/conn.go b/conn.go index 6c19d0b..bf5cb14 100644 --- a/conn.go +++ b/conn.go @@ -1,10 +1,14 @@ +// XTLS: Copyright 2020 RPRX. All rights reserved. +// Use of this source code is governed by a PRIVATE +// license that can be found in the LICENSE file. +// --- // Copyright 2010 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-Go file. -// TLS low level connection and record layer +// XTLS low level connection and record layer -package tls +package xtls import ( "bytes" @@ -24,7 +28,7 @@ import ( // It implements the net.Conn interface. type Conn struct { // constant - conn net.Conn + Connection net.Conn isClient bool handshakeFn func() error // (*Conn).clientHandshake or serverHandshake @@ -98,6 +102,28 @@ type Conn struct { buffering bool // whether records are buffered in sendBuf sendBuf []byte // a buffer of records waiting to be sent + DirectMode bool + DirectPre bool + DirectIn bool + DirectOut bool + + RPRX bool + SHOW bool + MARK string + + ic, oc int + + fall bool + total int + count int + + taken bool + first bool + index int + cache byte + skip int + maybe bool + // bytesSent counts the bytes of application data sent. // packetsSent counts packets. bytesSent int64 @@ -122,32 +148,32 @@ type Conn struct { // LocalAddr returns the local network address. func (c *Conn) LocalAddr() net.Addr { - return c.conn.LocalAddr() + return c.Connection.LocalAddr() } // RemoteAddr returns the remote network address. func (c *Conn) RemoteAddr() net.Addr { - return c.conn.RemoteAddr() + return c.Connection.RemoteAddr() } // SetDeadline sets the read and write deadlines associated with the connection. // A zero value for t means Read and Write will not time out. // After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. func (c *Conn) SetDeadline(t time.Time) error { - return c.conn.SetDeadline(t) + return c.Connection.SetDeadline(t) } // SetReadDeadline sets the read deadline on the underlying connection. // A zero value for t means Read will not time out. func (c *Conn) SetReadDeadline(t time.Time) error { - return c.conn.SetReadDeadline(t) + return c.Connection.SetReadDeadline(t) } // SetWriteDeadline sets the write deadline on the underlying connection. // A zero value for t means Write will not time out. // After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. func (c *Conn) SetWriteDeadline(t time.Time) error { - return c.conn.SetWriteDeadline(t) + return c.Connection.SetWriteDeadline(t) } // A halfConn represents one direction of the record layer @@ -605,7 +631,7 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { c.input.Reset(nil) // Read header, payload. - if err := c.readFromUntil(c.conn, recordHeaderLen); err != nil { + if err := c.readFromUntil(c.Connection, recordHeaderLen); err != nil { // RFC 8446, Section 6.1 suggests that EOF without an alertCloseNotify // is an error, but popular web sites seem to do this, so we accept it // if and only if at the record boundary. @@ -642,7 +668,7 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { // The current max version is 3.3 so if the version is >= 16.0, // it's probably not real. if (typ != recordTypeAlert && typ != recordTypeHandshake) || vers >= 0x1000 { - return c.in.setErrorLocked(c.newRecordHeaderError(c.conn, "first record does not look like a TLS handshake")) + return c.in.setErrorLocked(c.newRecordHeaderError(c.Connection, "first record does not look like a TLS handshake")) } } if c.vers == VersionTLS13 && n > maxCiphertextTLS13 || n > maxCiphertext { @@ -650,7 +676,7 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { msg := fmt.Sprintf("oversized record received with length %d", n) return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg)) } - if err := c.readFromUntil(c.conn, recordHeaderLen+n); err != nil { + if err := c.readFromUntil(c.Connection, recordHeaderLen+n); err != nil { if e, ok := err.(net.Error); !ok || !e.Temporary() { c.in.setErrorLocked(err) } @@ -659,10 +685,85 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { // Process message. record := c.rawInput.Next(recordHeaderLen + n) - data, typ, err := c.in.decrypt(record) - if err != nil { - return c.in.setErrorLocked(c.sendAlert(err.(alert))) + + var backup, data []byte + var err error + typp := typ + if c.fall && (typ != 23 || n == 19) { + backup = make([]byte, len(record)) + copy(backup, record) + } + if !c.fall || typ != 23 || n == 19 { + data, typ, err = c.in.decrypt(record) + } + if !c.fall { + if err != nil { + return c.in.setErrorLocked(c.sendAlert(err.(alert))) + } else if c.RPRX { + l := len(data) + if c.total == 0 && typ == 23 && l >= 5 { + if c.vers == 772 { + c.ic++ + if data[0] == 23 && data[1] == 3 && data[2] == 3 { + if (int(data[3])<<8 | int(data[4])) <= 16640 { + c.total = (int(data[3])<<8 | int(data[4])) + 5 + c.ic = 0 + } + } + if c.ic == 10 { + c.RPRX = false + } + } else { + c.RPRX = false + } + } + if c.total != 0 { + c.count += l + if c.count == c.total { + c.fall = true + if c.DirectMode { + c.DirectPre = true + if c.SHOW { + fmt.Println(c.MARK, "DirectPre = true") + } + } + } else if c.count > c.total { + //c.RPRX = false + } + } + // + } + // + } else { + if err != nil { + if typp == 23 && n == 19 { + if c.SHOW { + fmt.Println(c.MARK, `fallback`, len(backup)) + } + c.in.incSeq() + c.retryCount = 0 + c.input.Reset(backup) + return nil + } + if c.SHOW { + fmt.Printf("%v failed to decrypt: typp=%v, backup=%v\n", c.MARK, typp, backup) + } + return c.in.setErrorLocked(c.sendAlert(err.(alert))) + } else if data == nil { + if c.SHOW { + fmt.Println(c.MARK, `received`, len(record)) + } + c.in.incSeq() + c.retryCount = 0 + c.input.Reset(record) + return nil + } + if c.SHOW { + fmt.Printf("%v starts to process: typ=%v, data=%v\n", c.MARK, typ, data) + } + // } + if len(data) > maxPlaintext { return c.in.setErrorLocked(c.sendAlert(alertRecordOverflow)) } @@ -911,7 +1012,7 @@ func (c *Conn) write(data []byte) (int, error) { return len(data), nil } - n, err := c.conn.Write(data) + n, err := c.Connection.Write(data) c.bytesSent += int64(n) return n, err } @@ -921,7 +1022,7 @@ func (c *Conn) flush() (int, error) { return 0, nil } - n, err := c.conn.Write(c.sendBuf) + n, err := c.Connection.Write(c.sendBuf) c.bytesSent += int64(n) c.sendBuf = nil c.buffering = false @@ -931,7 +1032,149 @@ func (c *Conn) flush() (int, error) { // writeRecordLocked writes a TLS record with the given type and payload to the // connection and updates the record layer state. func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) { + + var l, f int + + if !c.RPRX { + goto normal + } + + l = len(data) + + if !c.taken && !c.first && typ == 23 && l >= 5 { + if c.vers == 772 { + c.oc++ + if data[0] == 23 && data[1] == 3 && data[2] == 3 { + if (int(data[3])<<8 | int(data[4])) <= 16640 { + c.taken = true + c.first = true + c.oc = 0 + } + } + if c.oc == 10 { + c.RPRX = false + } + } else { + c.RPRX = false + } + } + + if c.taken && typ != 21 { + if l == 0 { + return 0, nil + } + close := false + for i := 0; i < l; i++ { + if c.skip != 0 { + c.skip -= l - i + if c.skip < 0 { + i = l + c.skip + c.skip = 0 + if c.first { + f = i + c.taken = false + c.writeRecordLocked(23, data[:f]) + c.taken = true + c.first = false + if c.DirectMode { + c.DirectOut = true + if c.SHOW { + fmt.Println(c.MARK, "DirectOut = true") + } + } + } + i-- + continue + } else { + if c.first { + if c.skip == 0 { + c.first = false + if c.DirectMode { + c.DirectOut = true + if c.SHOW { + fmt.Println(c.MARK, "DirectOut = true") + } + } + } + goto normal + } + break + } + } + switch c.index { + case 0: + c.maybe = false + if data[i] == 23 { + c.index++ + } else { // usually 21 + close = true + } + case 1, 2: + if data[i] == 3 { + c.index++ + } else { + close = true + } + case 3: + c.cache = data[i] + if c.cache <= 66 { + c.index++ + } else { + close = true + } + case 4: + c.skip = int(c.cache)<<8 | int(data[i]) + if c.skip <= 16640 { + c.index = 0 + if !c.first { + c.out.incSeq() + if c.skip == 19 { + c.maybe = true + } + if c.SHOW { + fmt.Println(c.MARK, "sent", c.skip+5) + } + } + } else { + close = true + } + } + if close { + if t := i - c.index; t > f { + c.write(data[f:t]) + } + if c.SHOW { + fmt.Printf("%v close: typ=%v, c.index=%v, i=%v, data[i]=%v\n", c.MARK, typ, c.index, i, data[i]) + } + break + } + } + if close { + c.closeNotifyErr = c.sendAlertLocked(0) + c.closeNotifySent = true + c.Connection.Close() + } else { + if _, err := c.write(data[f:]); err != nil { + return 0, err + } + } + return l, nil + } + + if c.taken && typ == 21 { + if c.SHOW { + fmt.Printf("%v alert: c.maybe=%v, data=%v\n", c.MARK, c.maybe, data) + } + if c.maybe { + return l, nil + } + // + } + +normal: + var n int + for len(data) > 0 { m := len(data) if maxPayload := c.maxPayloadSizeForWrite(typ); m > maxPayload { @@ -1076,6 +1319,20 @@ var ( // Write writes data to the connection. func (c *Conn) Write(b []byte) (int, error) { + + if c.DirectOut { + if s := len(b) - 31; s >= 0 && b[s] == 21 { + if b[s+1] == 3 && b[s+2] == 3 && b[s+3] == 0 && b[s+4] == 26 { + if c.SHOW { + fmt.Println(c.MARK, "discarded 21 3 3 0 26 at s =", s) + } + c.Connection.Write(b[:s]) + return s + 31, nil + } + } + return c.Connection.Write(b) + } + // interlock with Close below for { x := atomic.LoadInt32(&c.activeCall) @@ -1236,6 +1493,27 @@ func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error { // Read can be made to time out and return a net.Error with Timeout() == true // after a fixed time limit; see SetDeadline and SetReadDeadline. func (c *Conn) Read(b []byte) (int, error) { + + if c.DirectIn { + return c.Connection.Read(b) + } + + if c.DirectPre { + if c.input.Len() != 0 { + n, _ := c.input.Read(b) + return n, nil + } + if c.rawInput.Len() != 0 { + n, _ := c.rawInput.Read(b) + return n, nil + } + c.DirectIn = true + if c.SHOW { + fmt.Println(c.MARK, "DirectIn = true") + } + return c.Connection.Read(b) + } + if err := c.Handshake(); err != nil { return 0, err } @@ -1280,6 +1558,11 @@ func (c *Conn) Read(b []byte) (int, error) { // Close closes the connection. func (c *Conn) Close() error { + + if c.DirectOut { + return c.Connection.Close() + } + // Interlock with Conn.Write above. var x int32 for { @@ -1298,7 +1581,7 @@ func (c *Conn) Close() error { // being used to break the Write and/or clean up resources and // avoid sending the alertCloseNotify, which may block // waiting on handshakeMutex or the c.out mutex. - return c.conn.Close() + return c.Connection.Close() } var alertErr error @@ -1307,7 +1590,7 @@ func (c *Conn) Close() error { alertErr = c.closeNotify() } - if err := c.conn.Close(); err != nil { + if err := c.Connection.Close(); err != nil { return err } return alertErr diff --git a/handshake_client.go b/handshake_client.go index a1f9212..f50cb52 100644 --- a/handshake_client.go +++ b/handshake_client.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-Go file. -package tls +package xtls import ( "bytes" @@ -251,7 +251,7 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, } // Try to resume a previously negotiated TLS session, if available. - cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config) + cacheKey = clientSessionCacheKey(c.Connection.RemoteAddr(), c.config) session, ok := c.config.ClientSessionCache.Get(cacheKey) if !ok || session == nil { return cacheKey, nil, nil, nil diff --git a/handshake_client_tls13.go b/handshake_client_tls13.go index 886264c..a405bb8 100644 --- a/handshake_client_tls13.go +++ b/handshake_client_tls13.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-Go file. -package tls +package xtls import ( "bytes" @@ -672,7 +672,7 @@ func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error { scts: c.scts, } - cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config) + cacheKey := clientSessionCacheKey(c.Connection.RemoteAddr(), c.config) c.config.ClientSessionCache.Put(cacheKey, session) return nil diff --git a/handshake_messages.go b/handshake_messages.go index dcc8ebf..a7d171b 100644 --- a/handshake_messages.go +++ b/handshake_messages.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-Go file. -package tls +package xtls import ( "fmt" diff --git a/handshake_server.go b/handshake_server.go index 545e2de..7a9cb6e 100644 --- a/handshake_server.go +++ b/handshake_server.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-Go file. -package tls +package xtls import ( "crypto" @@ -827,7 +827,7 @@ func clientHelloInfo(c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo { SignatureSchemes: clientHello.supportedSignatureAlgorithms, SupportedProtos: clientHello.alpnProtocols, SupportedVersions: supportedVersions, - Conn: c.conn, + Conn: c.Connection, config: c.config, } } diff --git a/handshake_server_tls13.go b/handshake_server_tls13.go index 39da02f..394e11b 100644 --- a/handshake_server_tls13.go +++ b/handshake_server_tls13.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-Go file. -package tls +package xtls import ( "bytes" diff --git a/key_agreement.go b/key_agreement.go index 37ed3fd..5358cbe 100644 --- a/key_agreement.go +++ b/key_agreement.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-Go file. -package tls +package xtls import ( "crypto" diff --git a/key_schedule.go b/key_schedule.go index 3b03a1a..35e632c 100644 --- a/key_schedule.go +++ b/key_schedule.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-Go file. -package tls +package xtls import ( "crypto/elliptic" diff --git a/prf.go b/prf.go index 607f80e..5dd7a70 100644 --- a/prf.go +++ b/prf.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-Go file. -package tls +package xtls import ( "crypto" diff --git a/ticket.go b/ticket.go index f2a1fca..7719f9d 100644 --- a/ticket.go +++ b/ticket.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-Go file. -package tls +package xtls import ( "bytes" diff --git a/tls.go b/tls.go index c98a349..bc0eba0 100644 --- a/tls.go +++ b/tls.go @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-Go file. -// Package tls partially implements TLS 1.2, as specified in RFC 5246, +// package xtls partially implements TLS 1.2, as specified in RFC 5246, // and TLS 1.3, as specified in RFC 8446. -package tls +package xtls // BUG(agl): The crypto/tls package only implements some countermeasures // against Lucky13 attacks on CBC-mode encryption, and only on SHA1 @@ -34,8 +34,8 @@ import ( // at least one certificate or else set GetCertificate. func Server(conn net.Conn, config *Config) *Conn { c := &Conn{ - conn: conn, - config: config, + Connection: conn, + config: config, } c.handshakeFn = c.serverHandshake return c @@ -47,9 +47,9 @@ func Server(conn net.Conn, config *Config) *Conn { // InsecureSkipVerify in the config. func Client(conn net.Conn, config *Config) *Conn { c := &Conn{ - conn: conn, - config: config, - isClient: true, + Connection: conn, + config: config, + isClient: true, } c.handshakeFn = c.clientHandshake return c