u_fingerprinter.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. // Copyright 2017 Google Inc. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package tls
  5. import (
  6. "errors"
  7. "fmt"
  8. "strings"
  9. "golang.org/x/crypto/cryptobyte"
  10. )
  11. // Fingerprinter is a struct largely for holding options for the FingerprintClientHello func
  12. type Fingerprinter struct {
  13. // KeepPSK will ensure that the PreSharedKey extension is passed along into the resulting ClientHelloSpec as-is
  14. KeepPSK bool
  15. // AllowBluntMimicry will ensure that unknown extensions are
  16. // passed along into the resulting ClientHelloSpec as-is
  17. // It will not ensure that the PSK is passed along, if you require that, use KeepPSK
  18. // WARNING: there could be numerous subtle issues with ClientHelloSpecs
  19. // that are generated with this flag which could compromise security and/or mimicry
  20. AllowBluntMimicry bool
  21. // AlwaysAddPadding will always add a UtlsPaddingExtension with BoringPaddingStyle
  22. // at the end of the extensions list if it isn't found in the fingerprinted hello.
  23. // This could be useful in scenarios where the hello you are fingerprinting does not
  24. // have any padding, but you suspect that other changes you make to the final hello
  25. // (including things like different SNI lengths) would cause padding to be necessary
  26. AlwaysAddPadding bool
  27. }
  28. // FingerprintClientHello returns a ClientHelloSpec which is based on the
  29. // ClientHello that is passed in as the data argument
  30. //
  31. // If the ClientHello passed in has extensions that are not recognized or cannot be handled
  32. // it will return a non-nil error and a nil *ClientHelloSpec value
  33. //
  34. // The data should be the full tls record, including the record type/version/length header
  35. // as well as the handshake type/length/version header
  36. // https://tools.ietf.org/html/rfc5246#section-6.2
  37. // https://tools.ietf.org/html/rfc5246#section-7.4
  38. func (f *Fingerprinter) FingerprintClientHello(data []byte) (*ClientHelloSpec, error) {
  39. clientHelloSpec := &ClientHelloSpec{}
  40. s := cryptobyte.String(data)
  41. var contentType uint8
  42. var recordVersion uint16
  43. if !s.ReadUint8(&contentType) || // record type
  44. !s.ReadUint16(&recordVersion) || !s.Skip(2) { // record version and length
  45. return nil, errors.New("unable to read record type, version, and length")
  46. }
  47. if recordType(contentType) != recordTypeHandshake {
  48. return nil, errors.New("record is not a handshake")
  49. }
  50. var handshakeVersion uint16
  51. var handshakeType uint8
  52. if !s.ReadUint8(&handshakeType) || !s.Skip(3) || // message type and 3 byte length
  53. !s.ReadUint16(&handshakeVersion) || !s.Skip(32) { // 32 byte random
  54. return nil, errors.New("unable to read handshake message type, length, and random")
  55. }
  56. if handshakeType != typeClientHello {
  57. return nil, errors.New("handshake message is not a ClientHello")
  58. }
  59. clientHelloSpec.TLSVersMin = recordVersion
  60. clientHelloSpec.TLSVersMax = handshakeVersion
  61. var ignoredSessionID cryptobyte.String
  62. if !s.ReadUint8LengthPrefixed(&ignoredSessionID) {
  63. return nil, errors.New("unable to read session id")
  64. }
  65. var cipherSuitesBytes cryptobyte.String
  66. if !s.ReadUint16LengthPrefixed(&cipherSuitesBytes) {
  67. return nil, errors.New("unable to read ciphersuites")
  68. }
  69. cipherSuites := []uint16{}
  70. for !cipherSuitesBytes.Empty() {
  71. var suite uint16
  72. if !cipherSuitesBytes.ReadUint16(&suite) {
  73. return nil, errors.New("unable to read ciphersuite")
  74. }
  75. cipherSuites = append(cipherSuites, unGREASEUint16(suite))
  76. }
  77. clientHelloSpec.CipherSuites = cipherSuites
  78. if !readUint8LengthPrefixed(&s, &clientHelloSpec.CompressionMethods) {
  79. return nil, errors.New("unable to read compression methods")
  80. }
  81. if s.Empty() {
  82. // ClientHello is optionally followed by extension data
  83. return clientHelloSpec, nil
  84. }
  85. var extensions cryptobyte.String
  86. if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() {
  87. return nil, errors.New("unable to read extensions data")
  88. }
  89. for !extensions.Empty() {
  90. var extension uint16
  91. var extData cryptobyte.String
  92. if !extensions.ReadUint16(&extension) ||
  93. !extensions.ReadUint16LengthPrefixed(&extData) {
  94. return nil, errors.New("unable to read extension data")
  95. }
  96. switch extension {
  97. case extensionServerName:
  98. // RFC 6066, Section 3
  99. var nameList cryptobyte.String
  100. if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() {
  101. return nil, errors.New("unable to read server name extension data")
  102. }
  103. var serverName string
  104. for !nameList.Empty() {
  105. var nameType uint8
  106. var serverNameBytes cryptobyte.String
  107. if !nameList.ReadUint8(&nameType) ||
  108. !nameList.ReadUint16LengthPrefixed(&serverNameBytes) ||
  109. serverNameBytes.Empty() {
  110. return nil, errors.New("unable to read server name extension data")
  111. }
  112. if nameType != 0 {
  113. continue
  114. }
  115. if len(serverName) != 0 {
  116. return nil, errors.New("multiple names of the same name_type in server name extension are prohibited")
  117. }
  118. serverName = string(serverNameBytes)
  119. if strings.HasSuffix(serverName, ".") {
  120. return nil, errors.New("SNI value may not include a trailing dot")
  121. }
  122. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SNIExtension{})
  123. }
  124. case extensionNextProtoNeg:
  125. // draft-agl-tls-nextprotoneg-04
  126. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &NPNExtension{})
  127. case extensionStatusRequest:
  128. // RFC 4366, Section 3.6
  129. var statusType uint8
  130. var ignored cryptobyte.String
  131. if !extData.ReadUint8(&statusType) ||
  132. !extData.ReadUint16LengthPrefixed(&ignored) ||
  133. !extData.ReadUint16LengthPrefixed(&ignored) {
  134. return nil, errors.New("unable to read status request extension data")
  135. }
  136. if statusType == statusTypeOCSP {
  137. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &StatusRequestExtension{})
  138. } else {
  139. return nil, errors.New("status request extension statusType is not statusTypeOCSP")
  140. }
  141. case extensionSupportedCurves:
  142. // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7
  143. var curvesBytes cryptobyte.String
  144. if !extData.ReadUint16LengthPrefixed(&curvesBytes) || curvesBytes.Empty() {
  145. return nil, errors.New("unable to read supported curves extension data")
  146. }
  147. curves := []CurveID{}
  148. for !curvesBytes.Empty() {
  149. var curve uint16
  150. if !curvesBytes.ReadUint16(&curve) {
  151. return nil, errors.New("unable to read supported curves extension data")
  152. }
  153. curves = append(curves, CurveID(unGREASEUint16(curve)))
  154. }
  155. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SupportedCurvesExtension{curves})
  156. case extensionSupportedPoints:
  157. // RFC 4492, Section 5.1.2
  158. supportedPoints := []uint8{}
  159. if !readUint8LengthPrefixed(&extData, &supportedPoints) ||
  160. len(supportedPoints) == 0 {
  161. return nil, errors.New("unable to read supported points extension data")
  162. }
  163. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SupportedPointsExtension{supportedPoints})
  164. case extensionSessionTicket:
  165. // RFC 5077, Section 3.2
  166. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SessionTicketExtension{})
  167. case extensionSignatureAlgorithms:
  168. // RFC 5246, Section 7.4.1.4.1
  169. var sigAndAlgs cryptobyte.String
  170. if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
  171. return nil, errors.New("unable to read signature algorithms extension data")
  172. }
  173. supportedSignatureAlgorithms := []SignatureScheme{}
  174. for !sigAndAlgs.Empty() {
  175. var sigAndAlg uint16
  176. if !sigAndAlgs.ReadUint16(&sigAndAlg) {
  177. return nil, errors.New("unable to read signature algorithms extension data")
  178. }
  179. supportedSignatureAlgorithms = append(
  180. supportedSignatureAlgorithms, SignatureScheme(sigAndAlg))
  181. }
  182. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SignatureAlgorithmsExtension{supportedSignatureAlgorithms})
  183. case extensionSignatureAlgorithmsCert:
  184. // RFC 8446, Section 4.2.3
  185. if f.AllowBluntMimicry {
  186. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &GenericExtension{extension, extData})
  187. } else {
  188. return nil, errors.New("unsupported extension SignatureAlgorithmsCert")
  189. }
  190. case extensionRenegotiationInfo:
  191. // RFC 5746, Section 3.2
  192. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &RenegotiationInfoExtension{RenegotiateOnceAsClient})
  193. case extensionALPN:
  194. // RFC 7301, Section 3.1
  195. var protoList cryptobyte.String
  196. if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
  197. return nil, errors.New("unable to read ALPN extension data")
  198. }
  199. alpnProtocols := []string{}
  200. for !protoList.Empty() {
  201. var proto cryptobyte.String
  202. if !protoList.ReadUint8LengthPrefixed(&proto) || proto.Empty() {
  203. return nil, errors.New("unable to read ALPN extension data")
  204. }
  205. alpnProtocols = append(alpnProtocols, string(proto))
  206. }
  207. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &ALPNExtension{alpnProtocols})
  208. case extensionSCT:
  209. // RFC 6962, Section 3.3.1
  210. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SCTExtension{})
  211. case extensionSupportedVersions:
  212. // RFC 8446, Section 4.2.1
  213. var versList cryptobyte.String
  214. if !extData.ReadUint8LengthPrefixed(&versList) || versList.Empty() {
  215. return nil, errors.New("unable to read supported versions extension data")
  216. }
  217. supportedVersions := []uint16{}
  218. for !versList.Empty() {
  219. var vers uint16
  220. if !versList.ReadUint16(&vers) {
  221. return nil, errors.New("unable to read supported versions extension data")
  222. }
  223. supportedVersions = append(supportedVersions, unGREASEUint16(vers))
  224. }
  225. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SupportedVersionsExtension{supportedVersions})
  226. // If SupportedVersionsExtension is present, use that instead of record+handshake versions
  227. clientHelloSpec.TLSVersMin = 0
  228. clientHelloSpec.TLSVersMax = 0
  229. case extensionKeyShare:
  230. // RFC 8446, Section 4.2.8
  231. var clientShares cryptobyte.String
  232. if !extData.ReadUint16LengthPrefixed(&clientShares) {
  233. return nil, errors.New("unable to read key share extension data")
  234. }
  235. keyShares := []KeyShare{}
  236. for !clientShares.Empty() {
  237. var ks KeyShare
  238. var group uint16
  239. if !clientShares.ReadUint16(&group) ||
  240. !readUint16LengthPrefixed(&clientShares, &ks.Data) ||
  241. len(ks.Data) == 0 {
  242. return nil, errors.New("unable to read key share extension data")
  243. }
  244. ks.Group = CurveID(unGREASEUint16(group))
  245. // if not GREASE, key share data will be discarded as it should
  246. // be generated per connection
  247. if ks.Group != GREASE_PLACEHOLDER {
  248. ks.Data = nil
  249. }
  250. keyShares = append(keyShares, ks)
  251. }
  252. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &KeyShareExtension{keyShares})
  253. case extensionPSKModes:
  254. // RFC 8446, Section 4.2.9
  255. // TODO: PSK Modes have their own form of GREASE-ing which is not currently implemented
  256. // the current functionality will NOT re-GREASE/re-randomize these values when using a fingerprinted spec
  257. // https://github.com/refraction-networking/utls/pull/58#discussion_r522354105
  258. // https://tools.ietf.org/html/draft-ietf-tls-grease-01#section-2
  259. pskModes := []uint8{}
  260. if !readUint8LengthPrefixed(&extData, &pskModes) {
  261. return nil, errors.New("unable to read PSK extension data")
  262. }
  263. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &PSKKeyExchangeModesExtension{pskModes})
  264. case utlsExtensionExtendedMasterSecret:
  265. // https://tools.ietf.org/html/rfc7627
  266. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &UtlsExtendedMasterSecretExtension{})
  267. case utlsExtensionPadding:
  268. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle})
  269. case utlsExtensionCompressCertificate:
  270. methods := []CertCompressionAlgo{}
  271. methodsRaw := new(cryptobyte.String)
  272. if !extData.ReadUint8LengthPrefixed(methodsRaw) {
  273. return nil, errors.New("unable to read cert compression algorithms extension data")
  274. }
  275. for !methodsRaw.Empty() {
  276. var method uint16
  277. if !methodsRaw.ReadUint16(&method) {
  278. return nil, errors.New("unable to read cert compression algorithms extension data")
  279. }
  280. methods = append(methods, CertCompressionAlgo(method))
  281. }
  282. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &UtlsCompressCertExtension{methods})
  283. case fakeExtensionChannelID, fakeRecordSizeLimit:
  284. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &GenericExtension{extension, extData})
  285. case extensionPreSharedKey:
  286. // RFC 8446, Section 4.2.11
  287. if f.KeepPSK {
  288. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &GenericExtension{extension, extData})
  289. } else {
  290. return nil, errors.New("unsupported extension PreSharedKey")
  291. }
  292. case extensionCookie:
  293. // RFC 8446, Section 4.2.2
  294. if f.AllowBluntMimicry {
  295. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &GenericExtension{extension, extData})
  296. } else {
  297. return nil, errors.New("unsupported extension Cookie")
  298. }
  299. case extensionEarlyData:
  300. // RFC 8446, Section 4.2.10
  301. if f.AllowBluntMimicry {
  302. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &GenericExtension{extension, extData})
  303. } else {
  304. return nil, errors.New("unsupported extension EarlyData")
  305. }
  306. default:
  307. if isGREASEUint16(extension) {
  308. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &UtlsGREASEExtension{unGREASEUint16(extension), extData})
  309. } else if f.AllowBluntMimicry {
  310. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &GenericExtension{extension, extData})
  311. } else {
  312. return nil, fmt.Errorf("unsupported extension %#x", extension)
  313. }
  314. continue
  315. }
  316. }
  317. if f.AlwaysAddPadding {
  318. alreadyHasPadding := false
  319. for _, ext := range clientHelloSpec.Extensions {
  320. if _, ok := ext.(*UtlsPaddingExtension); ok {
  321. alreadyHasPadding = true
  322. break
  323. }
  324. }
  325. if !alreadyHasPadding {
  326. clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle})
  327. }
  328. }
  329. return clientHelloSpec, nil
  330. }