customTLSProfiles.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /*
  2. * Copyright (c) 2019, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package protocol
  20. import (
  21. "crypto/sha256"
  22. "encoding/json"
  23. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  24. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
  25. utls "github.com/Psiphon-Labs/utls"
  26. )
  27. // CustomTLSProfile specifies custom TLS profile. This is used to deploy
  28. // custom ClientHellos as tactics data.
  29. type CustomTLSProfile struct {
  30. Name string
  31. UTLSSpec *UTLSSpec
  32. }
  33. type CustomTLSProfiles []*CustomTLSProfile
  34. // Redefined uTLS extensions.
  35. // uTLS added Unmarshaler interface to it's TLS extensions, which is not compatible
  36. // with currently deployed tactics. We redefine the types to maintain
  37. // compatibility. This may change in the future.
  38. type NPNExtensionCompat utls.NPNExtension
  39. type SNIExtensionCompat utls.SNIExtension
  40. type StatusRequestExtensionCompat utls.StatusRequestExtension
  41. type SupportedCurvesExtensionCompat utls.SupportedCurvesExtension
  42. type SupportedPointsExtensionCompat utls.SupportedPointsExtension
  43. type SignatureAlgorithmsExtensionCompat utls.SignatureAlgorithmsExtension
  44. type RenegotiationInfoExtensionCompat utls.RenegotiationInfoExtension
  45. type ALPNExtensionCompat utls.ALPNExtension
  46. type SCTExtensionCompat utls.SCTExtension
  47. type SessionTicketExtensionCompat utls.SessionTicketExtension
  48. type GenericExtensionCompat utls.GenericExtension
  49. type UtlsExtendedMasterSecretExtensionCompat utls.UtlsExtendedMasterSecretExtension
  50. type UtlsGREASEExtensionCompat utls.UtlsGREASEExtension
  51. type UtlsPaddingExtensionCompat utls.UtlsPaddingExtension
  52. type PSKKeyExchangeModesExtensionCompat utls.PSKKeyExchangeModesExtension
  53. type SupportedVersionsExtensionCompat utls.SupportedVersionsExtension
  54. type FakeChannelIDExtensionCompat utls.FakeChannelIDExtension
  55. type UtlsCompressCertExtensionCompat utls.UtlsCompressCertExtension
  56. type FakeRecordSizeLimitExtensionCompat utls.FakeRecordSizeLimitExtension
  57. type ApplicationSettingsExtensionCompat utls.ApplicationSettingsExtension
  58. type DelegatedCredentialsExtensionCompat utls.DelegatedCredentialsExtension
  59. type KeyShareExtensionCompat struct {
  60. KeyShares []struct {
  61. Group utls.CurveID
  62. Data []byte
  63. }
  64. }
  65. // Validate checks that the profiles in CustomTLSProfiles are initialized and
  66. // have no name conflicts.
  67. func (profiles CustomTLSProfiles) Validate() error {
  68. names := make(map[string]bool)
  69. for _, p := range profiles {
  70. if p.Name == "" {
  71. return errors.Tracef("custom TLS profile missing name: %s", p.Name)
  72. }
  73. if p.UTLSSpec == nil {
  74. return errors.Tracef("custom TLS profile missing utls spec: %s", p.Name)
  75. }
  76. if common.Contains(SupportedTLSProfiles, p.Name) ||
  77. common.Contains(legacyTLSProfiles, p.Name) {
  78. return errors.Tracef("invalid custom TLS profile name: %s", p.Name)
  79. }
  80. if _, ok := names[p.Name]; ok {
  81. return errors.Tracef("duplicate custom TLS profile name: %s", p.Name)
  82. }
  83. names[p.Name] = true
  84. }
  85. return nil
  86. }
  87. // GetClientHelloSpec creates a new utls.ClientHelloSpec from the ClientHello
  88. // definition in UTLSpec.
  89. //
  90. // A new utls.ClientHelloSpec, with no shared data, is created for each call,
  91. // as per:
  92. // https://github.com/refraction-networking/utls/blob/4da67951864128358459681399dd208c49d5d001/u_parrots.go#L483
  93. func (profile *CustomTLSProfile) GetClientHelloSpec() (*utls.ClientHelloSpec, error) {
  94. spec := &utls.ClientHelloSpec{}
  95. spec.TLSVersMin = profile.UTLSSpec.TLSVersMin
  96. spec.TLSVersMax = profile.UTLSSpec.TLSVersMax
  97. spec.CipherSuites = append([]uint16(nil), profile.UTLSSpec.CipherSuites...)
  98. spec.CompressionMethods = append([]uint8(nil), profile.UTLSSpec.CompressionMethods...)
  99. spec.Extensions = make([]utls.TLSExtension, len(profile.UTLSSpec.Extensions))
  100. for i, extension := range profile.UTLSSpec.Extensions {
  101. var err error
  102. spec.Extensions[i], err = extension.GetUTLSExtension()
  103. if err != nil {
  104. return nil, errors.Trace(err)
  105. }
  106. }
  107. if profile.UTLSSpec.GetSessionID == "SHA-256" {
  108. spec.GetSessionID = sha256.Sum256
  109. }
  110. return spec, nil
  111. }
  112. // UTLSSpec is a parallel data structure mirroring utls.ClientHelloSpec. Note
  113. // that utls.ClientHelloSpec cannot be directly marshaled with encoding/json
  114. // nor encoding/gob due to various type restrictions which
  115. // utls.ClientHelloSpec does not meet. Nor can we simply transmit a static,
  116. // raw ClientHello since concrete utls extension types must be instantiated in
  117. // order for related functionality to be enabled.
  118. // UTLSSpec specifies a utls.ClientHelloSpec.
  119. type UTLSSpec struct {
  120. TLSVersMin uint16
  121. TLSVersMax uint16
  122. CipherSuites []uint16
  123. CompressionMethods []uint8
  124. Extensions []*UTLSExtension
  125. GetSessionID string
  126. }
  127. // UTLSExtension specifies one of the several utls.TLSExtension concrete
  128. // implementations.
  129. type UTLSExtension struct {
  130. Name string
  131. Data json.RawMessage
  132. }
  133. // GetUTLSExtension instantiates the specified utls.TLSExtension concrete
  134. // implementation.
  135. func (e *UTLSExtension) GetUTLSExtension() (utls.TLSExtension, error) {
  136. switch e.Name {
  137. case "NPN":
  138. var compat NPNExtensionCompat
  139. err := json.Unmarshal(e.Data, &compat)
  140. if err != nil {
  141. return nil, errors.Trace(err)
  142. }
  143. extension := utls.NPNExtension(compat)
  144. return &extension, nil
  145. case "SNI":
  146. return &utls.SNIExtension{}, nil
  147. case "StatusRequest":
  148. return &utls.StatusRequestExtension{}, nil
  149. case "SupportedCurves":
  150. var compat SupportedCurvesExtensionCompat
  151. err := json.Unmarshal(e.Data, &compat)
  152. if err != nil {
  153. return nil, errors.Trace(err)
  154. }
  155. extension := utls.SupportedCurvesExtension(compat)
  156. return &extension, nil
  157. case "SupportedPoints":
  158. var compat SupportedPointsExtensionCompat
  159. err := json.Unmarshal(e.Data, &compat)
  160. if err != nil {
  161. return nil, errors.Trace(err)
  162. }
  163. extension := utls.SupportedPointsExtension(compat)
  164. return &extension, nil
  165. case "SignatureAlgorithms":
  166. var compat SignatureAlgorithmsExtensionCompat
  167. err := json.Unmarshal(e.Data, &compat)
  168. if err != nil {
  169. return nil, errors.Trace(err)
  170. }
  171. extension := utls.SignatureAlgorithmsExtension(compat)
  172. return &extension, nil
  173. case "RenegotiationInfo":
  174. var compat RenegotiationInfoExtensionCompat
  175. err := json.Unmarshal(e.Data, &compat)
  176. if err != nil {
  177. return nil, errors.Trace(err)
  178. }
  179. extension := utls.RenegotiationInfoExtension(compat)
  180. return &extension, nil
  181. case "ALPN":
  182. var compat ALPNExtensionCompat
  183. err := json.Unmarshal(e.Data, &compat)
  184. if err != nil {
  185. return nil, errors.Trace(err)
  186. }
  187. extension := utls.ALPNExtension(compat)
  188. return &extension, nil
  189. case "SCT":
  190. return &utls.SCTExtension{}, nil
  191. case "SessionTicket":
  192. return &utls.SessionTicketExtension{}, nil
  193. case "Generic":
  194. var compat GenericExtensionCompat
  195. err := json.Unmarshal(e.Data, &compat)
  196. if err != nil {
  197. return nil, errors.Trace(err)
  198. }
  199. extension := utls.GenericExtension(compat)
  200. return &extension, nil
  201. case "ExtendedMasterSecret":
  202. return &utls.UtlsExtendedMasterSecretExtension{}, nil
  203. case "GREASE":
  204. return &utls.UtlsGREASEExtension{}, nil
  205. case "BoringPadding":
  206. return &utls.UtlsPaddingExtension{GetPaddingLen: utls.BoringPaddingStyle}, nil
  207. case "KeyShare":
  208. var compat KeyShareExtensionCompat
  209. err := json.Unmarshal(e.Data, &compat)
  210. if err != nil {
  211. return nil, errors.Trace(err)
  212. }
  213. extension := utls.KeyShareExtension{
  214. KeyShares: make([]utls.KeyShare, len(compat.KeyShares)),
  215. }
  216. for i, keyShare := range compat.KeyShares {
  217. extension.KeyShares[i] = utls.KeyShare{
  218. Group: keyShare.Group,
  219. Data: keyShare.Data,
  220. }
  221. }
  222. return &extension, nil
  223. case "PSKKeyExchangeModes":
  224. var compat PSKKeyExchangeModesExtensionCompat
  225. err := json.Unmarshal(e.Data, &compat)
  226. if err != nil {
  227. return nil, errors.Trace(err)
  228. }
  229. extension := utls.PSKKeyExchangeModesExtension(compat)
  230. return &extension, nil
  231. case "SupportedVersions":
  232. var compat SupportedVersionsExtensionCompat
  233. err := json.Unmarshal(e.Data, &compat)
  234. if err != nil {
  235. return nil, errors.Trace(err)
  236. }
  237. extension := utls.SupportedVersionsExtension(compat)
  238. return &extension, nil
  239. case "ChannelID":
  240. return &utls.FakeChannelIDExtension{}, nil
  241. case "CertCompressionAlgs":
  242. var compat UtlsCompressCertExtensionCompat
  243. err := json.Unmarshal(e.Data, &compat)
  244. if err != nil {
  245. return nil, errors.Trace(err)
  246. }
  247. extension := utls.UtlsCompressCertExtension(compat)
  248. return &extension, nil
  249. case "RecordSizeLimit":
  250. var compat FakeRecordSizeLimitExtensionCompat
  251. err := json.Unmarshal(e.Data, &compat)
  252. if err != nil {
  253. return nil, errors.Trace(err)
  254. }
  255. extension := utls.FakeRecordSizeLimitExtension(compat)
  256. return &extension, nil
  257. case "ALPS":
  258. var compat ApplicationSettingsExtensionCompat
  259. err := json.Unmarshal(e.Data, &compat)
  260. if err != nil {
  261. return nil, errors.Trace(err)
  262. }
  263. extension := utls.ApplicationSettingsExtension(compat)
  264. return &extension, nil
  265. case "DelegatedCredentials":
  266. var compat DelegatedCredentialsExtensionCompat
  267. err := json.Unmarshal(e.Data, &compat)
  268. if err != nil {
  269. return nil, errors.Trace(err)
  270. }
  271. extension := utls.DelegatedCredentialsExtension(compat)
  272. return &extension, nil
  273. }
  274. return nil, errors.Tracef("unknown utls extension: %s", e.Name)
  275. }