ech.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. //go:build go1.24
  2. package tls
  3. import (
  4. "context"
  5. "crypto/tls"
  6. "encoding/base64"
  7. "encoding/pem"
  8. "net"
  9. "os"
  10. "strings"
  11. "sync"
  12. "time"
  13. "github.com/sagernet/sing-box/adapter"
  14. "github.com/sagernet/sing-box/dns"
  15. "github.com/sagernet/sing-box/option"
  16. E "github.com/sagernet/sing/common/exceptions"
  17. aTLS "github.com/sagernet/sing/common/tls"
  18. "github.com/sagernet/sing/service"
  19. mDNS "github.com/miekg/dns"
  20. "golang.org/x/crypto/cryptobyte"
  21. )
  22. func parseECHClientConfig(ctx context.Context, clientConfig ECHCapableConfig, options option.OutboundTLSOptions) (Config, error) {
  23. var echConfig []byte
  24. if len(options.ECH.Config) > 0 {
  25. echConfig = []byte(strings.Join(options.ECH.Config, "\n"))
  26. } else if options.ECH.ConfigPath != "" {
  27. content, err := os.ReadFile(options.ECH.ConfigPath)
  28. if err != nil {
  29. return nil, E.Cause(err, "read ECH config")
  30. }
  31. echConfig = content
  32. }
  33. //nolint:staticcheck
  34. if options.ECH.PQSignatureSchemesEnabled || options.ECH.DynamicRecordSizingDisabled {
  35. return nil, E.New("legacy ECH options are deprecated in sing-box 1.12.0 and removed in sing-box 1.13.0")
  36. }
  37. if len(echConfig) > 0 {
  38. block, rest := pem.Decode(echConfig)
  39. if block == nil || block.Type != "ECH CONFIGS" || len(rest) > 0 {
  40. return nil, E.New("invalid ECH configs pem")
  41. }
  42. clientConfig.SetECHConfigList(block.Bytes)
  43. return clientConfig, nil
  44. } else {
  45. return &ECHClientConfig{
  46. ECHCapableConfig: clientConfig,
  47. dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
  48. queryServerName: options.ECH.QueryServerName,
  49. }, nil
  50. }
  51. }
  52. func parseECHServerConfig(ctx context.Context, options option.InboundTLSOptions, tlsConfig *tls.Config, echKeyPath *string) error {
  53. var echKey []byte
  54. if len(options.ECH.Key) > 0 {
  55. echKey = []byte(strings.Join(options.ECH.Key, "\n"))
  56. } else if options.ECH.KeyPath != "" {
  57. content, err := os.ReadFile(options.ECH.KeyPath)
  58. if err != nil {
  59. return E.Cause(err, "read ECH keys")
  60. }
  61. echKey = content
  62. *echKeyPath = options.ECH.KeyPath
  63. } else {
  64. return E.New("missing ECH keys")
  65. }
  66. echKeys, err := parseECHKeys(echKey)
  67. if err != nil {
  68. return E.Cause(err, "parse ECH keys")
  69. }
  70. tlsConfig.EncryptedClientHelloKeys = echKeys
  71. //nolint:staticcheck
  72. if options.ECH.PQSignatureSchemesEnabled || options.ECH.DynamicRecordSizingDisabled {
  73. return E.New("legacy ECH options are deprecated in sing-box 1.12.0 and removed in sing-box 1.13.0")
  74. }
  75. return nil
  76. }
  77. func (c *STDServerConfig) setECHServerConfig(echKey []byte) error {
  78. echKeys, err := parseECHKeys(echKey)
  79. if err != nil {
  80. return err
  81. }
  82. c.access.Lock()
  83. config := c.config.Clone()
  84. config.EncryptedClientHelloKeys = echKeys
  85. c.config = config
  86. c.access.Unlock()
  87. return nil
  88. }
  89. func parseECHKeys(echKey []byte) ([]tls.EncryptedClientHelloKey, error) {
  90. block, _ := pem.Decode(echKey)
  91. if block == nil || block.Type != "ECH KEYS" {
  92. return nil, E.New("invalid ECH keys pem")
  93. }
  94. echKeys, err := UnmarshalECHKeys(block.Bytes)
  95. if err != nil {
  96. return nil, E.Cause(err, "parse ECH keys")
  97. }
  98. return echKeys, nil
  99. }
  100. type ECHClientConfig struct {
  101. ECHCapableConfig
  102. access sync.Mutex
  103. dnsRouter adapter.DNSRouter
  104. queryServerName string
  105. lastTTL time.Duration
  106. lastUpdate time.Time
  107. }
  108. func (s *ECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
  109. tlsConn, err := s.fetchAndHandshake(ctx, conn)
  110. if err != nil {
  111. return nil, err
  112. }
  113. err = tlsConn.HandshakeContext(ctx)
  114. if err != nil {
  115. return nil, err
  116. }
  117. return tlsConn, nil
  118. }
  119. func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
  120. s.access.Lock()
  121. defer s.access.Unlock()
  122. if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Since(s.lastUpdate) > s.lastTTL {
  123. queryServerName := s.queryServerName
  124. if queryServerName == "" {
  125. queryServerName = s.ServerName()
  126. }
  127. message := &mDNS.Msg{
  128. MsgHdr: mDNS.MsgHdr{
  129. RecursionDesired: true,
  130. },
  131. Question: []mDNS.Question{
  132. {
  133. Name: mDNS.Fqdn(queryServerName),
  134. Qtype: mDNS.TypeHTTPS,
  135. Qclass: mDNS.ClassINET,
  136. },
  137. },
  138. }
  139. response, err := s.dnsRouter.Exchange(ctx, message, adapter.DNSQueryOptions{})
  140. if err != nil {
  141. return nil, E.Cause(err, "fetch ECH config list")
  142. }
  143. if response.Rcode != mDNS.RcodeSuccess {
  144. return nil, E.Cause(dns.RcodeError(response.Rcode), "fetch ECH config list")
  145. }
  146. match:
  147. for _, rr := range response.Answer {
  148. switch resource := rr.(type) {
  149. case *mDNS.HTTPS:
  150. for _, value := range resource.Value {
  151. if value.Key().String() == "ech" {
  152. echConfigList, err := base64.StdEncoding.DecodeString(value.String())
  153. if err != nil {
  154. return nil, E.Cause(err, "decode ECH config")
  155. }
  156. s.lastTTL = time.Duration(rr.Header().Ttl) * time.Second
  157. s.lastUpdate = time.Now()
  158. s.SetECHConfigList(echConfigList)
  159. break match
  160. }
  161. }
  162. }
  163. }
  164. if len(s.ECHConfigList()) == 0 {
  165. return nil, E.New("no ECH config found in DNS records")
  166. }
  167. }
  168. return s.Client(conn)
  169. }
  170. func (s *ECHClientConfig) Clone() Config {
  171. return &ECHClientConfig{
  172. ECHCapableConfig: s.ECHCapableConfig.Clone().(ECHCapableConfig),
  173. dnsRouter: s.dnsRouter,
  174. queryServerName: s.queryServerName,
  175. lastUpdate: s.lastUpdate,
  176. }
  177. }
  178. func UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) {
  179. var keys []tls.EncryptedClientHelloKey
  180. rawString := cryptobyte.String(raw)
  181. for !rawString.Empty() {
  182. var key tls.EncryptedClientHelloKey
  183. if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.PrivateKey)) {
  184. return nil, E.New("error parsing private key")
  185. }
  186. if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.Config)) {
  187. return nil, E.New("error parsing config")
  188. }
  189. keys = append(keys, key)
  190. }
  191. if len(keys) == 0 {
  192. return nil, E.New("empty ECH keys")
  193. }
  194. return keys, nil
  195. }