Skip to content

Commit 64114eb

Browse files
committed
add updated (but incomplete) tests
1 parent bbefc88 commit 64114eb

File tree

1 file changed

+248
-81
lines changed

1 file changed

+248
-81
lines changed

application/transports/wrapping/utls/utls_test.go

Lines changed: 248 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@ package utls
22

33
import (
44
"bytes"
5+
"crypto/aes"
6+
"crypto/cipher"
7+
"crypto/hmac"
58
"crypto/rand"
9+
"crypto/sha256"
610
"encoding/base64"
11+
"encoding/binary"
712
"encoding/hex"
13+
"errors"
14+
"fmt"
815
"io"
16+
"net"
917
"net/http"
1018
"os"
1119
"testing"
@@ -20,79 +28,72 @@ import (
2028
tls "github.com/refraction-networking/utls"
2129
)
2230

23-
func formatClientPacket(reg *cj.DecoyRegistration, params any) ([]byte, error) {
24-
31+
func connect(conn net.Conn, reg *cj.DecoyRegistration) (net.Conn, error) {
2532
// TODO: put these in params
26-
helloID := tls.HelloChrome_102
33+
helloID := tls.HelloChrome_62
2734
config := tls.Config{ServerName: "", InsecureSkipVerify: true}
2835

29-
uTLSConn := tls.UClient(nil, &config, helloID)
36+
uTLSConn := tls.UClient(conn, &config, helloID)
3037
hmacID := reg.Keys.ConjureHMAC(hmacString)
3138

32-
err := uTLSConn.BuildHandshakeState() // Apply our client hello ID
39+
newRand := make([]byte, 32)
40+
_, err := rand.Read(newRand)
3341
if err != nil {
3442
return nil, err
3543
}
36-
uTLSConn.SetClientRandom(hmacID)
37-
err = uTLSConn.MarshalClientHello() // apply the updated ch random value
44+
45+
err = uTLSConn.BuildHandshakeState() // Apply our client hello ID
3846
if err != nil {
3947
return nil, err
4048
}
49+
uTLSConn.SetClientRandom(newRand)
50+
// fmt.Printf("clientRandom set - handshaking %s\n", hex.EncodeToString(hmacID))
4151

42-
return uTLSConn.HandshakeState.Hello.Marshal()
43-
}
44-
45-
func DisabledTestMarshalRandom(t *testing.T) {
46-
hmacID := make([]byte, 32)
47-
_, err := rand.Read(hmacID)
48-
require.Nil(t, err)
49-
50-
helloID := tls.HelloChrome_102
51-
config := tls.Config{ServerName: "", InsecureSkipVerify: true}
52+
uTLSConn.HandshakeState.Hello.SessionId = xorBytes(hmacID, newRand)
5253

53-
uTLSConn := tls.UClient(nil, &config, helloID)
54-
55-
err = uTLSConn.BuildHandshakeState()
56-
require.Nil(t, err)
57-
uTLSConn.SetClientRandom(hmacID)
58-
59-
err = uTLSConn.BuildHandshakeState()
60-
require.Nil(t, err)
61-
62-
// t.Log(hex.EncodeToString(hmacID))
63-
// t.Log(hex.EncodeToString(uTLSConn.HandshakeState.Hello.Random))
64-
65-
b, err := uTLSConn.HandshakeState.Hello.Marshal()
66-
require.Nil(t, err)
54+
err = uTLSConn.MarshalClientHello() // apply the updated ch random value
55+
if err != nil {
56+
return nil, err
57+
}
6758

68-
ch := tls.UnmarshalClientHello(b)
69-
require.NotNil(t, ch)
70-
// t.Log(hex.EncodeToString(ch.Random))
71-
require.True(t, bytes.Equal(ch.Random, hmacID))
59+
return uTLSConn, uTLSConn.Handshake()
7260
}
7361

74-
func DisabledTestMarshalSNI(t *testing.T) {
75-
hmacID := [32]byte{}
76-
_, err := rand.Read(hmacID[:])
77-
require.Nil(t, err)
78-
79-
helloID := tls.HelloChrome_102
80-
config := tls.Config{ServerName: hex.EncodeToString(hmacID[:]), InsecureSkipVerify: true}
81-
82-
uTLSConn := tls.UClient(nil, &config, helloID)
62+
func TestByteRegex(t *testing.T) {
63+
testCases := []struct {
64+
s string
65+
l uint16
66+
}{
67+
{s: "16030100e2000000", l: 226},
68+
{s: "160301ff00000000", l: 65280},
69+
}
8370

84-
err = uTLSConn.BuildHandshakeState()
85-
require.Nil(t, err)
71+
badCases := []string{
72+
"15030100e2000000",
73+
"160301ff",
74+
"0016030100e2000000",
75+
}
8676

87-
b, err := uTLSConn.HandshakeState.Hello.Marshal()
88-
require.Nil(t, err)
77+
for _, c := range testCases {
78+
b, err := hex.DecodeString(c.s)
79+
require.Nil(t, err)
8980

90-
ch := tls.UnmarshalClientHello(b)
91-
require.NotNil(t, ch)
81+
out := tlsHeaderRegex.FindSubmatch(b)
82+
// for _, x := range out {
83+
// t.Logf("%s", hex.EncodeToString(x))
84+
// }
85+
require.Equal(t, 2, len(out))
86+
require.Equal(t, 2, len(out[1]))
87+
u := binary.BigEndian.Uint16(out[1])
88+
require.Equal(t, c.l, u)
89+
}
90+
for _, c := range badCases {
91+
b, err := hex.DecodeString(c)
92+
require.Nil(t, err)
9293

93-
recv, err := hex.DecodeString(ch.ServerName)
94-
require.Nil(t, err)
95-
require.True(t, bytes.Equal(recv, hmacID[:]))
94+
out := tlsHeaderRegex.FindSubmatch(b)
95+
require.Equal(t, 0, len(out))
96+
}
9697
}
9798

9899
func TestSuccessfulWrap(t *testing.T) {
@@ -108,22 +109,46 @@ func TestSuccessfulWrap(t *testing.T) {
108109

109110
message := []byte(`test message!`)
110111

111-
connectMsg, err := formatClientPacket(reg, nil)
112-
require.Nil(t, err)
113-
_, err = c2p.Write(connectMsg)
112+
go func() {
113+
var buf [1501]byte
114+
115+
var wrapped net.Conn
116+
var err error
117+
for {
118+
n, err := sfp.Read(buf[:])
119+
if err != nil {
120+
panic("station read error")
121+
}
122+
123+
reg, wrapped, err = transport.WrapConnection(bytes.NewBuffer(buf[:n]), sfp, reg.PhantomIp, manager)
124+
if errors.Is(err, transports.ErrNotTransport) {
125+
panic("failed to find registration")
126+
} else if errors.Is(err, transports.ErrTransportNotSupported) {
127+
panic("transport supposed to be supported but isn't")
128+
} else if err == nil {
129+
break
130+
} // on transports.ErrTryAgain it should continue loop.
131+
}
132+
133+
stationReceived := make([]byte, len(message))
134+
_, err = io.ReadFull(wrapped, stationReceived)
135+
if err != nil {
136+
panic(fmt.Sprintf("failed ReadFull: %s %s", stationReceived, err))
137+
}
138+
_, err = wrapped.Write(stationReceived)
139+
if err != nil {
140+
panic("failed Write")
141+
}
142+
}()
143+
144+
clientConn, err := connect(c2p, reg)
114145
require.Nil(t, err)
115146

116-
var buf [4096]byte
117-
n, _ := sfp.Read(buf[:])
118-
119-
_, wrapped, err := transport.WrapConnection(bytes.NewBuffer(buf[:n]), sfp, reg.PhantomIp, manager)
120-
require.Nil(t, err, "error getting wrapped connection")
121-
122-
_, err = c2p.Write(message)
147+
_, err = clientConn.Write(message)
123148
require.Nil(t, err)
124149

125150
received := make([]byte, len(message))
126-
_, err = io.ReadFull(wrapped, received)
151+
_, err = io.ReadFull(clientConn, received)
127152
require.Nil(t, err, "failed reading from connection")
128153
require.True(t, bytes.Equal(message, received))
129154
}
@@ -197,31 +222,52 @@ func TestSuccessfulWrapLargeMessage(t *testing.T) {
197222
defer sfp.Close()
198223
require.NotNil(t, reg)
199224

200-
connectMsg, err := formatClientPacket(reg, nil)
201-
require.Nil(t, err)
202-
_, err = c2p.Write(connectMsg)
225+
message := make([]byte, 10000)
226+
_, err := rand.Read(message)
203227
require.Nil(t, err)
204228

205-
var buf [4096]byte
206-
var buffer bytes.Buffer
207-
n, _ := sfp.Read(buf[:])
208-
buffer.Write(buf[:n])
209-
210-
_, wrapped, err := transport.WrapConnection(&buffer, sfp, reg.PhantomIp, manager)
211-
require.Nil(t, err, "error getting wrapped connection")
212-
213-
message := make([]byte, 10000)
214-
_, err = rand.Read(message)
229+
go func() {
230+
var buf [1501]byte
231+
232+
var wrapped net.Conn
233+
var err error
234+
for {
235+
n, err := sfp.Read(buf[:])
236+
if err != nil {
237+
panic("station read error")
238+
}
239+
240+
reg, wrapped, err = transport.WrapConnection(bytes.NewBuffer(buf[:n]), sfp, reg.PhantomIp, manager)
241+
if errors.Is(err, transports.ErrNotTransport) {
242+
panic("failed to find registration")
243+
} else if errors.Is(err, transports.ErrTransportNotSupported) {
244+
panic("transport supposed to be supported but isn't")
245+
} else if err == nil {
246+
break
247+
} // on transports.ErrTryAgain it should continue loop.
248+
}
249+
250+
stationReceived := make([]byte, len(message))
251+
_, err = io.ReadFull(wrapped, stationReceived)
252+
if err != nil {
253+
panic(fmt.Sprintf("failed ReadFull: %s %s", stationReceived, err))
254+
}
255+
_, err = wrapped.Write(stationReceived)
256+
if err != nil {
257+
panic("failed Write")
258+
}
259+
}()
260+
261+
clientConn, err := connect(c2p, reg)
215262
require.Nil(t, err)
216263

217-
_, err = c2p.Write(message)
264+
_, err = clientConn.Write(message)
218265
require.Nil(t, err)
219266

220267
received := make([]byte, len(message))
221-
n, err = io.ReadFull(wrapped, received)
268+
n, err := io.ReadFull(clientConn, received)
222269
require.Nil(t, err, "failed reading from connection")
223-
require.True(t, bytes.Equal(message[:n], received), "xptd: %s\nrecv: %s", hex.EncodeToString(message[:len(received)]), hex.EncodeToString(received))
224-
// t.Log("l:", n)
270+
require.True(t, bytes.Equal(message[:n], received))
225271
}
226272

227273
func TestTryParamsToDstPort(t *testing.T) {
@@ -248,3 +294,124 @@ func TestTryParamsToDstPort(t *testing.T) {
248294
require.Equal(t, testCase.p, port)
249295
}
250296
}
297+
298+
func TestUtlsSessionResumption(t *testing.T) {
299+
// testSubnetPath := os.Getenv("GOPATH") + "/src/github.com/refraction-networking/conjure/application/lib/test/phantom_subnets.toml"
300+
// os.Setenv("PHANTOM_SUBNET_LOCATION", testSubnetPath)
301+
302+
// var transport Transport
303+
// manager := tests.SetupRegistrationManager(tests.Transport{Index: pb.TransportType_Prefix, Transport: transport})
304+
// c2p, sfp, reg := tests.SetupPhantomConnections(manager, pb.TransportType_Prefix)
305+
// defer c2p.Close()
306+
// defer sfp.Close()
307+
// require.NotNil(t, reg)
308+
var err error
309+
c2p, sfp := net.Pipe()
310+
311+
message := []byte(`test message!`)
312+
313+
randVal := [32]byte{}
314+
rand.Read(randVal[:])
315+
316+
go func() {
317+
318+
config := &tls.Config{
319+
Certificates: make([]tls.Certificate, 2),
320+
InsecureSkipVerify: true,
321+
MinVersion: tls.VersionTLS10,
322+
MaxVersion: tls.VersionTLS13,
323+
SessionTicketsDisabled: false,
324+
ClientAuth: tls.NoClientCert,
325+
}
326+
// config.Certificates[0].Certificate = [][]byte{testRSACertificate}
327+
// config.Certificates[0].PrivateKey = testRSAPrivateKey
328+
// config.Certificates[1].Certificate = [][]byte{testSNICertificate}
329+
// config.Certificates[1].PrivateKey = testRSAPrivateKey
330+
// config.BuildNameToCertificate()
331+
config.SetSessionTicketKeys([][32]byte{randVal})
332+
333+
wrapped := tls.Server(sfp, config)
334+
335+
stationReceived := make([]byte, len(message))
336+
_, err := io.ReadFull(wrapped, stationReceived)
337+
if err != nil {
338+
panic(fmt.Sprintf("failed ReadFull: %s %s", stationReceived, err))
339+
}
340+
_, err = wrapped.Write(stationReceived)
341+
if err != nil {
342+
panic("failed Write")
343+
}
344+
}()
345+
346+
sessionTicket := []uint8(`Here goes phony session ticket: phony enough to get into ASCII range
347+
Ticket could be of any length, but for camouflage purposes it's better to use uniformly random contents
348+
and common length. See https://tlsfingerprint.io/session-tickets`)
349+
350+
// clientConn, err := connect(c2p, reg)
351+
config := &tls.Config{ServerName: "", InsecureSkipVerify: true}
352+
353+
// Create a session ticket that wasn't actually issued by the server.
354+
sessionState := tls.MakeClientSessionState(sessionTicket, uint16(tls.VersionTLS12),
355+
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
356+
randVal[:],
357+
nil, nil)
358+
359+
clientTLSConn := tls.UClient(c2p, config, tls.HelloGolang)
360+
require.NotNil(t, clientTLSConn)
361+
362+
err = clientTLSConn.BuildHandshakeState()
363+
require.Nil(t, err)
364+
365+
err = clientTLSConn.SetSessionState(sessionState)
366+
require.Nil(t, err)
367+
368+
_, err = clientTLSConn.Write(message)
369+
require.Nil(t, err)
370+
371+
received := make([]byte, len(message))
372+
_, err = io.ReadFull(clientTLSConn, received)
373+
require.Nil(t, err, "failed reading from connection")
374+
require.True(t, bytes.Equal(message, received))
375+
}
376+
377+
const (
378+
// ticketKeyNameLen is the number of bytes of identifier that is prepended to
379+
// an encrypted session ticket in order to identify the key used to encrypt it.
380+
ticketKeyNameLen = 16
381+
)
382+
383+
// returns the session state and the marshalled sessionTicket, or an error should one occur.
384+
func forgeSession(secret [32]byte) (*tls.ClientSessionState, []byte, error) {
385+
serverState := tls.ForgeServerState(secret)
386+
stateBytes := serverState.Marshal()
387+
388+
encrypted := make([]byte, ticketKeyNameLen+aes.BlockSize+len(stateBytes)+sha256.Size)
389+
keyName := encrypted[:ticketKeyNameLen]
390+
iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
391+
macBytes := encrypted[len(encrypted)-sha256.Size:]
392+
393+
if _, err := io.ReadFull(c.config.rand(), iv); err != nil {
394+
return nil, nil, err
395+
}
396+
397+
copy(keyName, key.KeyName[:])
398+
block, err := aes.NewCipher(key.AesKey[:])
399+
if err != nil {
400+
return nil, nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
401+
}
402+
cipher.NewCTR(block, iv).XORKeyStream(encrypted[ticketKeyNameLen+aes.BlockSize:], stateBytes)
403+
404+
mac := hmac.New(sha256.New, key.HmacKey[:])
405+
mac.Write(encrypted[:len(encrypted)-sha256.Size])
406+
mac.Sum(macBytes[:0])
407+
408+
state := tls.MakeClientSessionState(encrypted, uint16(tls.VersionTLS12),
409+
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
410+
masterSecret,
411+
nil, nil)
412+
413+
return state, encrypted, nil
414+
}
415+
416+
// https://github.com/refraction-networking/utls/blob/c785bd3a1e8dd394d36526a2f3f118a21fc002c5/handshake_server_tls13.go#L736
417+
// https://github.com/refraction-networking/utls/blob/c785bd3a1e8dd394d36526a2f3f118a21fc002c5/handshake_server.go#L769

0 commit comments

Comments
 (0)