ech.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  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. "github.com/sagernet/sing-box/adapter"
  12. "github.com/sagernet/sing-box/dns"
  13. "github.com/sagernet/sing-box/experimental/deprecated"
  14. "github.com/sagernet/sing-box/option"
  15. E "github.com/sagernet/sing/common/exceptions"
  16. aTLS "github.com/sagernet/sing/common/tls"
  17. "github.com/sagernet/sing/service"
  18. mDNS "github.com/miekg/dns"
  19. "golang.org/x/crypto/cryptobyte"
  20. )
  21. func parseECHClientConfig(ctx context.Context, options option.OutboundTLSOptions, tlsConfig *tls.Config) (Config, error) {
  22. var echConfig []byte
  23. if len(options.ECH.Config) > 0 {
  24. echConfig = []byte(strings.Join(options.ECH.Config, "\n"))
  25. } else if options.ECH.ConfigPath != "" {
  26. content, err := os.ReadFile(options.ECH.ConfigPath)
  27. if err != nil {
  28. return nil, E.Cause(err, "read ECH config")
  29. }
  30. echConfig = content
  31. }
  32. //nolint:staticcheck
  33. if options.ECH.PQSignatureSchemesEnabled || options.ECH.DynamicRecordSizingDisabled {
  34. deprecated.Report(ctx, deprecated.OptionLegacyECHOptions)
  35. }
  36. if len(echConfig) > 0 {
  37. block, rest := pem.Decode(echConfig)
  38. if block == nil || block.Type != "ECH CONFIGS" || len(rest) > 0 {
  39. return nil, E.New("invalid ECH configs pem")
  40. }
  41. tlsConfig.EncryptedClientHelloConfigList = block.Bytes
  42. return &STDClientConfig{tlsConfig}, nil
  43. } else {
  44. return &STDECHClientConfig{STDClientConfig{tlsConfig}, service.FromContext[adapter.DNSRouter](ctx)}, nil
  45. }
  46. }
  47. func parseECHServerConfig(ctx context.Context, options option.InboundTLSOptions, tlsConfig *tls.Config, echKeyPath *string) error {
  48. var echKey []byte
  49. if len(options.ECH.Key) > 0 {
  50. echKey = []byte(strings.Join(options.ECH.Key, "\n"))
  51. } else if options.ECH.KeyPath != "" {
  52. content, err := os.ReadFile(options.ECH.KeyPath)
  53. if err != nil {
  54. return E.Cause(err, "read ECH keys")
  55. }
  56. echKey = content
  57. *echKeyPath = options.ECH.KeyPath
  58. } else {
  59. return E.New("missing ECH keys")
  60. }
  61. block, rest := pem.Decode(echKey)
  62. if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 {
  63. return E.New("invalid ECH keys pem")
  64. }
  65. echKeys, err := UnmarshalECHKeys(block.Bytes)
  66. if err != nil {
  67. return E.Cause(err, "parse ECH keys")
  68. }
  69. tlsConfig.EncryptedClientHelloKeys = echKeys
  70. //nolint:staticcheck
  71. if options.ECH.PQSignatureSchemesEnabled || options.ECH.DynamicRecordSizingDisabled {
  72. deprecated.Report(ctx, deprecated.OptionLegacyECHOptions)
  73. }
  74. return nil
  75. }
  76. func reloadECHKeys(echKeyPath string, tlsConfig *tls.Config) error {
  77. echKey, err := os.ReadFile(echKeyPath)
  78. if err != nil {
  79. return E.Cause(err, "reload ECH keys from ", echKeyPath)
  80. }
  81. block, _ := pem.Decode(echKey)
  82. if block == nil || block.Type != "ECH KEYS" {
  83. return E.New("invalid ECH keys pem")
  84. }
  85. echKeys, err := UnmarshalECHKeys(block.Bytes)
  86. if err != nil {
  87. return E.Cause(err, "parse ECH keys")
  88. }
  89. tlsConfig.EncryptedClientHelloKeys = echKeys
  90. return nil
  91. }
  92. type STDECHClientConfig struct {
  93. STDClientConfig
  94. dnsRouter adapter.DNSRouter
  95. }
  96. func (s *STDECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
  97. if len(s.config.EncryptedClientHelloConfigList) == 0 {
  98. message := &mDNS.Msg{
  99. MsgHdr: mDNS.MsgHdr{
  100. RecursionDesired: true,
  101. },
  102. Question: []mDNS.Question{
  103. {
  104. Name: mDNS.Fqdn(s.config.ServerName),
  105. Qtype: mDNS.TypeHTTPS,
  106. Qclass: mDNS.ClassINET,
  107. },
  108. },
  109. }
  110. response, err := s.dnsRouter.Exchange(ctx, message, adapter.DNSQueryOptions{})
  111. if err != nil {
  112. return nil, E.Cause(err, "fetch ECH config list")
  113. }
  114. if response.Rcode != mDNS.RcodeSuccess {
  115. return nil, E.Cause(dns.RcodeError(response.Rcode), "fetch ECH config list")
  116. }
  117. for _, rr := range response.Answer {
  118. switch resource := rr.(type) {
  119. case *mDNS.HTTPS:
  120. for _, value := range resource.Value {
  121. if value.Key().String() == "ech" {
  122. echConfigList, err := base64.StdEncoding.DecodeString(value.String())
  123. if err != nil {
  124. return nil, E.Cause(err, "decode ECH config")
  125. }
  126. s.config.EncryptedClientHelloConfigList = echConfigList
  127. }
  128. }
  129. }
  130. }
  131. return nil, E.New("no ECH config found in DNS records")
  132. }
  133. tlsConn, err := s.Client(conn)
  134. if err != nil {
  135. return nil, err
  136. }
  137. err = tlsConn.HandshakeContext(ctx)
  138. if err != nil {
  139. return nil, err
  140. }
  141. return tlsConn, nil
  142. }
  143. func (s *STDECHClientConfig) Clone() Config {
  144. return &STDECHClientConfig{STDClientConfig{s.config.Clone()}, s.dnsRouter}
  145. }
  146. func UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) {
  147. var keys []tls.EncryptedClientHelloKey
  148. rawString := cryptobyte.String(raw)
  149. for !rawString.Empty() {
  150. var key tls.EncryptedClientHelloKey
  151. if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.PrivateKey)) {
  152. return nil, E.New("error parsing private key")
  153. }
  154. if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.Config)) {
  155. return nil, E.New("error parsing config")
  156. }
  157. keys = append(keys, key)
  158. }
  159. if len(keys) == 0 {
  160. return nil, E.New("empty ECH keys")
  161. }
  162. return keys, nil
  163. }