Update fingerprints + add default spec version (#25)

Update fingerprints + add default spec version

 * Adds fingerprints for Chrome 75, iOS 12.1, and Firefox 65(=Firefox 63)
 * If min/max tls versions are not explicitly specified in the ClientHelloSpec,
   uTLS will try to parse versions from SupportedVersions extension,
   and fallback to [TLS 1.0, TLS 1.2] if SupportedVersions is absent.
 * Adds mimicked FakeRecordSizeLimitExtension and FakeCertCompressionAlgsExtension
   to be used instead of GenericExtension{} for clarity and extensibility
   (we are ready to use those with Firefox and Chrome fps with correct values
   whenever actual functionality is implemented)
* SetTLSVers: parse the right extensions + cosmetics
This commit is contained in:
sergeyfrolov 2019-03-27 10:53:10 -06:00 committed by GitHub
parent 05163f8a36
commit b7c656eec2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 310 additions and 46 deletions

View file

@ -65,9 +65,11 @@ This is not a problem, if you fully control the server and turn unsupported thin
| ------------- | -------- | ---------- | ---------------------- | --------------------------------------------- |
| Chrome 62 | no | no | ChannelID | [0a4a74aeebd1bb66](https://tlsfingerprint.io/id/0a4a74aeebd1bb66) |
| Chrome 70 | no | no | ChannelID, Encrypted Certs | [bc4c7e42f4961cd7](https://tlsfingerprint.io/id/bc4c7e42f4961cd7) |
| Chrome 72 | no | no | ChannelID, Encrypted Certs | [bbf04e5f1881f506](https://tlsfingerprint.io/id/bbf04e5f1881f506) |
| Firefox 56 | very low | no | None | [c884bad7f40bee56](https://tlsfingerprint.io/id/c884bad7f40bee56) |
| Firefox 63 | very low | no | MaxRecordSize | [6bfedc5d5c740d58](https://tlsfingerprint.io/id/6bfedc5d5c740d58) |
| Firefox 65 | very low | no | MaxRecordSize | [6bfedc5d5c740d58](https://tlsfingerprint.io/id/6bfedc5d5c740d58) |
| iOS 11.1 | low** | no | None | [71a81bafd58e1301](https://tlsfingerprint.io/id/71a81bafd58e1301) |
| iOS 12.1 | low** | no | None | [ec55e5b4136c7949](https://tlsfingerprint.io/id/ec55e5b4136c7949) |
\* Denotes very rough guesstimate of likelihood that unsupported things will get echoed back by the server in the wild,
*visibly breaking the connection*.

View file

@ -58,8 +58,21 @@ var (
FakeFFDHE3072 = uint16(0x0101)
)
// https://tools.ietf.org/html/draft-ietf-tls-certificate-compression-04
type CertCompressionAlgo uint16
const (
CertCompressionZlib CertCompressionAlgo = 0x0001
CertCompressionBrotli CertCompressionAlgo = 0x0002
)
const (
PskModePlain uint8 = pskModePlain
PskModeDHE uint8 = pskModeDHE
)
type ClientHelloID struct {
Client string
Client string
// Version specifies version of a mimicked clients (e.g. browsers).
// Not used in randomized, custom handshake, and default Go.
@ -99,8 +112,8 @@ type ClientHelloSpec struct {
CompressionMethods []uint8 // nil => no compression
Extensions []TLSExtension // nil => no extensions
TLSVersMin uint16 // [1.0-1.3]
TLSVersMax uint16 // [1.2-1.3]
TLSVersMin uint16 // [1.0-1.3] default: parse from .Extensions, if SupportedVersions ext is not present => 1.0
TLSVersMax uint16 // [1.2-1.3] default: parse from .Extensions, if SupportedVersions ext is not present => 1.2
// GreaseStyle: currently only random
// sessionID may or may not depend on ticket; nil => random
@ -126,18 +139,21 @@ var (
HelloRandomizedNoALPN = ClientHelloID{helloRandomizedNoALPN, helloAutoVers, nil}
// The rest will will parrot given browser.
HelloFirefox_Auto = HelloFirefox_63
HelloFirefox_Auto = HelloFirefox_65
HelloFirefox_55 = ClientHelloID{helloFirefox, "55", nil}
HelloFirefox_56 = ClientHelloID{helloFirefox, "56", nil}
HelloFirefox_63 = ClientHelloID{helloFirefox, "63", nil}
HelloFirefox_65 = ClientHelloID{helloFirefox, "65", nil}
HelloChrome_Auto = HelloChrome_70
HelloChrome_Auto = HelloChrome_72
HelloChrome_58 = ClientHelloID{helloChrome, "58", nil}
HelloChrome_62 = ClientHelloID{helloChrome, "62", nil}
HelloChrome_70 = ClientHelloID{helloChrome, "70", nil}
HelloChrome_72 = ClientHelloID{helloChrome, "72", nil}
HelloIOS_Auto = HelloIOS_11_1
HelloIOS_11_1 = ClientHelloID{helloIOS, "111", nil}
HelloIOS_Auto = HelloIOS_12_1
HelloIOS_11_1 = ClientHelloID{helloIOS, "111", nil} // legacy "111" means 11.1
HelloIOS_12_1 = ClientHelloID{helloIOS, "12.1", nil}
)
// based on spec's GreaseStyle, GREASE_PLACEHOLDER may be replaced by another GREASE value

View file

@ -480,8 +480,58 @@ func (uconn *UConn) GetOutKeystream(length int) ([]byte, error) {
return nil, errors.New("Could not convert OutCipher to cipher.AEAD")
}
// SetVersCreateState set min and max TLS version in all appropriate places.
func (uconn *UConn) SetTLSVers(minTLSVers, maxTLSVers uint16) error {
// SetTLSVers sets min and max TLS version in all appropriate places.
// Function will use first non-zero version parsed in following order:
// 1) Provided minTLSVers, maxTLSVers
// 2) specExtensions may have SupportedVersionsExtension
// 3) [default] min = TLS 1.0, max = TLS 1.2
//
// Error is only returned if things are in clearly undesirable state
// to help user fix them.
func (uconn *UConn) SetTLSVers(minTLSVers, maxTLSVers uint16, specExtensions []TLSExtension) error {
if minTLSVers == 0 && maxTLSVers == 0 {
// if version is not set explicitly in the ClientHelloSpec, check the SupportedVersions extension
supportedVersionsExtensionsPresent := 0
for _, e := range specExtensions {
switch ext := e.(type) {
case *SupportedVersionsExtension:
findVersionsInSupportedVersionsExtensions := func(versions []uint16) (uint16, uint16) {
// returns (minVers, maxVers)
minVers := uint16(0)
maxVers := uint16(0)
for _, vers := range versions {
if vers == GREASE_PLACEHOLDER {
continue
}
if maxVers < vers || maxVers == 0 {
maxVers = vers
}
if minVers > vers || minVers == 0 {
minVers = vers
}
}
return minVers, maxVers
}
supportedVersionsExtensionsPresent += 1
minTLSVers, maxTLSVers = findVersionsInSupportedVersionsExtensions(ext.Versions)
if minTLSVers == 0 && maxTLSVers == 0 {
return fmt.Errorf("SupportedVersions extension has invalid Versions field")
} // else: proceed
}
}
switch supportedVersionsExtensionsPresent {
case 0:
// if mandatory for TLS 1.3 extension is not present, just default to 1.2
minTLSVers = VersionTLS10
maxTLSVers = VersionTLS12
case 1:
default:
return fmt.Errorf("uconn.Extensions contains %v separate SupportedVersions extensions",
supportedVersionsExtensionsPresent)
}
}
if minTLSVers < VersionTLS10 || minTLSVers > VersionTLS12 {
return fmt.Errorf("uTLS does not support 0x%X as min version", minTLSVers)
}

View file

@ -133,7 +133,81 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) {
CurveP256,
CurveP384,
}},
&GenericExtension{id: fakeCertCompressionAlgs, data: []byte{02, 00, 02}},
&FakeCertCompressionAlgsExtension{[]CertCompressionAlgo{CertCompressionBrotli}},
&UtlsGREASEExtension{},
&UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle},
},
}, nil
case HelloChrome_72:
return ClientHelloSpec{
CipherSuites: []uint16{
GREASE_PLACEHOLDER,
TLS_AES_128_GCM_SHA256,
TLS_AES_256_GCM_SHA384,
TLS_CHACHA20_POLY1305_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
TLS_RSA_WITH_AES_128_GCM_SHA256,
TLS_RSA_WITH_AES_256_GCM_SHA384,
TLS_RSA_WITH_AES_128_CBC_SHA,
TLS_RSA_WITH_AES_256_CBC_SHA,
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
},
CompressionMethods: []byte{
0x00, // compressionNone
},
Extensions: []TLSExtension{
&UtlsGREASEExtension{},
&SNIExtension{},
&UtlsExtendedMasterSecretExtension{},
&RenegotiationInfoExtension{renegotiation: RenegotiateOnceAsClient},
&SupportedCurvesExtension{[]CurveID{
CurveID(GREASE_PLACEHOLDER),
X25519,
CurveP256,
CurveP384,
}},
&SupportedPointsExtension{SupportedPoints: []byte{
0x00, // pointFormatUncompressed
}},
&SessionTicketExtension{},
&ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}},
&StatusRequestExtension{},
&SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{
ECDSAWithP256AndSHA256,
PSSWithSHA256,
PKCS1WithSHA256,
ECDSAWithP384AndSHA384,
PSSWithSHA384,
PKCS1WithSHA384,
PSSWithSHA512,
PKCS1WithSHA512,
PKCS1WithSHA1,
}},
&SCTExtension{},
&KeyShareExtension{[]KeyShare{
{Group: CurveID(GREASE_PLACEHOLDER), Data: []byte{0}},
{Group: X25519},
}},
&PSKKeyExchangeModesExtension{[]uint8{
PskModeDHE,
}},
&SupportedVersionsExtension{[]uint16{
GREASE_PLACEHOLDER,
VersionTLS13,
VersionTLS12,
VersionTLS11,
VersionTLS10,
}},
&FakeCertCompressionAlgsExtension{[]CertCompressionAlgo{
CertCompressionBrotli,
}},
&UtlsGREASEExtension{},
&UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle},
},
@ -186,7 +260,7 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) {
},
GetSessionID: nil,
}, nil
case HelloFirefox_63:
case HelloFirefox_63, HelloFirefox_65:
return ClientHelloSpec{
TLSVersMin: VersionTLS10,
TLSVersMax: VersionTLS13,
@ -254,7 +328,7 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) {
PKCS1WithSHA1,
}},
&PSKKeyExchangeModesExtension{[]uint8{pskModeDHE}},
&GenericExtension{id: fakeRecordSizeLimit, data: []byte{0x40, 0x01}},
&FakeRecordSizeLimitExtension{0x4001},
&UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle},
}}, nil
case HelloIOS_11_1:
@ -316,6 +390,68 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) {
}},
},
}, nil
case HelloIOS_12_1:
return ClientHelloSpec{
CipherSuites: []uint16{
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
DISABLED_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
DISABLED_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
TLS_RSA_WITH_AES_256_GCM_SHA384,
TLS_RSA_WITH_AES_128_GCM_SHA256,
DISABLED_TLS_RSA_WITH_AES_256_CBC_SHA256,
TLS_RSA_WITH_AES_128_CBC_SHA256,
TLS_RSA_WITH_AES_256_CBC_SHA,
TLS_RSA_WITH_AES_128_CBC_SHA,
0xc008,
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
},
CompressionMethods: []byte{
compressionNone,
},
Extensions: []TLSExtension{
&RenegotiationInfoExtension{renegotiation: RenegotiateOnceAsClient},
&SNIExtension{},
&UtlsExtendedMasterSecretExtension{},
&SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{
ECDSAWithP256AndSHA256,
PSSWithSHA256,
PKCS1WithSHA256,
ECDSAWithP384AndSHA384,
ECDSAWithSHA1,
PSSWithSHA384,
PSSWithSHA384,
PKCS1WithSHA384,
PSSWithSHA512,
PKCS1WithSHA512,
PKCS1WithSHA1,
}},
&StatusRequestExtension{},
&NPNExtension{},
&SCTExtension{},
&ALPNExtension{AlpnProtocols: []string{"h2", "h2-16", "h2-15", "h2-14", "spdy/3.1", "spdy/3", "http/1.1"}},
&SupportedPointsExtension{SupportedPoints: []byte{
pointFormatUncompressed,
}},
&SupportedCurvesExtension{[]CurveID{
X25519,
CurveP256,
CurveP384,
CurveP521,
}},
},
}, nil
default:
return ClientHelloSpec{}, errors.New("ClientHello ID " + id.Str() + " is unknown")
}
@ -349,7 +485,8 @@ func (uconn *UConn) applyPresetByID(id ClientHelloID) (err error) {
// same ClientHelloSpec. It is advised to use different specs and avoid any shared state.
func (uconn *UConn) ApplyPreset(p *ClientHelloSpec) error {
var err error
err = uconn.SetTLSVers(p.TLSVersMin, p.TLSVersMax)
err = uconn.SetTLSVers(p.TLSVersMin, p.TLSVersMax, p.Extensions)
if err != nil {
return err
}
@ -640,10 +777,6 @@ func (uconn *UConn) generateRandomizedSpec() (ClientHelloSpec, error) {
r.rand.Shuffle(len(p.Extensions), func(i, j int) {
p.Extensions[i], p.Extensions[j] = p.Extensions[j], p.Extensions[i]
})
err = uconn.SetTLSVers(p.TLSVersMin, p.TLSVersMax)
if err != nil {
return p, err
}
return p, nil
}

View file

@ -392,32 +392,6 @@ func (e *GenericExtension) Read(b []byte) (int, error) {
return e.Len(), io.EOF
}
/*
FAKE EXTENSIONS
*/
type FakeChannelIDExtension struct {
}
func (e *FakeChannelIDExtension) writeToUConn(uc *UConn) error {
return nil
}
func (e *FakeChannelIDExtension) Len() int {
return 4
}
func (e *FakeChannelIDExtension) Read(b []byte) (int, error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
// https://tools.ietf.org/html/draft-balfanz-tls-channelid-00
b[0] = byte(fakeExtensionChannelID >> 8)
b[1] = byte(fakeExtensionChannelID & 0xff)
// The length is 0
return e.Len(), io.EOF
}
type UtlsExtendedMasterSecretExtension struct {
}
@ -712,5 +686,94 @@ func (e *CookieExtension) Read(b []byte) (int, error) {
return e.Len(), io.EOF
}
// TODO: FakeCertificateCompressionAlgorithmsExtension
// TODO: FakeRecordSizeLimitExtension
/*
FAKE EXTENSIONS
*/
type FakeChannelIDExtension struct {
}
func (e *FakeChannelIDExtension) writeToUConn(uc *UConn) error {
return nil
}
func (e *FakeChannelIDExtension) Len() int {
return 4
}
func (e *FakeChannelIDExtension) Read(b []byte) (int, error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
// https://tools.ietf.org/html/draft-balfanz-tls-channelid-00
b[0] = byte(fakeExtensionChannelID >> 8)
b[1] = byte(fakeExtensionChannelID & 0xff)
// The length is 0
return e.Len(), io.EOF
}
type FakeCertCompressionAlgsExtension struct {
Methods []CertCompressionAlgo
}
func (e *FakeCertCompressionAlgsExtension) writeToUConn(uc *UConn) error {
return nil
}
func (e *FakeCertCompressionAlgsExtension) Len() int {
return 4 + 1 + (2 * len(e.Methods))
}
func (e *FakeCertCompressionAlgsExtension) Read(b []byte) (int, error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
// https://tools.ietf.org/html/draft-balfanz-tls-channelid-00
b[0] = byte(fakeCertCompressionAlgs >> 8)
b[1] = byte(fakeCertCompressionAlgs & 0xff)
extLen := 2 * len(e.Methods)
if extLen > 255 {
return 0, errors.New("too many certificate compression methods")
}
b[2] = byte((extLen + 1) >> 8)
b[3] = byte((extLen + 1) & 0xff)
b[4] = byte(extLen)
i := 5
for _, compMethod := range e.Methods {
b[i] = byte(compMethod >> 8)
b[i+1] = byte(compMethod)
i += 2
}
return e.Len(), io.EOF
}
type FakeRecordSizeLimitExtension struct {
Limit uint16
}
func (e *FakeRecordSizeLimitExtension) writeToUConn(uc *UConn) error {
return nil
}
func (e *FakeRecordSizeLimitExtension) Len() int {
return 6
}
func (e *FakeRecordSizeLimitExtension) Read(b []byte) (int, error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
// https://tools.ietf.org/html/draft-balfanz-tls-channelid-00
b[0] = byte(fakeRecordSizeLimit >> 8)
b[1] = byte(fakeRecordSizeLimit & 0xff)
b[2] = byte(0)
b[3] = byte(2)
b[4] = byte(e.Limit >> 8)
b[5] = byte(e.Limit & 0xff)
return e.Len(), io.EOF
}