ech.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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/experimental/deprecated"
  16. "github.com/sagernet/sing-box/option"
  17. E "github.com/sagernet/sing/common/exceptions"
  18. aTLS "github.com/sagernet/sing/common/tls"
  19. "github.com/sagernet/sing/service"
  20. mDNS "github.com/miekg/dns"
  21. "golang.org/x/crypto/cryptobyte"
  22. )
  23. func parseECHClientConfig(ctx context.Context, clientConfig ECHCapableConfig, options option.OutboundTLSOptions) (Config, error) {
  24. var echConfig []byte
  25. if len(options.ECH.Config) > 0 {
  26. echConfig = []byte(strings.Join(options.ECH.Config, "\n"))
  27. } else if options.ECH.ConfigPath != "" {
  28. content, err := os.ReadFile(options.ECH.ConfigPath)
  29. if err != nil {
  30. return nil, E.Cause(err, "read ECH config")
  31. }
  32. echConfig = content
  33. }
  34. //nolint:staticcheck
  35. if options.ECH.PQSignatureSchemesEnabled || options.ECH.DynamicRecordSizingDisabled {
  36. deprecated.Report(ctx, deprecated.OptionLegacyECHOptions)
  37. }
  38. if len(echConfig) > 0 {
  39. block, rest := pem.Decode(echConfig)
  40. if block == nil || block.Type != "ECH CONFIGS" || len(rest) > 0 {
  41. return nil, E.New("invalid ECH configs pem")
  42. }
  43. clientConfig.SetECHConfigList(block.Bytes)
  44. return clientConfig, nil
  45. } else {
  46. return &ECHClientConfig{
  47. ECHCapableConfig: clientConfig,
  48. dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
  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. deprecated.Report(ctx, deprecated.OptionLegacyECHOptions)
  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. lastTTL time.Duration
  105. lastUpdate time.Time
  106. }
  107. func (s *ECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
  108. tlsConn, err := s.fetchAndHandshake(ctx, conn)
  109. if err != nil {
  110. return nil, err
  111. }
  112. err = tlsConn.HandshakeContext(ctx)
  113. if err != nil {
  114. return nil, err
  115. }
  116. return tlsConn, nil
  117. }
  118. func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
  119. s.access.Lock()
  120. defer s.access.Unlock()
  121. if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Since(s.lastUpdate) > s.lastTTL {
  122. message := &mDNS.Msg{
  123. MsgHdr: mDNS.MsgHdr{
  124. RecursionDesired: true,
  125. },
  126. Question: []mDNS.Question{
  127. {
  128. Name: mDNS.Fqdn(s.ServerName()),
  129. Qtype: mDNS.TypeHTTPS,
  130. Qclass: mDNS.ClassINET,
  131. },
  132. },
  133. }
  134. response, err := s.dnsRouter.Exchange(ctx, message, adapter.DNSQueryOptions{})
  135. if err != nil {
  136. return nil, E.Cause(err, "fetch ECH config list")
  137. }
  138. if response.Rcode != mDNS.RcodeSuccess {
  139. return nil, E.Cause(dns.RcodeError(response.Rcode), "fetch ECH config list")
  140. }
  141. match:
  142. for _, rr := range response.Answer {
  143. switch resource := rr.(type) {
  144. case *mDNS.HTTPS:
  145. for _, value := range resource.Value {
  146. if value.Key().String() == "ech" {
  147. echConfigList, err := base64.StdEncoding.DecodeString(value.String())
  148. if err != nil {
  149. return nil, E.Cause(err, "decode ECH config")
  150. }
  151. s.lastTTL = time.Duration(rr.Header().Ttl) * time.Second
  152. s.lastUpdate = time.Now()
  153. s.SetECHConfigList(echConfigList)
  154. break match
  155. }
  156. }
  157. }
  158. }
  159. if len(s.ECHConfigList()) == 0 {
  160. return nil, E.New("no ECH config found in DNS records")
  161. }
  162. }
  163. return s.Client(conn)
  164. }
  165. func (s *ECHClientConfig) Clone() Config {
  166. return &ECHClientConfig{ECHCapableConfig: s.ECHCapableConfig.Clone().(ECHCapableConfig), dnsRouter: s.dnsRouter, lastUpdate: s.lastUpdate}
  167. }
  168. func UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) {
  169. var keys []tls.EncryptedClientHelloKey
  170. rawString := cryptobyte.String(raw)
  171. for !rawString.Empty() {
  172. var key tls.EncryptedClientHelloKey
  173. if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.PrivateKey)) {
  174. return nil, E.New("error parsing private key")
  175. }
  176. if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.Config)) {
  177. return nil, E.New("error parsing config")
  178. }
  179. keys = append(keys, key)
  180. }
  181. if len(keys) == 0 {
  182. return nil, E.New("empty ECH keys")
  183. }
  184. return keys, nil
  185. }