Permalink
Cannot retrieve contributors at this time
868 lines (823 sloc)
26 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Copyright 2017 Google Inc. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package tls | |
| import ( | |
| "crypto/sha256" | |
| "encoding/binary" | |
| "errors" | |
| "fmt" | |
| "io" | |
| "sort" | |
| "strconv" | |
| ) | |
| func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { | |
| switch id { | |
| case HelloChrome_58, HelloChrome_62: | |
| return ClientHelloSpec{ | |
| TLSVersMax: VersionTLS12, | |
| TLSVersMin: VersionTLS10, | |
| CipherSuites: []uint16{ | |
| GREASE_PLACEHOLDER, | |
| 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{compressionNone}, | |
| Extensions: []TLSExtension{ | |
| &UtlsGREASEExtension{}, | |
| &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, | |
| &SNIExtension{}, | |
| &UtlsExtendedMasterSecretExtension{}, | |
| &SessionTicketExtension{}, | |
| &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ | |
| ECDSAWithP256AndSHA256, | |
| PSSWithSHA256, | |
| PKCS1WithSHA256, | |
| ECDSAWithP384AndSHA384, | |
| PSSWithSHA384, | |
| PKCS1WithSHA384, | |
| PSSWithSHA512, | |
| PKCS1WithSHA512, | |
| PKCS1WithSHA1}, | |
| }, | |
| &StatusRequestExtension{}, | |
| &SCTExtension{}, | |
| &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, | |
| &FakeChannelIDExtension{}, | |
| &SupportedPointsExtension{SupportedPoints: []byte{pointFormatUncompressed}}, | |
| &SupportedCurvesExtension{[]CurveID{CurveID(GREASE_PLACEHOLDER), | |
| X25519, CurveP256, CurveP384}}, | |
| &UtlsGREASEExtension{}, | |
| &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, | |
| }, | |
| GetSessionID: sha256.Sum256, | |
| }, nil | |
| case HelloChrome_70: | |
| return ClientHelloSpec{ | |
| TLSVersMin: VersionTLS10, | |
| TLSVersMax: VersionTLS13, | |
| 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{ | |
| compressionNone, | |
| }, | |
| Extensions: []TLSExtension{ | |
| &UtlsGREASEExtension{}, | |
| &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, | |
| &SNIExtension{}, | |
| &UtlsExtendedMasterSecretExtension{}, | |
| &SessionTicketExtension{}, | |
| &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ | |
| ECDSAWithP256AndSHA256, | |
| PSSWithSHA256, | |
| PKCS1WithSHA256, | |
| ECDSAWithP384AndSHA384, | |
| PSSWithSHA384, | |
| PKCS1WithSHA384, | |
| PSSWithSHA512, | |
| PKCS1WithSHA512, | |
| PKCS1WithSHA1, | |
| }}, | |
| &StatusRequestExtension{}, | |
| &SCTExtension{}, | |
| &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, | |
| &FakeChannelIDExtension{}, | |
| &SupportedPointsExtension{SupportedPoints: []byte{ | |
| pointFormatUncompressed, | |
| }}, | |
| &KeyShareExtension{[]KeyShare{ | |
| {Group: CurveID(GREASE_PLACEHOLDER), Data: []byte{0}}, | |
| {Group: X25519}, | |
| }}, | |
| &PSKKeyExchangeModesExtension{[]uint8{pskModeDHE}}, | |
| &SupportedVersionsExtension{[]uint16{ | |
| GREASE_PLACEHOLDER, | |
| VersionTLS13, | |
| VersionTLS12, | |
| VersionTLS11, | |
| VersionTLS10}}, | |
| &SupportedCurvesExtension{[]CurveID{ | |
| CurveID(GREASE_PLACEHOLDER), | |
| X25519, | |
| CurveP256, | |
| CurveP384, | |
| }}, | |
| &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}, | |
| }, | |
| }, nil | |
| case HelloFirefox_55, HelloFirefox_56: | |
| return ClientHelloSpec{ | |
| TLSVersMax: VersionTLS12, | |
| TLSVersMin: VersionTLS10, | |
| CipherSuites: []uint16{ | |
| TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, | |
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, | |
| TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, | |
| TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, | |
| TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, | |
| TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, | |
| TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, | |
| TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, | |
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, | |
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, | |
| FAKE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, | |
| FAKE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, | |
| TLS_RSA_WITH_AES_128_CBC_SHA, | |
| TLS_RSA_WITH_AES_256_CBC_SHA, | |
| TLS_RSA_WITH_3DES_EDE_CBC_SHA, | |
| }, | |
| CompressionMethods: []byte{compressionNone}, | |
| Extensions: []TLSExtension{ | |
| &SNIExtension{}, | |
| &UtlsExtendedMasterSecretExtension{}, | |
| &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, | |
| &SupportedCurvesExtension{[]CurveID{X25519, CurveP256, CurveP384, CurveP521}}, | |
| &SupportedPointsExtension{SupportedPoints: []byte{pointFormatUncompressed}}, | |
| &SessionTicketExtension{}, | |
| &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, | |
| &StatusRequestExtension{}, | |
| &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ | |
| ECDSAWithP256AndSHA256, | |
| ECDSAWithP384AndSHA384, | |
| ECDSAWithP521AndSHA512, | |
| PSSWithSHA256, | |
| PSSWithSHA384, | |
| PSSWithSHA512, | |
| PKCS1WithSHA256, | |
| PKCS1WithSHA384, | |
| PKCS1WithSHA512, | |
| ECDSAWithSHA1, | |
| PKCS1WithSHA1}, | |
| }, | |
| &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, | |
| }, | |
| GetSessionID: nil, | |
| }, nil | |
| case HelloFirefox_63, HelloFirefox_65: | |
| return ClientHelloSpec{ | |
| TLSVersMin: VersionTLS10, | |
| TLSVersMax: VersionTLS13, | |
| CipherSuites: []uint16{ | |
| TLS_AES_128_GCM_SHA256, | |
| TLS_CHACHA20_POLY1305_SHA256, | |
| TLS_AES_256_GCM_SHA384, | |
| TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, | |
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, | |
| TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, | |
| TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, | |
| TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, | |
| TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, | |
| TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, | |
| TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, | |
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, | |
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, | |
| FAKE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, | |
| FAKE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, | |
| TLS_RSA_WITH_AES_128_CBC_SHA, | |
| TLS_RSA_WITH_AES_256_CBC_SHA, | |
| TLS_RSA_WITH_3DES_EDE_CBC_SHA, | |
| }, | |
| CompressionMethods: []byte{ | |
| compressionNone, | |
| }, | |
| Extensions: []TLSExtension{ | |
| &SNIExtension{}, | |
| &UtlsExtendedMasterSecretExtension{}, | |
| &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, | |
| &SupportedCurvesExtension{[]CurveID{ | |
| X25519, | |
| CurveP256, | |
| CurveP384, | |
| CurveP521, | |
| CurveID(FakeFFDHE2048), | |
| CurveID(FakeFFDHE3072), | |
| }}, | |
| &SupportedPointsExtension{SupportedPoints: []byte{ | |
| pointFormatUncompressed, | |
| }}, | |
| &SessionTicketExtension{}, | |
| &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, | |
| &StatusRequestExtension{}, | |
| &KeyShareExtension{[]KeyShare{ | |
| {Group: X25519}, | |
| {Group: CurveP256}, | |
| }}, | |
| &SupportedVersionsExtension{[]uint16{ | |
| VersionTLS13, | |
| VersionTLS12, | |
| VersionTLS11, | |
| VersionTLS10}}, | |
| &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ | |
| ECDSAWithP256AndSHA256, | |
| ECDSAWithP384AndSHA384, | |
| ECDSAWithP521AndSHA512, | |
| PSSWithSHA256, | |
| PSSWithSHA384, | |
| PSSWithSHA512, | |
| PKCS1WithSHA256, | |
| PKCS1WithSHA384, | |
| PKCS1WithSHA512, | |
| ECDSAWithSHA1, | |
| PKCS1WithSHA1, | |
| }}, | |
| &PSKKeyExchangeModesExtension{[]uint8{pskModeDHE}}, | |
| &FakeRecordSizeLimitExtension{0x4001}, | |
| &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, | |
| }}, nil | |
| case HelloIOS_11_1: | |
| return ClientHelloSpec{ | |
| TLSVersMax: VersionTLS12, | |
| TLSVersMin: VersionTLS10, | |
| 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, | |
| }, | |
| CompressionMethods: []byte{ | |
| compressionNone, | |
| }, | |
| Extensions: []TLSExtension{ | |
| &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, | |
| &SNIExtension{}, | |
| &UtlsExtendedMasterSecretExtension{}, | |
| &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ | |
| ECDSAWithP256AndSHA256, | |
| PSSWithSHA256, | |
| PKCS1WithSHA256, | |
| ECDSAWithP384AndSHA384, | |
| 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{Curves: []CurveID{ | |
| X25519, | |
| CurveP256, | |
| CurveP384, | |
| CurveP521, | |
| }}, | |
| }, | |
| }, 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") | |
| } | |
| } | |
| func (uconn *UConn) applyPresetByID(id ClientHelloID) (err error) { | |
| var spec ClientHelloSpec | |
| uconn.ClientHelloID = id | |
| // choose/generate the spec | |
| switch id.Client { | |
| case helloRandomized, helloRandomizedNoALPN, helloRandomizedALPN: | |
| spec, err = uconn.generateRandomizedSpec() | |
| if err != nil { | |
| return err | |
| } | |
| case helloCustom: | |
| return nil | |
| default: | |
| spec, err = utlsIdToSpec(id) | |
| if err != nil { | |
| return err | |
| } | |
| } | |
| return uconn.ApplyPreset(&spec) | |
| } | |
| // ApplyPreset should only be used in conjunction with HelloCustom to apply custom specs. | |
| // Fields of TLSExtensions that are slices/pointers are shared across different connections with | |
| // 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, p.Extensions) | |
| if err != nil { | |
| return err | |
| } | |
| privateHello, ecdheParams, err := uconn.makeClientHello() | |
| if err != nil { | |
| return err | |
| } | |
| uconn.HandshakeState.Hello = privateHello.getPublicPtr() | |
| uconn.HandshakeState.State13.EcdheParams = ecdheParams | |
| hello := uconn.HandshakeState.Hello | |
| session := uconn.HandshakeState.Session | |
| switch len(hello.Random) { | |
| case 0: | |
| hello.Random = make([]byte, 32) | |
| _, err := io.ReadFull(uconn.config.rand(), hello.Random) | |
| if err != nil { | |
| return errors.New("tls: short read from Rand: " + err.Error()) | |
| } | |
| case 32: | |
| // carry on | |
| default: | |
| return errors.New("ClientHello expected length: 32 bytes. Got: " + | |
| strconv.Itoa(len(hello.Random)) + " bytes") | |
| } | |
| if len(hello.CipherSuites) == 0 { | |
| hello.CipherSuites = defaultCipherSuites() | |
| } | |
| if len(hello.CompressionMethods) == 0 { | |
| hello.CompressionMethods = []uint8{compressionNone} | |
| } | |
| // Currently, GREASE is assumed to come from BoringSSL | |
| grease_bytes := make([]byte, 2*ssl_grease_last_index) | |
| grease_extensions_seen := 0 | |
| _, err = io.ReadFull(uconn.config.rand(), grease_bytes) | |
| if err != nil { | |
| return errors.New("tls: short read from Rand: " + err.Error()) | |
| } | |
| for i := range uconn.greaseSeed { | |
| uconn.greaseSeed[i] = binary.LittleEndian.Uint16(grease_bytes[2*i : 2*i+2]) | |
| } | |
| if uconn.greaseSeed[ssl_grease_extension1] == uconn.greaseSeed[ssl_grease_extension2] { | |
| uconn.greaseSeed[ssl_grease_extension2] ^= 0x1010 | |
| } | |
| hello.CipherSuites = make([]uint16, len(p.CipherSuites)) | |
| copy(hello.CipherSuites, p.CipherSuites) | |
| for i := range hello.CipherSuites { | |
| if hello.CipherSuites[i] == GREASE_PLACEHOLDER { | |
| hello.CipherSuites[i] = GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_cipher) | |
| } | |
| } | |
| uconn.GetSessionID = p.GetSessionID | |
| uconn.Extensions = make([]TLSExtension, len(p.Extensions)) | |
| copy(uconn.Extensions, p.Extensions) | |
| // reGrease, and point things to each other | |
| for _, e := range uconn.Extensions { | |
| switch ext := e.(type) { | |
| case *SNIExtension: | |
| if ext.ServerName == "" { | |
| ext.ServerName = uconn.config.ServerName | |
| } | |
| case *UtlsGREASEExtension: | |
| switch grease_extensions_seen { | |
| case 0: | |
| ext.Value = GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_extension1) | |
| case 1: | |
| ext.Value = GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_extension2) | |
| ext.Body = []byte{0} | |
| default: | |
| return errors.New("at most 2 grease extensions are supported") | |
| } | |
| grease_extensions_seen += 1 | |
| case *SessionTicketExtension: | |
| if session == nil && uconn.config.ClientSessionCache != nil { | |
| cacheKey := clientSessionCacheKey(uconn.RemoteAddr(), uconn.config) | |
| session, _ = uconn.config.ClientSessionCache.Get(cacheKey) | |
| // TODO: use uconn.loadSession(hello.getPrivateObj()) to support TLS 1.3 PSK-style resumption | |
| } | |
| err := uconn.SetSessionState(session) | |
| if err != nil { | |
| return err | |
| } | |
| case *SupportedCurvesExtension: | |
| for i := range ext.Curves { | |
| if ext.Curves[i] == GREASE_PLACEHOLDER { | |
| ext.Curves[i] = CurveID(GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_group)) | |
| } | |
| } | |
| case *KeyShareExtension: | |
| preferredCurveIsSet := false | |
| for i := range ext.KeyShares { | |
| curveID := ext.KeyShares[i].Group | |
| if curveID == GREASE_PLACEHOLDER { | |
| ext.KeyShares[i].Group = CurveID(GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_group)) | |
| continue | |
| } | |
| if len(ext.KeyShares[i].Data) > 1 { | |
| continue | |
| } | |
| ecdheParams, err := generateECDHEParameters(uconn.config.rand(), curveID) | |
| if err != nil { | |
| return fmt.Errorf("unsupported Curve in KeyShareExtension: %v."+ | |
| "To mimic it, fill the Data(key) field manually.", curveID) | |
| } | |
| ext.KeyShares[i].Data = ecdheParams.PublicKey() | |
| if !preferredCurveIsSet { | |
| // only do this once for the first non-grease curve | |
| uconn.HandshakeState.State13.EcdheParams = ecdheParams | |
| preferredCurveIsSet = true | |
| } | |
| } | |
| case *SupportedVersionsExtension: | |
| for i := range ext.Versions { | |
| if ext.Versions[i] == GREASE_PLACEHOLDER { | |
| ext.Versions[i] = GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_version) | |
| } | |
| } | |
| } | |
| } | |
| return nil | |
| } | |
| func (uconn *UConn) generateRandomizedSpec() (ClientHelloSpec, error) { | |
| p := ClientHelloSpec{} | |
| if uconn.ClientHelloID.Seed == nil { | |
| seed, err := NewPRNGSeed() | |
| if err != nil { | |
| return p, err | |
| } | |
| uconn.ClientHelloID.Seed = seed | |
| } | |
| r, err := newPRNGWithSeed(uconn.ClientHelloID.Seed) | |
| if err != nil { | |
| return p, err | |
| } | |
| id := uconn.ClientHelloID | |
| var WithALPN bool | |
| switch id.Client { | |
| case helloRandomizedALPN: | |
| WithALPN = true | |
| case helloRandomizedNoALPN: | |
| WithALPN = false | |
| case helloRandomized: | |
| if r.FlipWeightedCoin(0.7) { | |
| WithALPN = true | |
| } else { | |
| WithALPN = false | |
| } | |
| default: | |
| return p, fmt.Errorf("using non-randomized ClientHelloID %v to generate randomized spec", id.Client) | |
| } | |
| p.CipherSuites = make([]uint16, len(defaultCipherSuites())) | |
| copy(p.CipherSuites, defaultCipherSuites()) | |
| shuffledSuites, err := shuffledCiphers(r) | |
| if err != nil { | |
| return p, err | |
| } | |
| if r.FlipWeightedCoin(0.4) { | |
| p.TLSVersMin = VersionTLS10 | |
| p.TLSVersMax = VersionTLS13 | |
| tls13ciphers := make([]uint16, len(defaultCipherSuitesTLS13())) | |
| copy(tls13ciphers, defaultCipherSuitesTLS13()) | |
| r.rand.Shuffle(len(tls13ciphers), func(i, j int) { | |
| tls13ciphers[i], tls13ciphers[j] = tls13ciphers[j], tls13ciphers[i] | |
| }) | |
| // appending TLS 1.3 ciphers before TLS 1.2, since that's what popular implementations do | |
| shuffledSuites = append(tls13ciphers, shuffledSuites...) | |
| // TLS 1.3 forbids RC4 in any configurations | |
| shuffledSuites = removeRC4Ciphers(shuffledSuites) | |
| } else { | |
| p.TLSVersMin = VersionTLS10 | |
| p.TLSVersMax = VersionTLS12 | |
| } | |
| p.CipherSuites = removeRandomCiphers(r, shuffledSuites, 0.4) | |
| sni := SNIExtension{uconn.config.ServerName} | |
| sessionTicket := SessionTicketExtension{Session: uconn.HandshakeState.Session} | |
| sigAndHashAlgos := []SignatureScheme{ | |
| ECDSAWithP256AndSHA256, | |
| PKCS1WithSHA256, | |
| ECDSAWithP384AndSHA384, | |
| PKCS1WithSHA384, | |
| PKCS1WithSHA1, | |
| PKCS1WithSHA512, | |
| } | |
| if r.FlipWeightedCoin(0.63) { | |
| sigAndHashAlgos = append(sigAndHashAlgos, ECDSAWithSHA1) | |
| } | |
| if r.FlipWeightedCoin(0.59) { | |
| sigAndHashAlgos = append(sigAndHashAlgos, ECDSAWithP521AndSHA512) | |
| } | |
| if r.FlipWeightedCoin(0.51) || p.TLSVersMax == VersionTLS13 { | |
| // https://tools.ietf.org/html/rfc8446 says "...RSASSA-PSS (which is mandatory in TLS 1.3)..." | |
| sigAndHashAlgos = append(sigAndHashAlgos, PSSWithSHA256) | |
| if r.FlipWeightedCoin(0.9) { | |
| // these usually go together | |
| sigAndHashAlgos = append(sigAndHashAlgos, PSSWithSHA384) | |
| sigAndHashAlgos = append(sigAndHashAlgos, PSSWithSHA512) | |
| } | |
| } | |
| r.rand.Shuffle(len(sigAndHashAlgos), func(i, j int) { | |
| sigAndHashAlgos[i], sigAndHashAlgos[j] = sigAndHashAlgos[j], sigAndHashAlgos[i] | |
| }) | |
| sigAndHash := SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: sigAndHashAlgos} | |
| status := StatusRequestExtension{} | |
| sct := SCTExtension{} | |
| ems := UtlsExtendedMasterSecretExtension{} | |
| points := SupportedPointsExtension{SupportedPoints: []byte{pointFormatUncompressed}} | |
| curveIDs := []CurveID{} | |
| if r.FlipWeightedCoin(0.71) || p.TLSVersMax == VersionTLS13 { | |
| curveIDs = append(curveIDs, X25519) | |
| } | |
| curveIDs = append(curveIDs, CurveP256, CurveP384) | |
| if r.FlipWeightedCoin(0.46) { | |
| curveIDs = append(curveIDs, CurveP521) | |
| } | |
| curves := SupportedCurvesExtension{curveIDs} | |
| padding := UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle} | |
| reneg := RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient} | |
| p.Extensions = []TLSExtension{ | |
| &sni, | |
| &sessionTicket, | |
| &sigAndHash, | |
| &points, | |
| &curves, | |
| } | |
| if WithALPN { | |
| if len(uconn.config.NextProtos) == 0 { | |
| // if user didn't specify alpn yet, choose something popular | |
| uconn.config.NextProtos = []string{"h2", "http/1.1"} | |
| } | |
| alpn := ALPNExtension{AlpnProtocols: uconn.config.NextProtos} | |
| p.Extensions = append(p.Extensions, &alpn) | |
| } | |
| if r.FlipWeightedCoin(0.62) || p.TLSVersMax == VersionTLS13 { | |
| // always include for TLS 1.3, since TLS 1.3 ClientHellos are often over 256 bytes | |
| // and that's when padding is required to work around buggy middleboxes | |
| p.Extensions = append(p.Extensions, &padding) | |
| } | |
| if r.FlipWeightedCoin(0.74) { | |
| p.Extensions = append(p.Extensions, &status) | |
| } | |
| if r.FlipWeightedCoin(0.46) { | |
| p.Extensions = append(p.Extensions, &sct) | |
| } | |
| if r.FlipWeightedCoin(0.75) { | |
| p.Extensions = append(p.Extensions, &reneg) | |
| } | |
| if r.FlipWeightedCoin(0.77) { | |
| p.Extensions = append(p.Extensions, &ems) | |
| } | |
| if p.TLSVersMax == VersionTLS13 { | |
| ks := KeyShareExtension{[]KeyShare{ | |
| {Group: X25519}, // the key for the group will be generated later | |
| }} | |
| if r.FlipWeightedCoin(0.25) { | |
| // do not ADD second keyShare because crypto/tls does not support multiple ecdheParams | |
| // TODO: add it back when they implement multiple keyShares, or implement it oursevles | |
| // ks.KeyShares = append(ks.KeyShares, KeyShare{Group: CurveP256}) | |
| ks.KeyShares[0].Group = CurveP256 | |
| } | |
| pskExchangeModes := PSKKeyExchangeModesExtension{[]uint8{pskModeDHE}} | |
| supportedVersionsExt := SupportedVersionsExtension{ | |
| Versions: makeSupportedVersions(p.TLSVersMin, p.TLSVersMax), | |
| } | |
| p.Extensions = append(p.Extensions, &ks, &pskExchangeModes, &supportedVersionsExt) | |
| } | |
| r.rand.Shuffle(len(p.Extensions), func(i, j int) { | |
| p.Extensions[i], p.Extensions[j] = p.Extensions[j], p.Extensions[i] | |
| }) | |
| return p, nil | |
| } | |
| func removeRandomCiphers(r *prng, s []uint16, maxRemovalProbability float64) []uint16 { | |
| // removes elements in place | |
| // probability to remove increases for further elements | |
| // never remove first cipher | |
| if len(s) <= 1 { | |
| return s | |
| } | |
| // remove random elements | |
| floatLen := float64(len(s)) | |
| sliceLen := len(s) | |
| for i := 1; i < sliceLen; i++ { | |
| if r.FlipWeightedCoin(maxRemovalProbability * float64(i) / floatLen) { | |
| s = append(s[:i], s[i+1:]...) | |
| sliceLen-- | |
| i-- | |
| } | |
| } | |
| return s[:sliceLen] | |
| } | |
| func shuffledCiphers(r *prng) ([]uint16, error) { | |
| ciphers := make(sortableCiphers, len(cipherSuites)) | |
| perm := r.Perm(len(cipherSuites)) | |
| for i, suite := range cipherSuites { | |
| ciphers[i] = sortableCipher{suite: suite.id, | |
| isObsolete: ((suite.flags & suiteTLS12) == 0), | |
| randomTag: perm[i]} | |
| } | |
| sort.Sort(ciphers) | |
| return ciphers.GetCiphers(), nil | |
| } | |
| type sortableCipher struct { | |
| isObsolete bool | |
| randomTag int | |
| suite uint16 | |
| } | |
| type sortableCiphers []sortableCipher | |
| func (ciphers sortableCiphers) Len() int { | |
| return len(ciphers) | |
| } | |
| func (ciphers sortableCiphers) Less(i, j int) bool { | |
| if ciphers[i].isObsolete && !ciphers[j].isObsolete { | |
| return false | |
| } | |
| if ciphers[j].isObsolete && !ciphers[i].isObsolete { | |
| return true | |
| } | |
| return ciphers[i].randomTag < ciphers[j].randomTag | |
| } | |
| func (ciphers sortableCiphers) Swap(i, j int) { | |
| ciphers[i], ciphers[j] = ciphers[j], ciphers[i] | |
| } | |
| func (ciphers sortableCiphers) GetCiphers() []uint16 { | |
| cipherIDs := make([]uint16, len(ciphers)) | |
| for i := range ciphers { | |
| cipherIDs[i] = ciphers[i].suite | |
| } | |
| return cipherIDs | |
| } | |
| func removeRC4Ciphers(s []uint16) []uint16 { | |
| // removes elements in place | |
| sliceLen := len(s) | |
| for i := 0; i < sliceLen; i++ { | |
| cipher := s[i] | |
| if cipher == TLS_ECDHE_ECDSA_WITH_RC4_128_SHA || | |
| cipher == TLS_ECDHE_RSA_WITH_RC4_128_SHA || | |
| cipher == TLS_RSA_WITH_RC4_128_SHA { | |
| s = append(s[:i], s[i+1:]...) | |
| sliceLen-- | |
| i-- | |
| } | |
| } | |
| return s[:sliceLen] | |
| } |