1
0

ech.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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. block, rest := pem.Decode(echKey)
  67. if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 {
  68. return E.New("invalid ECH keys pem")
  69. }
  70. echKeys, err := UnmarshalECHKeys(block.Bytes)
  71. if err != nil {
  72. return E.Cause(err, "parse ECH keys")
  73. }
  74. tlsConfig.EncryptedClientHelloKeys = echKeys
  75. //nolint:staticcheck
  76. if options.ECH.PQSignatureSchemesEnabled || options.ECH.DynamicRecordSizingDisabled {
  77. deprecated.Report(ctx, deprecated.OptionLegacyECHOptions)
  78. }
  79. return nil
  80. }
  81. func reloadECHKeys(echKeyPath string, tlsConfig *tls.Config) error {
  82. echKey, err := os.ReadFile(echKeyPath)
  83. if err != nil {
  84. return E.Cause(err, "reload ECH keys from ", echKeyPath)
  85. }
  86. block, _ := pem.Decode(echKey)
  87. if block == nil || block.Type != "ECH KEYS" {
  88. return E.New("invalid ECH keys pem")
  89. }
  90. echKeys, err := UnmarshalECHKeys(block.Bytes)
  91. if err != nil {
  92. return E.Cause(err, "parse ECH keys")
  93. }
  94. tlsConfig.EncryptedClientHelloKeys = echKeys
  95. return nil
  96. }
  97. type ECHClientConfig struct {
  98. ECHCapableConfig
  99. access sync.Mutex
  100. dnsRouter adapter.DNSRouter
  101. lastTTL time.Duration
  102. lastUpdate time.Time
  103. }
  104. func (s *ECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
  105. tlsConn, err := s.fetchAndHandshake(ctx, conn)
  106. if err != nil {
  107. return nil, err
  108. }
  109. err = tlsConn.HandshakeContext(ctx)
  110. if err != nil {
  111. return nil, err
  112. }
  113. return tlsConn, nil
  114. }
  115. func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
  116. s.access.Lock()
  117. defer s.access.Unlock()
  118. if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Now().Sub(s.lastUpdate) > s.lastTTL {
  119. message := &mDNS.Msg{
  120. MsgHdr: mDNS.MsgHdr{
  121. RecursionDesired: true,
  122. },
  123. Question: []mDNS.Question{
  124. {
  125. Name: mDNS.Fqdn(s.ServerName()),
  126. Qtype: mDNS.TypeHTTPS,
  127. Qclass: mDNS.ClassINET,
  128. },
  129. },
  130. }
  131. response, err := s.dnsRouter.Exchange(ctx, message, adapter.DNSQueryOptions{})
  132. if err != nil {
  133. return nil, E.Cause(err, "fetch ECH config list")
  134. }
  135. if response.Rcode != mDNS.RcodeSuccess {
  136. return nil, E.Cause(dns.RcodeError(response.Rcode), "fetch ECH config list")
  137. }
  138. match:
  139. for _, rr := range response.Answer {
  140. switch resource := rr.(type) {
  141. case *mDNS.HTTPS:
  142. for _, value := range resource.Value {
  143. if value.Key().String() == "ech" {
  144. echConfigList, err := base64.StdEncoding.DecodeString(value.String())
  145. if err != nil {
  146. return nil, E.Cause(err, "decode ECH config")
  147. }
  148. s.lastTTL = time.Duration(rr.Header().Ttl) * time.Second
  149. s.lastUpdate = time.Now()
  150. s.SetECHConfigList(echConfigList)
  151. break match
  152. }
  153. }
  154. }
  155. }
  156. if len(s.ECHConfigList()) == 0 {
  157. return nil, E.New("no ECH config found in DNS records")
  158. }
  159. }
  160. return s.Client(conn)
  161. }
  162. func (s *ECHClientConfig) Clone() Config {
  163. return &ECHClientConfig{ECHCapableConfig: s.ECHCapableConfig.Clone().(ECHCapableConfig), dnsRouter: s.dnsRouter, lastUpdate: s.lastUpdate}
  164. }
  165. func UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) {
  166. var keys []tls.EncryptedClientHelloKey
  167. rawString := cryptobyte.String(raw)
  168. for !rawString.Empty() {
  169. var key tls.EncryptedClientHelloKey
  170. if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.PrivateKey)) {
  171. return nil, E.New("error parsing private key")
  172. }
  173. if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.Config)) {
  174. return nil, E.New("error parsing config")
  175. }
  176. keys = append(keys, key)
  177. }
  178. if len(keys) == 0 {
  179. return nil, E.New("empty ECH keys")
  180. }
  181. return keys, nil
  182. }