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