u_ech.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. package tls
  2. import (
  3. "crypto/rand"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "math/big"
  8. "sync"
  9. "github.com/Psiphon-Labs/utls/dicttls"
  10. "github.com/cloudflare/circl/hpke"
  11. "golang.org/x/crypto/cryptobyte"
  12. )
  13. // Unstable API: This is a work in progress and may change in the future. Using
  14. // it in your application may cause your application to break when updating to
  15. // a new version of uTLS.
  16. const (
  17. OuterClientHello byte = 0x00
  18. InnerClientHello byte = 0x01
  19. )
  20. type EncryptedClientHelloExtension interface {
  21. // TLSExtension must be implemented by all EncryptedClientHelloExtension implementations.
  22. TLSExtension
  23. // Configure configures the EncryptedClientHelloExtension with the given slice of ECHConfig.
  24. Configure([]ECHConfig) error
  25. // MarshalClientHello is called by (*UConn).MarshalClientHello() when an ECH extension
  26. // is present to allow the ECH extension to take control of the generation of the
  27. // entire ClientHello message.
  28. MarshalClientHello(*UConn) error
  29. mustEmbedUnimplementedECHExtension()
  30. }
  31. type ECHExtension = EncryptedClientHelloExtension // alias
  32. // type guard: GREASEEncryptedClientHelloExtension must implement EncryptedClientHelloExtension
  33. var (
  34. _ EncryptedClientHelloExtension = (*GREASEEncryptedClientHelloExtension)(nil)
  35. _ EncryptedClientHelloExtension = (*UnimplementedECHExtension)(nil)
  36. )
  37. type GREASEEncryptedClientHelloExtension struct {
  38. CandidateCipherSuites []HPKESymmetricCipherSuite
  39. cipherSuite HPKESymmetricCipherSuite // randomly picked from CandidateCipherSuites or generated if empty
  40. CandidateConfigIds []uint8
  41. configId uint8 // randomly picked from CandidateConfigIds or generated if empty
  42. EncapsulatedKey []byte // if empty, will generate random bytes
  43. CandidatePayloadLens []uint16 // Pre-encryption. If 0, will pick 128(+16=144)
  44. payload []byte // payload should be calculated ONCE and stored here, HRR will reuse this
  45. initOnce sync.Once
  46. UnimplementedECHExtension
  47. }
  48. type GREASEECHExtension = GREASEEncryptedClientHelloExtension // alias
  49. // init initializes the GREASEEncryptedClientHelloExtension with random values if they are not set.
  50. //
  51. // Based on cloudflare/go's echGenerateGreaseExt()
  52. func (g *GREASEEncryptedClientHelloExtension) init() error {
  53. var initErr error
  54. g.initOnce.Do(func() {
  55. // Set the config_id field to a random byte.
  56. //
  57. // Note: must not reuse this extension unless for HRR. It is required
  58. // to generate new random bytes for config_id for each new ClientHello,
  59. // but reuse the same config_id for HRR.
  60. if len(g.CandidateConfigIds) == 0 {
  61. var b []byte = make([]byte, 1)
  62. _, err := rand.Read(b[:])
  63. if err != nil {
  64. initErr = fmt.Errorf("error generating random byte for config_id: %w", err)
  65. return
  66. }
  67. g.configId = b[0]
  68. } else {
  69. // randomly pick one from the list
  70. rndIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(g.CandidateConfigIds))))
  71. if err != nil {
  72. initErr = fmt.Errorf("error generating random index for config_id: %w", err)
  73. return
  74. }
  75. g.configId = g.CandidateConfigIds[rndIndex.Int64()]
  76. }
  77. // Set the cipher_suite field to a supported HpkeSymmetricCipherSuite.
  78. // The selection SHOULD vary to exercise all supported configurations,
  79. // but MAY be held constant for successive connections to the same server
  80. // in the same session.
  81. if len(g.CandidateCipherSuites) == 0 {
  82. _, kdf, aead := defaultHPKESuite.Params()
  83. g.cipherSuite = HPKESymmetricCipherSuite{uint16(kdf), uint16(aead)}
  84. } else {
  85. // randomly pick one from the list
  86. rndIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(g.CandidateCipherSuites))))
  87. if err != nil {
  88. initErr = fmt.Errorf("error generating random index for cipher_suite: %w", err)
  89. return
  90. }
  91. g.cipherSuite = HPKESymmetricCipherSuite{
  92. g.CandidateCipherSuites[rndIndex.Int64()].KdfId,
  93. g.CandidateCipherSuites[rndIndex.Int64()].AeadId,
  94. }
  95. // aead = hpke.AEAD(g.cipherSuite.AeadId)
  96. }
  97. if len(g.EncapsulatedKey) == 0 {
  98. // use default random key from cloudflare/go
  99. kem := hpke.KEM_X25519_HKDF_SHA256
  100. pk, err := kem.Scheme().UnmarshalBinaryPublicKey(dummyX25519PublicKey)
  101. if err != nil {
  102. initErr = fmt.Errorf("tls: grease ech: failed to parse dummy public key: %w", err)
  103. return
  104. }
  105. sender, err := defaultHPKESuite.NewSender(pk, nil)
  106. if err != nil {
  107. initErr = fmt.Errorf("tls: grease ech: failed to create sender: %w", err)
  108. return
  109. }
  110. g.EncapsulatedKey, _, err = sender.Setup(rand.Reader)
  111. if err != nil {
  112. initErr = fmt.Errorf("tls: grease ech: failed to setup encapsulated key: %w", err)
  113. return
  114. }
  115. }
  116. if len(g.payload) == 0 {
  117. if len(g.CandidatePayloadLens) == 0 {
  118. g.CandidatePayloadLens = []uint16{128}
  119. }
  120. // randomly pick one from the list
  121. rndIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(g.CandidatePayloadLens))))
  122. if err != nil {
  123. initErr = fmt.Errorf("error generating random index for payload length: %w", err)
  124. return
  125. }
  126. initErr = g.randomizePayload(g.CandidatePayloadLens[rndIndex.Int64()])
  127. }
  128. })
  129. return initErr
  130. }
  131. func (g *GREASEEncryptedClientHelloExtension) randomizePayload(encodedHelloInnerLen uint16) error {
  132. if len(g.payload) != 0 {
  133. return errors.New("tls: grease ech: regenerating payload is forbidden")
  134. }
  135. aead := hpke.AEAD(g.cipherSuite.AeadId)
  136. g.payload = make([]byte, int(aead.CipherLen(uint(encodedHelloInnerLen))))
  137. _, err := rand.Read(g.payload)
  138. if err != nil {
  139. return fmt.Errorf("tls: generating grease ech payload: %w", err)
  140. }
  141. return nil
  142. }
  143. // writeToUConn implements TLSExtension.
  144. //
  145. // For ECH extensions, writeToUConn simply points the ech field in UConn to the extension.
  146. func (g *GREASEEncryptedClientHelloExtension) writeToUConn(uconn *UConn) error {
  147. uconn.ech = g
  148. return uconn.MarshalClientHelloNoECH()
  149. }
  150. // Len implements TLSExtension.
  151. func (g *GREASEEncryptedClientHelloExtension) Len() int {
  152. g.init()
  153. return 2 + 2 + 1 /* ClientHello Type */ + 4 /* CipherSuite */ + 1 /* Config ID */ + 2 + len(g.EncapsulatedKey) + 2 + len(g.payload)
  154. }
  155. // Read implements TLSExtension.
  156. func (g *GREASEEncryptedClientHelloExtension) Read(b []byte) (int, error) {
  157. if len(b) < g.Len() {
  158. return 0, io.ErrShortBuffer
  159. }
  160. b[0] = byte(utlsExtensionECH >> 8)
  161. b[1] = byte(utlsExtensionECH & 0xFF)
  162. b[2] = byte((g.Len() - 4) >> 8)
  163. b[3] = byte((g.Len() - 4) & 0xFF)
  164. b[4] = OuterClientHello
  165. b[5] = byte(g.cipherSuite.KdfId >> 8)
  166. b[6] = byte(g.cipherSuite.KdfId & 0xFF)
  167. b[7] = byte(g.cipherSuite.AeadId >> 8)
  168. b[8] = byte(g.cipherSuite.AeadId & 0xFF)
  169. b[9] = g.configId
  170. b[10] = byte(len(g.EncapsulatedKey) >> 8)
  171. b[11] = byte(len(g.EncapsulatedKey) & 0xFF)
  172. copy(b[12:], g.EncapsulatedKey)
  173. b[12+len(g.EncapsulatedKey)] = byte(len(g.payload) >> 8)
  174. b[12+len(g.EncapsulatedKey)+1] = byte(len(g.payload) & 0xFF)
  175. copy(b[12+len(g.EncapsulatedKey)+2:], g.payload)
  176. return g.Len(), io.EOF
  177. }
  178. // Configure implements EncryptedClientHelloExtension.
  179. func (*GREASEEncryptedClientHelloExtension) Configure([]ECHConfig) error {
  180. return nil // no-op, it is not possible to configure a GREASE extension for now
  181. }
  182. // MarshalClientHello implements EncryptedClientHelloExtension.
  183. func (*GREASEEncryptedClientHelloExtension) MarshalClientHello(*UConn) error {
  184. return errors.New("tls: grease ech: MarshalClientHello() is not implemented, use (*UConn).MarshalClientHello() instead")
  185. }
  186. // Write implements TLSExtensionWriter.
  187. func (g *GREASEEncryptedClientHelloExtension) Write(b []byte) (int, error) {
  188. fullLen := len(b)
  189. extData := cryptobyte.String(b)
  190. // Check the extension type, it must be OuterClientHello otherwise we are not
  191. // parsing the correct extension
  192. var chType uint8 // 0: outer, 1: inner
  193. var ignored cryptobyte.String
  194. if !extData.ReadUint8(&chType) || chType != 0 {
  195. return fullLen, errors.New("bad Client Hello type, expected 0, got " + fmt.Sprintf("%d", chType))
  196. }
  197. // Parse the cipher suite
  198. if !extData.ReadUint16(&g.cipherSuite.KdfId) || !extData.ReadUint16(&g.cipherSuite.AeadId) {
  199. return fullLen, errors.New("bad cipher suite")
  200. }
  201. if g.cipherSuite.KdfId != dicttls.HKDF_SHA256 &&
  202. g.cipherSuite.KdfId != dicttls.HKDF_SHA384 &&
  203. g.cipherSuite.KdfId != dicttls.HKDF_SHA512 {
  204. return fullLen, errors.New("bad KDF ID: " + fmt.Sprintf("%d", g.cipherSuite.KdfId))
  205. }
  206. if g.cipherSuite.AeadId != dicttls.AEAD_AES_128_GCM &&
  207. g.cipherSuite.AeadId != dicttls.AEAD_AES_256_GCM &&
  208. g.cipherSuite.AeadId != dicttls.AEAD_CHACHA20_POLY1305 {
  209. return fullLen, errors.New("bad AEAD ID: " + fmt.Sprintf("%d", g.cipherSuite.AeadId))
  210. }
  211. g.CandidateCipherSuites = []HPKESymmetricCipherSuite{g.cipherSuite}
  212. // GREASE the ConfigId
  213. if !extData.ReadUint8(&g.configId) {
  214. return fullLen, errors.New("bad config ID")
  215. }
  216. // we don't write to CandidateConfigIds because we don't really want to reuse the same config_id
  217. // GREASE the EncapsulatedKey
  218. if !extData.ReadUint16LengthPrefixed(&ignored) {
  219. return fullLen, errors.New("bad encapsulated key")
  220. }
  221. g.EncapsulatedKey = make([]byte, len(ignored))
  222. n, err := rand.Read(g.EncapsulatedKey)
  223. if err != nil {
  224. return fullLen, fmt.Errorf("tls: generating grease ech encapsulated key: %w", err)
  225. }
  226. if n != len(g.EncapsulatedKey) {
  227. return fullLen, fmt.Errorf("tls: generating grease ech encapsulated key: short read for %d bytes", len(ignored)-n)
  228. }
  229. // GREASE the payload
  230. if !extData.ReadUint16LengthPrefixed(&ignored) {
  231. return fullLen, errors.New("bad payload")
  232. }
  233. aead := hpke.AEAD(g.cipherSuite.AeadId)
  234. g.CandidatePayloadLens = []uint16{uint16(len(ignored) - int(aead.CipherLen(0)))}
  235. return fullLen, nil
  236. }
  237. // UnimplementedECHExtension is a placeholder for an ECH extension that is not implemented.
  238. // All implementations of EncryptedClientHelloExtension should embed this struct to ensure
  239. // forward compatibility.
  240. type UnimplementedECHExtension struct{}
  241. // writeToUConn implements TLSExtension.
  242. func (*UnimplementedECHExtension) writeToUConn(_ *UConn) error {
  243. return errors.New("tls: unimplemented ECHExtension")
  244. }
  245. // Len implements TLSExtension.
  246. func (*UnimplementedECHExtension) Len() int {
  247. return 0
  248. }
  249. // Read implements TLSExtension.
  250. func (*UnimplementedECHExtension) Read(_ []byte) (int, error) {
  251. return 0, errors.New("tls: unimplemented ECHExtension")
  252. }
  253. // Configure implements EncryptedClientHelloExtension.
  254. func (*UnimplementedECHExtension) Configure([]ECHConfig) error {
  255. return errors.New("tls: unimplemented ECHExtension")
  256. }
  257. // MarshalClientHello implements EncryptedClientHelloExtension.
  258. func (*UnimplementedECHExtension) MarshalClientHello(*UConn) error {
  259. return errors.New("tls: unimplemented ECHExtension")
  260. }
  261. // mustEmbedUnimplementedECHExtension is a noop function but is required to
  262. // ensure forward compatibility.
  263. func (*UnimplementedECHExtension) mustEmbedUnimplementedECHExtension() {
  264. panic("mustEmbedUnimplementedECHExtension() is not implemented")
  265. }
  266. // BoringGREASEECH returns a GREASE scheme BoringSSL uses by default.
  267. func BoringGREASEECH() *GREASEEncryptedClientHelloExtension {
  268. return &GREASEEncryptedClientHelloExtension{
  269. CandidateCipherSuites: []HPKESymmetricCipherSuite{
  270. {
  271. KdfId: dicttls.HKDF_SHA256,
  272. AeadId: dicttls.AEAD_AES_128_GCM,
  273. },
  274. {
  275. KdfId: dicttls.HKDF_SHA256,
  276. AeadId: dicttls.AEAD_CHACHA20_POLY1305,
  277. },
  278. },
  279. CandidatePayloadLens: []uint16{128, 160, 192, 224}, // +16: 144, 176, 208, 240
  280. }
  281. }