Coder Social home page Coder Social logo

refraction-networking / utls Goto Github PK

View Code? Open in Web Editor NEW
1.5K 36.0 225.0 5.12 MB

Fork of the Go standard TLS library, providing low-level access to the ClientHello for mimicry purposes.

License: BSD 3-Clause "New" or "Revised" License

Go 100.00%
tls clienthello parrot low-level-tls golang crypto handshake tls-extension cipher-suites obfuscation

utls's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

utls's Issues

Cannot instantiate extensions due to unexported fields

I am unable instantiate a utls.ClientHelloSpec containing a functional utls.RenegotiationInfoExtension because its renegotiation field is not exported.

E.g., I need to construct something like the following outside the utls package, and then use it with utls.HelloCustom.

utls.ClientHelloSpec{
	Extensions: []utls.TLSExtension{
		&utls.RenegotiationInfoExtension{
			renegotiation: RenegotiateOnceAsClient}
	}
}

RenegotiationInfoExtension does have an exported field, SecureRenegotiation, but looking at RenegotiationInfoExtension.writeToUConn/Len/Read, it appears that renegotiation must be set to something other than its zero value of RenegotiateNever for the extension to get added to the ClientHello and for the HandshakeState.Hello.SecureRenegotiationSupported flag to get set.

utls/u_tls_extensions.go

Lines 217 to 231 in 4da6795

func (e *RenegotiationInfoExtension) Len() int {
switch e.renegotiation {
case RenegotiateOnceAsClient:
fallthrough
case RenegotiateFreelyAsClient:
extBodyLen := len(e.SecureRenegotiation)
if extBodyLen == 0 {
extBodyLen = 1
}
return 4 + extBodyLen
case RenegotiateNever:
default:
}
return 0
}

Incidentally, this code in RenegotiationInfoExtension.Read looks odd. When e.SecureRenegotiation is not empty, secureRenegBody points to e.SecureRenegotiation. And then that data is copied first starting at offset 4 and again at offset 5?

utls/u_tls_extensions.go

Lines 241 to 255 in 4da6795

secureRenegBody := e.SecureRenegotiation
if len(secureRenegBody) == 0 {
secureRenegBody = []byte{0}
}
extBodyLen := len(secureRenegBody)
b[0] = byte(extensionRenegotiationInfo >> 8)
b[1] = byte(extensionRenegotiationInfo & 0xff)
b[2] = byte(extBodyLen >> 8)
b[3] = byte(extBodyLen)
copy(b[4:], secureRenegBody)
if len(e.SecureRenegotiation) != 0 {
copy(b[5:], e.SecureRenegotiation)
}

In addition, I can't instantiate a utls.GenericExtension as both of its fields are unexported.

utls/u_tls_extensions.go

Lines 367 to 370 in 4da6795

type GenericExtension struct {
id uint16
data []byte
}

utls is not sending GREASE

Hello, I was trying to achieve Chrome's CipherSuite using utls while I got this:

image

on the left is Chrome's Client Hello compared to that of utls on the left

This is the JA3 I'm trying with
771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-21,29-23-24,0

image

The issue is this server is blocking requests without GREASE, so I get a Decode Error when I send with utls right now

image

How can I make utls send GREASE?

  • Environment:
$ go version
go version go1.16.7 windows/amd64

"uTLS does not support 0x0 as min version" with `tls.HelloRandomized`

I'm using utls commit b84d7d5. Running this program:

package main

import (
	"fmt"
	"os"
	"net"

	tls "github.com/refraction-networking/utls"
)

func run() error {
	conn, err := net.Dial("tcp", "tlsfingerprint.io:443")
	if err != nil {
		return err
	}
	uconn := tls.UClient(conn, nil, tls.HelloRandomized)
	uconn.SetSNI("tlsfingerprint.io")
	return uconn.Handshake()
}

func main() {
	err := run()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

Fails with the error message:

uTLS does not support 0x0 as min version

It also happens with HelloRandomizedALPN and HelloRandomizedNoALPN. It doesn't happen with HelloChrome_70 and HelloFirefox_63.

Consider forking/merging strategy for BoringSSL test runner

BoringSSL has a fork of golang's tls implementation as well that it uses as a runner for it's test suite. There are a number of potential holes in uTls that could be plugged with implementations already written in this wrapper (certificate compression, channel ids, upcoming quic, etc). I'm still trying to understand exactly how this could work (still poking through utls to understand it), but it seems like a promising option to keep in sync as tls signatures change.

utls unconditionally sets SessionID in all ClientHellos

While attempting to construct a TLS 1.2 parrot that omits the SessionID, I have observed via packet capture that utls always sends a SessionID in the ClientHello, regardless of whether a session ticket is attached.

I believe this is due to changes in the upstream crypto/tls when TLS 1.3 support was added.

As far as I understand it, utls calls makeClientHello here, and uses the results as the base ClientHello:

utls/u_parrots.go

Lines 494 to 500 in 4da6795

privateHello, ecdheParams, err := uconn.makeClientHello()
if err != nil {
return err
}
uconn.HandshakeState.Hello = privateHello.getPublicPtr()
uconn.HandshakeState.State13.EcdheParams = ecdheParams
hello := uconn.HandshakeState.Hello

Here's where the current makeClientHello, from crypto/tls, unconditionally sets the SessionID:

utls/handshake_client.go

Lines 111 to 116 in 4da6795

// A random session ID is used to detect when the server accepted a ticket
// and is resuming a session (see RFC 5077). In TLS 1.3, it's always set as
// a compatibility measure (see RFC 8446, Section 4.1.2).
if _, err := io.ReadFull(config.rand(), hello.sessionId); err != nil {
return nil, nil, errors.New("tls: short read from Rand: " + err.Error())
}

This is probably desired for TLS 1.3 parrots, as my brief testing shows similar behavior in at least one browser ("compatibility" mode).

In older crypto/tls and utls, makeClientHello doesn't have this unconditional code block and the SessionID is set conditionally, when there's a session ticket, here:

utls/handshake_client.go

Lines 150 to 159 in 03d875d

if session != nil {
hello.sessionTicket = session.sessionTicket
// A random session ID is used to detect when the
// server accepted the ticket and is resuming a session
// (see RFC 5077).
hello.sessionId = make([]byte, 16)
if _, err := io.ReadFull(c.config.rand(), hello.sessionId); err != nil {
return errors.New("tls: short read from Rand: " + err.Error())
}
}

Has the on-the-wire format of parrots such as HelloIOS_11_1 changed since utls was upgraded to support TLS 1.3?

Of the bundled TLS 1.2 parrots, I can't tell whether the SessionID should or should not be set when there is no session ticket. I didn't find any stats regarding SessionID here, https://tlsfingerprint.io. Is this information in your raw database?

With the existing API, I don't see how to create a parrot that omits the SessionID. It appears not difficult to implement, as something like this can be added after the makeClientHello call in ApplyPreset:

if p.NoDefaultSessionID {
	uconn.HandshakeState.Hello.SessionId = nil
}

NoDefaultSessionID would be a new ClientHelloSpec boolean field that defaults to false. When a session ticket is used, other code already sets a SessionID [again] as required.

It would be simpler to skip the new field and remove the default SessionID for TLS 1.2. I'm not sure that produces correct results in all cases though?

Add Chrome 65 fingerprint

Chrome 65 TLS 1.3 ciphers are PSK-only, so they shouldn't get picked in the wild.

Included extensions do worry me, they totally have distinctive content, but current tlsfingerprint.io infrastructure doesn't handle them yet. Deeper dive is needed.

Seeing how tls 1.2 ClientHello messages are still relevant, I'd rather for Golang crypto/tls to implement tls 1.3, pull it, and then start parroting 1.3 stuff.

Custom `tls.ClientHelloSpec`s failing

I've tried the top four specs from tlsfingerprint.io and all have issues.

The top three fail with similar error messages (all top three seem to be chrome-related). I've assembled a list of potential error messages outputted when attempting to use these configurations:

Top Fingerprint Hash Errors received (in no particular order)
1 8466c4390d4bc355 uTLS does not support 0x**** as max version
2 133e933dd1dfea90 uTLS does not support 0x**** as max version
3 256c61ee90fe805f unsupported Curve in KeyShareExtension: 43690.
uTLS does not support 0x**** as max version
4 cd9f86e32edc16fb x509: certificate is valid for *.example-1.com, not example-2.com

Of course for the purpose of this issue, i've compressed my code and removed arbitrary error handling code. This is my approach to making TLS requests using uTLS.

	dial, _ := net.DialTimeout("tcp", u.Host, time.Duration(15)*time.Second)

	utlsConn := tls.UClient(dial, config, tls.HelloCustom)
	defer utlsConn.Close()
	_ := utlsConn.ApplyPreset(&Fingerprints.HASH_TOP_4) // top 1-4
	_ := utlsConn.Handshake()

	request, _ := http.NewRequest(method, u.String(), bytes.NewBuffer(body))
	request.Close = true
	defer request.Body.Close()

	switch utlsConn.ConnectionState().NegotiatedProtocol {
	case "h2":
		request.Proto = "HTTP/2.0"
		request.ProtoMajor = 2
		request.ProtoMinor = 0

		tr := http2.Transport{}
		cConn, _ := tr.NewClientConn(connection)

		response_obj, response_err = cConn.RoundTrip(request)
	case "http/1.1", "":
		request.Proto = "HTTP/1.1"
		request.ProtoMajor = 1
		request.ProtoMinor = 1

		_ := request.Write(connection)

		response_obj, response_err = http.ReadResponse(bufio.NewReader(connection), request)
	default:
		panic(fmt.Errorf("ALPN not supported"))
	}

	resp, _ := httputil.DumpResponse(response_obj, true)
	println(string(resp))

I would like to add that tlsfingerprint.io's "generated code" is invalid. tls.SignatureScheme is formatted as just SignatureScheme, the renegotiation field should be capitalized, and other small bugs.

x

x

tls: server selected unsupported protocol version 304

thanks for this library. i use it to connect some website, but response is error( tls: server selected unsupported protocol version 304)
var ja3 = '771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-21,29-23-24,0';
var ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36';

Unable to compile to wasm

I have a script in Go that uses uTLS however when I try to compile it to wasm using GOOS=js GOARCH=wasm go build -o main.wasm client.go I get the error shown below:

#github.com/refraction-networking/utls/cpu
../go/pkg/mod/github.com/refraction-networking/[email protected]/cpu/cpu.go:16:16: undefined: CacheLineSize

../go/pkg/mod/github.com/refraction-networking/[email protected]/cpu/cpu.go:33:16: undefined: CacheLineSize

../go/pkg/mod/github.com/refraction-networking/[email protected]/cpu/cpu.go:46:14: undefined: CacheLineSize

../go/pkg/mod/github.com/refraction-networking/[email protected]/cpu/cpu.go:58:14: undefined: CacheLineSize

../go/pkg/mod/github.com/refraction-networking/[email protected]/cpu/cpu.go:66:14: undefined: CacheLineSize

../go/pkg/mod/github.com/refraction-networking/[email protected]/cpu/cpu.go:76:14: undefined: CacheLineSize

What I expected to happen: build to succeed without error

When building to Linux or Windows binaries it works fine. However I get this issue with building to wasm. Please can someone let me know what I would need to do to make this build work, or if this build isn't possible.

tls packet

Can we change the tls packet in Client-TLSv12-UTLS-ECDHE-ECDSA-AES128-GCM-SHA256-Firefox-63, etc. so that we can have a fingerprint similar to Chrome or ...?

Stronger guards against session ticket resumption with tls 1.3 and custom parrots?

We seem to be having some issues with some of the parrots and session ticket resumption.

It looks to me like session ticket resumption is not currently supported with tls 1.3 and custom parrots. This snippet of loadSession seems to be re-marshalling the client hello with updateBinders, which assumes that the raw bytes have a extensionPreSharedKey at the end:
https://github.com/getlantern/utls/blob/0c02248f7ce1fa1928b137ff77d6bad4cb0486aa/handshake_client.go#L281-L333

utls/handshake_messages.go

Lines 323 to 351 in 33a2903

// updateBinders updates the m.pskBinders field, if necessary updating the
// cached marshaled representation. The supplied binders must have the same
// length as the current m.pskBinders.
func (m *clientHelloMsg) updateBinders(pskBinders [][]byte) {
if len(pskBinders) != len(m.pskBinders) {
panic("tls: internal error: pskBinders length mismatch")
}
for i := range m.pskBinders {
if len(pskBinders[i]) != len(m.pskBinders[i]) {
panic("tls: internal error: pskBinders length mismatch")
}
}
m.pskBinders = pskBinders
if m.raw != nil {
lenWithoutBinders := len(m.marshalWithoutBinders())
// TODO(filippo): replace with NewFixedBuilder once CL 148882 is imported.
b := cryptobyte.NewBuilder(m.raw[:lenWithoutBinders])
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
for _, binder := range m.pskBinders {
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes(binder)
})
}
})
if len(b.BytesOrPanic()) != len(m.raw) {
panic("tls: internal error: failed to update binders")
}
}
}

This has garbled the ends of ClientHellos that we've generated when using session tickets and parrots that support tls1.3.

It seems to me that basically, utls in its present form doesn't support the combination of:

  • session ticket resumption
  • parrots
  • tls 1.3

Am I wrong about that? Are we somehow holding the library incorrectly? I've included some pcaps demonstrating the issue we've been encountering and if it would help, I think I could write up a test which demonstrates the behavior we're encountering. I'd be happy to find out that I'm just missing some key piece somewhere.

If I'm not wrong about this, is there some way to put up stronger guards against doing this?

Lastly, is implementing the pre_shared_key extension on your radar/something you'd be interested in? If it's just a matter of your time/energy/resources, I think our org could at least attempt a PR?

As always, thank you so much for your time, energy and patience on this!!!

chrome-83-clienthello-errs.pcap.gz
firefox-65-clienthello-errs.pcap.gz

Panic in toPublic12

We have encountered this panic in utls:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0xd1690b88]

goroutine 24162 [running]:
github.com/Psiphon-Labs/psiphon-tunnel-core/vendor/github.com/refraction- networking/utls.(*clientHandshakeState).toPublic12(0x935a81e0, 0xd1f18f78)
/go/src/github.com/Psiphon-Labs/psiphon-tunnel-core/vendor/github.com/refraction- networking/utls/u_public.go:136 +0x12c
github.com/Psiphon-Labs/psiphon-tunnel-core/vendor/github.com/refraction-networking/utls.(*UConn).clientHandshake(0x934ea2d0, 0xd1f18f78, 0x936b40e0)
/go/src/github.com/Psiphon-Labs/psiphon-tunnel-core/vendor/github.com/refraction-networking/utls/u_conn.go:372 +0x4a4
github.com/Psiphon-Labs/psiphon-tunnel-core/vendor/github.com/refraction-networking/utls.(*UConn).Handshake(0x934ea2d0, 0x0, 0x0)
/go/src/github.com/Psiphon-Labs/psiphon-tunnel-core/vendor/github.com/refraction-networking/utls/u_conn.go:189 +0x11c

The panic is here:

utls/u_public.go

Lines 131 to 136 in b7c656e

func (chs12 *clientHandshakeState) toPublic12() *ClientHandshakeState {
if chs12 == nil {
return nil
} else {
tls12State := TLS12OnlyState{
Suite: *chs12.suite.getPublicPtr(),

I suspect suite is nil?

toPublic12 is called from here, and is called even when hs12.handshake returns an error:

utls/u_conn.go

Lines 371 to 375 in b7c656e

err = hs12.handshake()
c.HandshakeState = *hs12.toPublic12()
if err != nil {
return err
}

handshake calls processServerHello which calls pickCipherSuite which can fail and leave suite nil:

utls/handshake_client.go

Lines 429 to 433 in b7c656e

func (hs *clientHandshakeState) pickCipherSuite() error {
if hs.suite = mutualCipherSuite(hs.hello.cipherSuites, hs.serverHello.cipherSuite); hs.suite == nil {
hs.c.sendAlert(alertHandshakeFailure)
return errors.New("tls: server chose an unconfigured cipher suite")
}

So it appears that when the "server chose an unconfigured cipher suite" error occurs, suite is nil and toPublic12 will panic since it's called before the err check?

There's a similar case where toPublic13 is called before checking err here:

utls/u_conn.go

Lines 363 to 365 in b7c656e

err = hs13.handshake()
c.HandshakeState = *hs13.toPublic13()
return err

Any plans to implement it in Rust?

I see you also developing in rust, so any plans to reimplement same functionality(or like a lite version) in rust(probably using boringssl or openssl)?

tlsfingerprint.io status

Hi,

Does anyone have any idea of the status of tlsfingerprint.io? it seems the cluster / database is dead... there are no id's on the site... client.tlsfingerprint.io does not work...

if this site is now dead, will it be made open source? it was a really great tool to use... and if it is indeed dead, it a very sad day for the security community

Calling Read before Write ignores the fingerprint

Hello
If you call Read before Handshake or Write, the default go ciphers will be used. This is because the Write method is implemented for UConn and the handshake in it will call the utls handshake. But the Read method is implemented by Conn and executing it will cause the Read to call the tls handshake instead of utls one.
Is this intended? And if so, is there anyway (except Handshake) to establish the connection with fingerprints by calling Read at first?
Thanks

Construct a `utls.Config` from a `tls.Config`

At #16 (comment) I had to adapt utls to the interface of http2.Transport. The DialTLS callback receives a golang net/crypto tls.Config. To create a UClient I need a utls.Config. I can't just pass on the tls.Config, because the two types are incompatible.

I manually created a new utls.Config and copied the NextProtos member because it was important for my example, but in general there are other important fields that may need to be copied.

uconn := utls.UClient(conn, &utls.Config{NextProtos: cfg.NextProtos}, *utlsClientHelloID)

Mainline crypto/tls provides a Config.Clone method that internally knows which fields are safe to copy (not all are safe to copy, for example locks). Does it make sense to have something similar to create a utls.Config from an existing tls.Config?

Randomized fingerprints, but consistent?

HelloRandomized and friends provide a randomized fingerprint, but it is re-randomized for each new connection. Is there a way to get a "handle" to a specific randomized fingerprint so it can be re-used within a process?

Here's a sample program that makes 10 connections with HelloRandomizedNoALPN:

package main

import (
	"fmt"
	tls "github.com/refraction-networking/utls"
	"net"
)

const hostName = "golang.org"

func connect() error {
	conn, err := net.Dial("tcp", net.JoinHostPort(hostName, "443"))
	if err != nil {
		return err
	}
	uconn := tls.UClient(conn, nil, tls.HelloRandomizedNoALPN)
	uconn.SetSNI(hostName)
	err = uconn.Handshake()
	if err != nil {
		return err
	}
	return uconn.Close()
}

func main() {
	for i := 0; i < 10; i++ {
		err := connect()
		if err == nil {
			fmt.Println(i, "ok")
		} else {
			fmt.Println(i, err)
		}
	}
}

Sample output is:

0 ok
1 remote error: tls: handshake failure
2 ok
3 remote error: tls: handshake failure
4 ok
5 ok
6 ok
7 ok
8 ok
9 ok

I am using tshark to verify that the fingerprint changes with each connection:

tshark -V -f 'host golang.org' -Y ssl.handshake.ciphersuites

Incidentally, the reason this came up is because I'm trying to use UClient inside the DialTLS of http.Transport in order to get TLS fingerprint obfuscation along with connection reuse, keepalive, and the other features that http.Transport provides. When used with utls, http.Transport seems to create 10 independent connections (each with its own randomized fingerprint). I'll file a separate issue about that.

Session tickets from ClientSessionCache are not used

In testing, it appears that session states/tickets set using SetSessionCache are not added to ClientHellos. Individual session states/tickets explicitly set using SetSessionState are added to ClientHellos, as long as SetSessionState is called before BuildHandshakeState.

I suspect the issue with the cache is that Handshake calls BuildHandshakeState before clientHandshake is called here:

utls/u_conn.go

Lines 182 to 189 in f8425e6

// [uTLS section begins]
err := c.BuildHandshakeState()
if err != nil {
return err
}
// [uTLS section ends]
c.handshakeErr = c.clientHandshake()

clientHandshake consults the session cache and attempts to use session tickets, but its call to SetSessionState comes too late:

utls/u_conn.go

Lines 312 to 334 in f8425e6

cacheKey, session, earlySecret, binderKey := c.loadSession(hello)
if cacheKey != "" && session != nil {
defer func() {
// If we got a handshake failure when resuming a session, throw away
// the session ticket. See RFC 5077, Section 3.2.
//
// RFC 8446 makes no mention of dropping tickets on failure, but it
// does require servers to abort on invalid binders, so we need to
// delete tickets to recover from a corrupted PSK.
if err != nil {
c.config.ClientSessionCache.Put(cacheKey, nil)
}
}()
}
if !sessionIsAlreadySet { // uTLS: do not overwrite already set session
err = c.SetSessionState(session)
if err != nil {
return
}
}
if _, err := c.writeRecord(recordTypeHandshake, hello.marshal()); err != nil {

There's a call to hello.marshal after the session cache is checked, but it will just use the previously marshaled value stored in hello.Raw set in MarshalClientHello which is called by BuildHandshakeState:

utls/u_conn.go

Line 468 in f8425e6

hello.Raw = helloBuffer.Bytes()

As a result, it appears that TLS session resumption won't work, as session tickets received from servers and stored in the cache will never be sent in ClientHellos?

I didn't try setting a cache directly using Config.ClientSessionCache; it appears that would have the same issue?

Improve tests

Need more tests than what was covered at release time.

  • Add tests for no parrot
  • Add tests for randomized -- current testing framework only does pre-recorded replays, making it difficult to test always random FP
  • Add session ticket tests
  • Add set client random tests
  • Integrate tlsfingerprint.io to automatically confirm correctness

Grease is sometimes repeating values

GREASE values are sometimes repeated in the current setup. There are some CDNs and sites that are running something on the backend that is rejecting this, but it happens very infrequently, so it's hard to pickup on. I do not know how to write a unit test for this, so I've been putting off submitting a pr for you. You can see the basic code changes here:
ulixee@3b28961

Basically, the current version doesn't actually check if the resulting value is changed.

Use recorded ClientHello messages as templates/fingerprints

We think it could be useful to generate client hello messages from recorded bytes.

Ideally we would like to contribute this upstream, so I wanted to see if you had any thoughts/concerns/ideas about this approach towards the outset.

We had two ideas about how we might proceed:

Idea 1

Attempt to create a ClientHelloSpec from a []byte slice, something along the lines of: func FingerPrintClientHello(data []byte) (*ClientHelloSpec, error)

This seems to mostly require figuring out how to populate the []TLSExtension slice. We could use the clientHelloMsg.unmarshal to get information about the Extensions that go's crypto/tls library understands/parses, but that leaves us with at least a handful of []TLSExtensions that we'd have to go back through the raw bytes and attempt to re-parse. As we re-parse the hello, we could look specifically for extensions which go doesn't support/parse and then use the GenericExtension type to just stuff those bytes into our hello.

It occurs to me that re-parsing likely suffers from 2 problems:

  1. I think that our extension could end up out of order from the recorded one?
  2. We're basically going to have to move through the whole client hello byte slice anyways in order to parse the generic extensions

At this point it might be worth just writing a parallel unmarshal function which does what we need, but I guess I get a little nervy at making a parallel copy of so much core crypto/tls code?

Idea 2

Use the recorded bytes to populate a clientHelloMsg.raw, unmarshal those bytes to fill out the rest of the relevant clientHelloMsg fields, update Config and UConn state to reflect any necessary changes, and update the ClientHello as appropriate (random, etc).
In the Custom Handshake section of the README, it says:

If you need to manually control all the bytes on the wire(certainly not recommended!), you can set UConn.HandshakeStateBuilt = true, and marshal clientHello into UConn.HandshakeState.Hello.raw yourself. In this case you will be responsible for modifying other parts of Config and ClientHelloMsg to reflect your setup and not confuse "crypto/tls", which will be processing response from server.

I'm not actually seeing that field on the UConn struct? Additionally, it looks to me that currently BuildHandshakeState() and then consequently the MarshalClientHello() are called by Handshake() and that they will modify raw bytes in any constructed ClientHelloMsg

So this approach doesn't seem possible with the current u_conn.go behavior and also I worry that it could suffer from some tricky state/config translations.

Thanks so much for your time and patience with this! We'd love to hear what you think about it!

Using utls with `http.Transport`

I'm using commit a89e7e6. The examples I have found of using utls with HTTPS all make a single request on a single connection, then throw the connection away. For example, httpGetOverConn in examples.go.

I'm trying to use utls with http.Transport, to take advantage of persistent connections and reasonable default timeouts. To do this, I'm hooking into the DialTLS callback. There is a problem when using a utls fingerprint that includes h2 in ALPN and a server that supports HTTP/2. The server switches to HTTP/2 mode, but the client stays in HTTP/1.1 mode, because net/http disables automatic HTTP/2 support whenever DialTLS is set. The end result is an HTTP/1.1 client speaking to an HTTP/2 server; i.e, a similar problem as what was reported in golang/go#14275 (comment). The error message differs depending on the fingerprint:

HelloFirefox_63
net/http: HTTP/1.x transport connection broken: malformed HTTP response "\x00\x00\x12\x04\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00d\x00\x04\x00\x10\x00\x00\x00\x06\x00\x00@\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x0f\x00\x01\x00\x00\x1e\a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01http2_handshake_failed"
HelloChrome_70
local error: tls: unexpected message
HelloIOS_11_1
2019/01/11 14:48:56 Unsolicited response received on idle HTTP channel starting with "\x00\x00\x12\x04\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00d\x00\x04\x00\x10\x00\x00\x00\x06\x00\x00@\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x0f\x00\x01"; err=<nil>
readLoopPeekFailLocked: <nil>

I get the same results even if I pre-configure the http.Transport with HTTP/2 support by calling http2.ConfigureTransport(tr).

I wrote a test program to reproduce these results. It takes a -utls option to select a utls client hello ID, and a -callhandshake option to control whether to call UConn.Handshake within DialTLS, or allow it to be called implicitly by the next Read or Write. I included the latter option because I found that not calling UConn.Handshake inside DialTLS avoids the HTTP version mismatch; however it also results in a client hello that lacks ALPN and differs from the requested one in other ways, so it's not an adequate workaround.

Click to expand program
package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strings"

	utls "github.com/refraction-networking/utls"
)

func main() {
	utlsClientHelloIDName := flag.String("utls", "", "use utls with the given ClientHelloID (e.g. HelloGolang)")
	callHandshake := flag.Bool("callhandshake", false, "call UConn.Handshake inside DialTLS")
	flag.Parse()

	if *callHandshake && *utlsClientHelloIDName == "" {
		fmt.Fprintf(os.Stderr, "error: -callhandshake only makes sense with -utls\n")
		os.Exit(1)
	}

	if flag.NArg() != 1 {
		fmt.Fprintf(os.Stderr, "error: need a URL\n")
		os.Exit(1)
	}
	url := flag.Arg(0)

	utlsClientHelloID, ok := map[string]*utls.ClientHelloID{
		"":                      nil,
		"HelloGolang":           &utls.HelloGolang,
		"HelloRandomized":       &utls.HelloRandomized,
		"HelloRandomizedALPN":   &utls.HelloRandomizedALPN,
		"HelloRandomizedNoALPN": &utls.HelloRandomizedNoALPN,
		"HelloFirefox_Auto":     &utls.HelloFirefox_Auto,
		"HelloFirefox_55":       &utls.HelloFirefox_55,
		"HelloFirefox_56":       &utls.HelloFirefox_56,
		"HelloFirefox_63":       &utls.HelloFirefox_63,
		"HelloChrome_Auto":      &utls.HelloChrome_Auto,
		"HelloChrome_58":        &utls.HelloChrome_58,
		"HelloChrome_62":        &utls.HelloChrome_62,
		"HelloChrome_70":        &utls.HelloChrome_70,
		"HelloIOS_Auto":         &utls.HelloIOS_Auto,
		"HelloIOS_11_1":         &utls.HelloIOS_11_1,
	}[*utlsClientHelloIDName]
	if !ok {
		fmt.Fprintf(os.Stderr, "unknown client hello ID %q\n", *utlsClientHelloIDName)
		os.Exit(1)
	}

	tr := http.DefaultTransport.(*http.Transport)
	if utlsClientHelloID != nil {
		tr.DialContext = nil
		tr.Dial = func(network, addr string) (net.Conn, error) { panic("Dial should not be called") }
		tr.DialTLS = func(network, addr string) (net.Conn, error) {
			fmt.Printf("DialTLS(%q, %q)\n", network, addr)
			if tr.TLSClientConfig != nil {
				fmt.Printf("warning: ignoring TLSClientConfig %v\n", tr.TLSClientConfig)
			}
			conn, err := net.Dial(network, addr)
			if err != nil {
				return nil, err
			}
			uconn := utls.UClient(conn, nil, *utlsClientHelloID)
			colonPos := strings.LastIndex(addr, ":")
			if colonPos == -1 {
				colonPos = len(addr)
			}
			uconn.SetSNI(addr[:colonPos])
			if *callHandshake {
				err = uconn.Handshake()
			}
			return uconn, err
		}
	}

	for i := 0; i < 4; i++ {
		resp, err := get(tr, url)
		if err != nil {
			fmt.Printf("%2d err %v\n", i, err)
		} else {
			fmt.Printf("%2d %s %s\n", i, resp.Proto, resp.Status)
		}
	}
}

func get(rt http.RoundTripper, url string) (*http.Response, error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	resp, err := rt.RoundTrip(req)
	if err != nil {
		return nil, err
	}
	// Read and close the body to enable connection reuse with HTTP/1.1.
	_, err = io.Copy(ioutil.Discard, resp.Body)
	if err != nil {
		return nil, err
	}
	err = resp.Body.Close()
	if err != nil {
		return nil, err
	}
	return resp, nil
}

Sample usage:

test -utls HelloFirefox_63 -callhandshake https://golang.org/robots.txt

The output of the program appears in the following table. Things to notice:

  • DialTLS with HelloGolang produces a fingerprint that is different from using http.Transport without DialTLS set.
  • HelloFirefox_63, HelloChrome_70, and HelloIOS_11_1 all provide a usable connection (but with an incorrect fingerprint), as long as you don't call UConn.Handshake before returning from DialTLS.
  • HelloFirefox_63, HelloChrome_70, and HelloIOS_11_1 all give the correct fingerprint, but fail with an HTTP version mismatch, when UConn.Handshake is called inside DialTLS.
Client Hello ID call Handshake? client ALPN result
none N/A [h2, http/1.1] ok HTTP/2
-utls HelloGolang none ok HTTP/1.1
-utls HelloGolang -callhandshake none ok HTTP/1.1
-utls HelloFirefox_63 none ok HTTP/1.1
-utls HelloFirefox_63 -callhandshake [h2, http/1.1] malformed HTTP response (HTTP/1.1 client, HTTP/2 server)
-utls HelloChrome_70 none ok HTTP/1.1
-utls HelloChrome_70 -callhandshake [h2, http/1.1] local error: tls: unexpected message
-utls HelloIOS_11_1 none ok HTTP/1.1
-utls HelloIOS_11_1 -callhandshake [h2, h2-16, h2-15, h2-14, spdy/3.1, spdy/3, http/1.1] readLoopPeekFailLocked: <nil> (HTTP/1.1 client, HTTP/2 server)

Is there a way to accomplish what I am trying to do?

Usage with proxy?

What's the best way to use forward proxies with this tls implementation? Right now I'm creating a HTTP tunnel followed by a custom Handshake, but by using a proxy the server blocks my request. Everything works fine without the proxy. I also tested the proxy in Chrome, which also works without any issues.

I send the final requests with the function httpGetOverConn that is given in examples.go. This is the code I am using for creating the HTTP tunnel.

func connToProxy(proxyURL string, proxyPort string, remoteAddress string,
	remotePort string, credentials string) (TLSConn, error) {
	addr, err := net.LookupHost(proxyURL)
	if err != nil {
		return TLSConn{}, err
	}

	dialConn, err := net.DialTimeout("tcp", addr[0] + ":" + proxyPort, dialTimeout)

	if err != nil {
		return TLSConn{}, fmt.Errorf("net.DialTimeout error: %+v", err)
	}

	parsedURL, err := url.Parse("https://" + remoteAddress + ":" + remotePort)

	credentials = base64.StdEncoding.EncodeToString([]byte(credentials))
	_, err = dialConn.Write([]byte("CONNECT " + parsedURL.Host +  " HTTP/1.1\r\n" +
		"Host: " + parsedURL.Host + "\r\n" +
		"Proxy-Connection: keep-alive\r\n" +
		"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36\r\n" +
		"Proxy-Authorization: Basic " + credentials +
		"\r\n\r\n",
	))

	br := make([]byte, 1024)
	_, err = dialConn.Read(br)
	if err != nil {
		return TLSConn{}, err
	}

	config := tls.Config{ServerName: remoteAddress}
	uTlsConn := tls.UClient(dialConn, &config, tls.HelloCustom)

	spec := getClientHello()
	err = uTlsConn.ApplyPreset(&spec)

	err = uTlsConn.Handshake()
	if err != nil {
		return TLSConn{}, fmt.Errorf("uTlsConn.Handshake() error: %+v", err)
	}

	return TLSConn{CustomConn: uTlsConn, Alpn: uTlsConn.HandshakeState.ServerHello.AlpnProtocol}, nil
}

Transport Connection Broken

Hello,

I am currently facing an issue with sending a GET request to https://http2.pro/api/v1.

The steps to reproduce:

  • git clone https://github.com/mehtaabGill/utls-test.git
  • cd utls-test
  • go get
  • go run main.go

The error i am receiving:

panic: Get "https://http2.pro/api/v1": net/http: HTTP/1.x transport connection broken: malformed HTTP status code "bad"

goroutine 1 [running]:
main.main()
        C:/Users/mehta/go/src/github.com/mehtaabGill/utls-test/main.go:23 +0x4f3
exit status 2

What I expected to see:
A proper JSON response which indicates I sent a request with a connection that supports HTTP2

Note - I am using the HelloChrome_83 ClientHelloID

Implement CertCompressionAlgo extension

Env: Go 1.14, Windows amd64

Whenever I use the client hello of chrome for version 70, 72, or 83, the program will run into this error

HttpGetByHelloID(HelloChrome_70) failed: uTlsConn.Handshake() error: local error: tls: unexpected message

How to reproduce:

  1. Change the example to hello chrome of the versions mentioned above
  2. Run it

Sends SNI containing an IP address, when real implementations would not

If you use a utls.Config with a ServerName that is an IP address, uTLS v1.0.0 sends a server_name extension, whereas the real implementations that uTLS is mimicking do not send server_name when it would contain an IP address. (Neither does crypto/tls.)

uTLS should omit the server_name extension when it would contain an IP address. (But it should still use the IP address for certificate validation. There are TLS servers that have certificates for IP addresses, for example https://1.1.1.1/.)

It is actually a protocol violation to put an IP address in server_name:

Literal IPv4 and IPv6 addresses are not permitted in "HostName".

Here is where crypto/tls internally checks whether the SNI string is an IP address, and removes the extension if it is:
https://github.com/golang/go/blob/4300f105147dc0da9d1034704ad1cd24bedde5da/src/crypto/tls/handshake_client.go#L1004-L1006

if net.ParseIP(host) != nil {
	return ""
}

And here it is in Firefox (I think):
https://searchfox.org/mozilla-central/source/security/nss/lib/ssl/ssl3exthandle.c#30-34

/* must not be an IPv4 or IPv6 address */
if (PR_SUCCESS == PR_StringToNetAddr(url, &netAddr)) {
    /* is an IP address (v4 or v6) */
    return PR_FALSE;
}

A workaround for uTLS is to inspect ServerName after calling UClient but before calling Handshake, and calling RemoveSNIExtension (from #51) if it is an IP address.

Here is a demo program (go run ip-sni.go):

ip-sni.go
package main

import (
	// "crypto/tls"
	"fmt"
	"net"
	"os"

	utls "github.com/refraction-networking/utls"
)

func run() error {
	conn, err := net.Dial("tcp", "1.1.1.1:853")
	if err != nil {
		return err
	}
	defer conn.Close()

	// config := utls.Config{} // "either ServerName or InsecureSkipVerify must be specified in the tls.Config"
	config := utls.Config{ServerName: "1.1.1.1"}
	tlsConn := utls.UClient(conn, &config, utls.HelloFirefox_65)
	/*
		// workaround:
		if net.ParseIP(config.ServerName) != nil {
			err := tlsConn.RemoveSNIExtension()
			if err != nil {
				return err
			}
		}
	*/
	err = tlsConn.Handshake()
	if err != nil {
		return err
	}
	defer tlsConn.Close()

	/*
		// crypto/tls version:
		config := tls.Config{ServerName: "1.1.1.1"}
		tlsConn := tls.Client(conn, &config)
		err = tlsConn.Handshake()
		if err != nil {
			return err
		}
		defer tlsConn.Close()
	*/

	fmt.Printf("%s connected\n", tlsConn.RemoteAddr())

	return nil
}

func main() {
	err := run()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

I cannot give a fingerprint ID because https://tlsfingerprint.io/pcap is not working for me. But here is the Client Hello produced by the above program with uTLS v1.0.0 and go1.15.9:

uTLS fingerprint (note server_name="1.1.1.1")
Secure Sockets Layer
    TLSv1 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 512
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Length: 508
            Version: TLS 1.2 (0x0303)
            Random: d479fe839284e6cd5a05a57ac3c614480443718e0b4801b6...
                GMT Unix Time: Dec 17, 2082 13:26:43.000000000 MST
                Random Bytes: 9284e6cd5a05a57ac3c614480443718e0b4801b61c3ed70b...
            Session ID Length: 32
            Session ID: f89a6e0030c3f0d579e6fd47aa9bf3ba8f8666caf5d76c8c...
            Cipher Suites Length: 36
            Cipher Suites (18 suites)
                Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
                Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303)
                Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9)
                Cipher Suite: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
                Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x0033)
                Cipher Suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x0039)
                Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
                Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
                Cipher Suite: TLS_RSA_WITH_3DES_EDE_CBC_SHA (0x000a)
            Compression Methods Length: 1
            Compression Methods (1 method)
                Compression Method: null (0)
            Extensions Length: 399
            Extension: server_name (len=12)
                Type: server_name (0)
                Length: 12
                Server Name Indication extension
                    Server Name list length: 10
                    Server Name Type: host_name (0)
                    Server Name length: 7
                    Server Name: 1.1.1.1
            Extension: extended_master_secret (len=0)
                Type: extended_master_secret (23)
                Length: 0
            Extension: renegotiation_info (len=1)
                Type: renegotiation_info (65281)
                Length: 1
                Renegotiation Info extension
                    Renegotiation info extension length: 0
            Extension: supported_groups (len=14)
                Type: supported_groups (10)
                Length: 14
                Supported Groups List Length: 12
                Supported Groups (6 groups)
                    Supported Group: x25519 (0x001d)
                    Supported Group: secp256r1 (0x0017)
                    Supported Group: secp384r1 (0x0018)
                    Supported Group: secp521r1 (0x0019)
                    Supported Group: ffdhe2048 (0x0100)
                    Supported Group: ffdhe3072 (0x0101)
            Extension: ec_point_formats (len=2)
                Type: ec_point_formats (11)
                Length: 2
                EC point formats Length: 1
                Elliptic curves point formats (1)
                    EC point format: uncompressed (0)
            Extension: SessionTicket TLS (len=0)
                Type: SessionTicket TLS (35)
                Length: 0
                Data (0 bytes)
            Extension: application_layer_protocol_negotiation (len=14)
                Type: application_layer_protocol_negotiation (16)
                Length: 14
                ALPN Extension Length: 12
                ALPN Protocol
                    ALPN string length: 2
                    ALPN Next Protocol: h2
                    ALPN string length: 8
                    ALPN Next Protocol: http/1.1
            Extension: status_request (len=5)
                Type: status_request (5)
                Length: 5
                Certificate Status Type: OCSP (1)
                Responder ID list Length: 0
                Request Extensions Length: 0
            Extension: key_share (len=107)
                Type: key_share (51)
                Length: 107
                Key Share extension
                    Client Key Share Length: 105
                    Key Share Entry: Group: x25519, Key Exchange length: 32
                        Group: x25519 (29)
                        Key Exchange Length: 32
                        Key Exchange: 7dd4286f301e1cc5570f4a90f5d0a100a32fe3fdced1188a...
                    Key Share Entry: Group: secp256r1, Key Exchange length: 65
                        Group: secp256r1 (23)
                        Key Exchange Length: 65
                        Key Exchange: 04151d7a0b38c12aa32e4cef4f4a4abfdd17f9d00141a05a...
            Extension: supported_versions (len=9)
                Type: supported_versions (43)
                Length: 9
                Supported Versions length: 8
                Supported Version: TLS 1.3 (0x0304)
                Supported Version: TLS 1.2 (0x0303)
                Supported Version: TLS 1.1 (0x0302)
                Supported Version: TLS 1.0 (0x0301)
            Extension: signature_algorithms (len=24)
                Type: signature_algorithms (13)
                Length: 24
                Signature Hash Algorithms Length: 22
                Signature Hash Algorithms (11 algorithms)
                    Signature Algorithm: ecdsa_secp256r1_sha256 (0x0403)
                        Signature Hash Algorithm Hash: SHA256 (4)
                        Signature Hash Algorithm Signature: ECDSA (3)
                    Signature Algorithm: ecdsa_secp384r1_sha384 (0x0503)
                        Signature Hash Algorithm Hash: SHA384 (5)
                        Signature Hash Algorithm Signature: ECDSA (3)
                    Signature Algorithm: ecdsa_secp521r1_sha512 (0x0603)
                        Signature Hash Algorithm Hash: SHA512 (6)
                        Signature Hash Algorithm Signature: ECDSA (3)
                    Signature Algorithm: rsa_pss_rsae_sha256 (0x0804)
                        Signature Hash Algorithm Hash: Unknown (8)
                        Signature Hash Algorithm Signature: Unknown (4)
                    Signature Algorithm: rsa_pss_rsae_sha384 (0x0805)
                        Signature Hash Algorithm Hash: Unknown (8)
                        Signature Hash Algorithm Signature: Unknown (5)
                    Signature Algorithm: rsa_pss_rsae_sha512 (0x0806)
                        Signature Hash Algorithm Hash: Unknown (8)
                        Signature Hash Algorithm Signature: Unknown (6)
                    Signature Algorithm: rsa_pkcs1_sha256 (0x0401)
                        Signature Hash Algorithm Hash: SHA256 (4)
                        Signature Hash Algorithm Signature: RSA (1)
                    Signature Algorithm: rsa_pkcs1_sha384 (0x0501)
                        Signature Hash Algorithm Hash: SHA384 (5)
                        Signature Hash Algorithm Signature: RSA (1)
                    Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
                        Signature Hash Algorithm Hash: SHA512 (6)
                        Signature Hash Algorithm Signature: RSA (1)
                    Signature Algorithm: ecdsa_sha1 (0x0203)
                        Signature Hash Algorithm Hash: SHA1 (2)
                        Signature Hash Algorithm Signature: ECDSA (3)
                    Signature Algorithm: rsa_pkcs1_sha1 (0x0201)
                        Signature Hash Algorithm Hash: SHA1 (2)
                        Signature Hash Algorithm Signature: RSA (1)
            Extension: psk_key_exchange_modes (len=2)
                Type: psk_key_exchange_modes (45)
                Length: 2
                PSK Key Exchange Modes Length: 1
                PSK Key Exchange Mode: PSK with (EC)DHE key establishment (psk_dhe_ke) (1)
            Extension: Unknown type 28 (len=2)
                Type: Unknown (28)
                Length: 2
                Data: 4001
            Extension: padding (len=151)
                Type: padding (21)
                Length: 151
                Padding Data: 000000000000000000000000000000000000000000000000...
uTLS fingerprint with RemoveSNIExtension workaround (note absence of server_name)
Secure Sockets Layer
    TLSv1 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 512
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Length: 508
            Version: TLS 1.2 (0x0303)
            Random: c5ea2e01d674d7e7e280bbf68dc8faca6308bd5552a7fede...
                GMT Unix Time: Mar 21, 2075 23:39:13.000000000 MDT
                Random Bytes: d674d7e7e280bbf68dc8faca6308bd5552a7feded6ad774d...
            Session ID Length: 32
            Session ID: 74f6ba412c1e3c606bd4a361624485bc93880c2d5994884f...
            Cipher Suites Length: 36
            Cipher Suites (18 suites)
                Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
                Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303)
                Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9)
                Cipher Suite: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
                Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x0033)
                Cipher Suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x0039)
                Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
                Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
                Cipher Suite: TLS_RSA_WITH_3DES_EDE_CBC_SHA (0x000a)
            Compression Methods Length: 1
            Compression Methods (1 method)
                Compression Method: null (0)
            Extensions Length: 399
            Extension: extended_master_secret (len=0)
                Type: extended_master_secret (23)
                Length: 0
            Extension: renegotiation_info (len=1)
                Type: renegotiation_info (65281)
                Length: 1
                Renegotiation Info extension
                    Renegotiation info extension length: 0
            Extension: supported_groups (len=14)
                Type: supported_groups (10)
                Length: 14
                Supported Groups List Length: 12
                Supported Groups (6 groups)
                    Supported Group: x25519 (0x001d)
                    Supported Group: secp256r1 (0x0017)
                    Supported Group: secp384r1 (0x0018)
                    Supported Group: secp521r1 (0x0019)
                    Supported Group: ffdhe2048 (0x0100)
                    Supported Group: ffdhe3072 (0x0101)
            Extension: ec_point_formats (len=2)
                Type: ec_point_formats (11)
                Length: 2
                EC point formats Length: 1
                Elliptic curves point formats (1)
                    EC point format: uncompressed (0)
            Extension: SessionTicket TLS (len=0)
                Type: SessionTicket TLS (35)
                Length: 0
                Data (0 bytes)
            Extension: application_layer_protocol_negotiation (len=14)
                Type: application_layer_protocol_negotiation (16)
                Length: 14
                ALPN Extension Length: 12
                ALPN Protocol
                    ALPN string length: 2
                    ALPN Next Protocol: h2
                    ALPN string length: 8
                    ALPN Next Protocol: http/1.1
            Extension: status_request (len=5)
                Type: status_request (5)
                Length: 5
                Certificate Status Type: OCSP (1)
                Responder ID list Length: 0
                Request Extensions Length: 0
            Extension: key_share (len=107)
                Type: key_share (51)
                Length: 107
                Key Share extension
                    Client Key Share Length: 105
                    Key Share Entry: Group: x25519, Key Exchange length: 32
                        Group: x25519 (29)
                        Key Exchange Length: 32
                        Key Exchange: de3a466a3bdbbd2d2368fedd56c6d9315cd4006dc88a4da0...
                    Key Share Entry: Group: secp256r1, Key Exchange length: 65
                        Group: secp256r1 (23)
                        Key Exchange Length: 65
                        Key Exchange: 047ec6d114b9d95ba1fbd2241740dcaa05033e186d8d6af0...
            Extension: supported_versions (len=9)
                Type: supported_versions (43)
                Length: 9
                Supported Versions length: 8
                Supported Version: TLS 1.3 (0x0304)
                Supported Version: TLS 1.2 (0x0303)
                Supported Version: TLS 1.1 (0x0302)
                Supported Version: TLS 1.0 (0x0301)
            Extension: signature_algorithms (len=24)
                Type: signature_algorithms (13)
                Length: 24
                Signature Hash Algorithms Length: 22
                Signature Hash Algorithms (11 algorithms)
                    Signature Algorithm: ecdsa_secp256r1_sha256 (0x0403)
                        Signature Hash Algorithm Hash: SHA256 (4)
                        Signature Hash Algorithm Signature: ECDSA (3)
                    Signature Algorithm: ecdsa_secp384r1_sha384 (0x0503)
                        Signature Hash Algorithm Hash: SHA384 (5)
                        Signature Hash Algorithm Signature: ECDSA (3)
                    Signature Algorithm: ecdsa_secp521r1_sha512 (0x0603)
                        Signature Hash Algorithm Hash: SHA512 (6)
                        Signature Hash Algorithm Signature: ECDSA (3)
                    Signature Algorithm: rsa_pss_rsae_sha256 (0x0804)
                        Signature Hash Algorithm Hash: Unknown (8)
                        Signature Hash Algorithm Signature: Unknown (4)
                    Signature Algorithm: rsa_pss_rsae_sha384 (0x0805)
                        Signature Hash Algorithm Hash: Unknown (8)
                        Signature Hash Algorithm Signature: Unknown (5)
                    Signature Algorithm: rsa_pss_rsae_sha512 (0x0806)
                        Signature Hash Algorithm Hash: Unknown (8)
                        Signature Hash Algorithm Signature: Unknown (6)
                    Signature Algorithm: rsa_pkcs1_sha256 (0x0401)
                        Signature Hash Algorithm Hash: SHA256 (4)
                        Signature Hash Algorithm Signature: RSA (1)
                    Signature Algorithm: rsa_pkcs1_sha384 (0x0501)
                        Signature Hash Algorithm Hash: SHA384 (5)
                        Signature Hash Algorithm Signature: RSA (1)
                    Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
                        Signature Hash Algorithm Hash: SHA512 (6)
                        Signature Hash Algorithm Signature: RSA (1)
                    Signature Algorithm: ecdsa_sha1 (0x0203)
                        Signature Hash Algorithm Hash: SHA1 (2)
                        Signature Hash Algorithm Signature: ECDSA (3)
                    Signature Algorithm: rsa_pkcs1_sha1 (0x0201)
                        Signature Hash Algorithm Hash: SHA1 (2)
                        Signature Hash Algorithm Signature: RSA (1)
            Extension: psk_key_exchange_modes (len=2)
                Type: psk_key_exchange_modes (45)
                Length: 2
                PSK Key Exchange Modes Length: 1
                PSK Key Exchange Mode: PSK with (EC)DHE key establishment (psk_dhe_ke) (1)
            Extension: Unknown type 28 (len=2)
                Type: Unknown (28)
                Length: 2
                Data: 4001
            Extension: padding (len=167)
                Type: padding (21)
                Length: 167
                Padding Data: 000000000000000000000000000000000000000000000000...

Open source the TLS api?

Hello I am quite intrigued with the concept of TLS fingerprinting I want to implement it into a webserver of mine as a layer on top of nodejs however I have minimal experience in go and the language is unlike anything I have ever seen, I did as much digging as I could and saw that https://client.tlsfingerprint.io:8443/ is what is requested to essentially meet an endpoint and gain all the information that is sent back as a response, based of the parameters of the request I see that its some sort of amalgamation of the class ClientHelloMsg from crypto/tls and backend access to your own TLS fingerprinting analytics you have collected, I don't want the analytical part just 100% of the information from the client hello in the same format you have primarily the cipher suites and extensions, if you don't mind I feel like it would be very beneficial if something like this was open sourced but its up to you

Not actually properly testing omitSNI in u_conn_test

🤦 I feel like an idiot about this one. It looks like I left a hardcoded false as the argument here instead of correctly passing through omitSNI. I'm honestly a little confused that the compiler allowed it since it's an unused variable.

I'll open a quick PR to fix this. Sorry!!!

test.runUTLS(t, *update, helloID, false)

Using a Proxy to Connect

Hello, is it possible to connect using HTTP / SOCKS5 Proxy?
My connection is currently done using default HttpGetConsistentRandomized

Use Custom TLS Timestamp

Hi, first off hats off for such an awesome tool!

Is there any possibility to set a custom Timestamp inside the TLS Handshake instead of the random generated one?

Fallback cipher list

Chrome 88 will send a fallback cipher list that includes TLS_RSA_WITH_3DES_EDE_CBC_SHA on top of it's regular list if a server replies with a server rejection to the client hello. (see esaj.tjsp.jus.br for an example site).

Is there anything built-in at the moment to do a fallback cipher list?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.