apple_client.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. //go:build darwin && cgo
  2. package tls
  3. import (
  4. "context"
  5. "net"
  6. "os"
  7. "strings"
  8. "time"
  9. "github.com/sagernet/sing-box/adapter"
  10. boxConstant "github.com/sagernet/sing-box/constant"
  11. "github.com/sagernet/sing-box/option"
  12. E "github.com/sagernet/sing/common/exceptions"
  13. "github.com/sagernet/sing/common/logger"
  14. "github.com/sagernet/sing/common/ntp"
  15. "github.com/sagernet/sing/service"
  16. )
  17. type appleCertificateStore interface {
  18. StoreKind() string
  19. CurrentPEM() []string
  20. }
  21. type appleClientConfig struct {
  22. serverName string
  23. nextProtos []string
  24. handshakeTimeout time.Duration
  25. minVersion uint16
  26. maxVersion uint16
  27. insecure bool
  28. anchorPEM string
  29. anchorOnly bool
  30. certificatePublicKeySHA256 [][]byte
  31. timeFunc func() time.Time
  32. }
  33. func (c *appleClientConfig) ServerName() string {
  34. return c.serverName
  35. }
  36. func (c *appleClientConfig) SetServerName(serverName string) {
  37. c.serverName = serverName
  38. }
  39. func (c *appleClientConfig) NextProtos() []string {
  40. return c.nextProtos
  41. }
  42. func (c *appleClientConfig) SetNextProtos(nextProto []string) {
  43. c.nextProtos = append(c.nextProtos[:0], nextProto...)
  44. }
  45. func (c *appleClientConfig) HandshakeTimeout() time.Duration {
  46. return c.handshakeTimeout
  47. }
  48. func (c *appleClientConfig) SetHandshakeTimeout(timeout time.Duration) {
  49. c.handshakeTimeout = timeout
  50. }
  51. func (c *appleClientConfig) STDConfig() (*STDConfig, error) {
  52. return nil, E.New("unsupported usage for Apple TLS engine")
  53. }
  54. func (c *appleClientConfig) Client(conn net.Conn) (Conn, error) {
  55. return nil, os.ErrInvalid
  56. }
  57. func (c *appleClientConfig) Clone() Config {
  58. return &appleClientConfig{
  59. serverName: c.serverName,
  60. nextProtos: append([]string(nil), c.nextProtos...),
  61. handshakeTimeout: c.handshakeTimeout,
  62. minVersion: c.minVersion,
  63. maxVersion: c.maxVersion,
  64. insecure: c.insecure,
  65. anchorPEM: c.anchorPEM,
  66. anchorOnly: c.anchorOnly,
  67. certificatePublicKeySHA256: append([][]byte(nil), c.certificatePublicKeySHA256...),
  68. timeFunc: c.timeFunc,
  69. }
  70. }
  71. func newAppleClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions, allowEmptyServerName bool) (Config, error) {
  72. validated, err := ValidateAppleTLSOptions(ctx, options, "Apple TLS engine")
  73. if err != nil {
  74. return nil, err
  75. }
  76. var serverName string
  77. if options.ServerName != "" {
  78. serverName = options.ServerName
  79. } else if serverAddress != "" {
  80. serverName = serverAddress
  81. }
  82. if serverName == "" && !options.Insecure && !allowEmptyServerName {
  83. return nil, errMissingServerName
  84. }
  85. var handshakeTimeout time.Duration
  86. if options.HandshakeTimeout > 0 {
  87. handshakeTimeout = options.HandshakeTimeout.Build()
  88. } else {
  89. handshakeTimeout = boxConstant.TCPTimeout
  90. }
  91. return &appleClientConfig{
  92. serverName: serverName,
  93. nextProtos: append([]string(nil), options.ALPN...),
  94. handshakeTimeout: handshakeTimeout,
  95. minVersion: validated.MinVersion,
  96. maxVersion: validated.MaxVersion,
  97. insecure: options.Insecure || len(options.CertificatePublicKeySHA256) > 0,
  98. anchorPEM: validated.AnchorPEM,
  99. anchorOnly: validated.AnchorOnly,
  100. certificatePublicKeySHA256: append([][]byte(nil), options.CertificatePublicKeySHA256...),
  101. timeFunc: ntp.TimeFuncFromContext(ctx),
  102. }, nil
  103. }
  104. type AppleTLSValidated struct {
  105. MinVersion uint16
  106. MaxVersion uint16
  107. AnchorPEM string
  108. AnchorOnly bool
  109. }
  110. func ValidateAppleTLSOptions(ctx context.Context, options option.OutboundTLSOptions, engineName string) (AppleTLSValidated, error) {
  111. if options.Reality != nil && options.Reality.Enabled {
  112. return AppleTLSValidated{}, E.New("reality is unsupported in ", engineName)
  113. }
  114. if options.UTLS != nil && options.UTLS.Enabled {
  115. return AppleTLSValidated{}, E.New("utls is unsupported in ", engineName)
  116. }
  117. if options.ECH != nil && options.ECH.Enabled {
  118. return AppleTLSValidated{}, E.New("ech is unsupported in ", engineName)
  119. }
  120. if options.DisableSNI {
  121. return AppleTLSValidated{}, E.New("disable_sni is unsupported in ", engineName)
  122. }
  123. if len(options.CipherSuites) > 0 {
  124. return AppleTLSValidated{}, E.New("cipher_suites is unsupported in ", engineName)
  125. }
  126. if len(options.CurvePreferences) > 0 {
  127. return AppleTLSValidated{}, E.New("curve_preferences is unsupported in ", engineName)
  128. }
  129. if len(options.ClientCertificate) > 0 || options.ClientCertificatePath != "" || len(options.ClientKey) > 0 || options.ClientKeyPath != "" {
  130. return AppleTLSValidated{}, E.New("client certificate is unsupported in ", engineName)
  131. }
  132. if options.Fragment || options.RecordFragment {
  133. return AppleTLSValidated{}, E.New("tls fragment is unsupported in ", engineName)
  134. }
  135. if options.KernelTx || options.KernelRx {
  136. return AppleTLSValidated{}, E.New("ktls is unsupported in ", engineName)
  137. }
  138. if options.Spoof != "" || options.SpoofMethod != "" {
  139. return AppleTLSValidated{}, E.New("spoof is unsupported in ", engineName)
  140. }
  141. if len(options.CertificatePublicKeySHA256) > 0 && (len(options.Certificate) > 0 || options.CertificatePath != "") {
  142. return AppleTLSValidated{}, E.New("certificate_public_key_sha256 is conflict with certificate or certificate_path")
  143. }
  144. var minVersion uint16
  145. if options.MinVersion != "" {
  146. var err error
  147. minVersion, err = ParseTLSVersion(options.MinVersion)
  148. if err != nil {
  149. return AppleTLSValidated{}, E.Cause(err, "parse min_version")
  150. }
  151. }
  152. var maxVersion uint16
  153. if options.MaxVersion != "" {
  154. var err error
  155. maxVersion, err = ParseTLSVersion(options.MaxVersion)
  156. if err != nil {
  157. return AppleTLSValidated{}, E.Cause(err, "parse max_version")
  158. }
  159. }
  160. anchorPEM, anchorOnly, err := AppleAnchorPEM(ctx, options)
  161. if err != nil {
  162. return AppleTLSValidated{}, err
  163. }
  164. return AppleTLSValidated{
  165. MinVersion: minVersion,
  166. MaxVersion: maxVersion,
  167. AnchorPEM: anchorPEM,
  168. AnchorOnly: anchorOnly,
  169. }, nil
  170. }
  171. func AppleAnchorPEM(ctx context.Context, options option.OutboundTLSOptions) (string, bool, error) {
  172. if len(options.Certificate) > 0 {
  173. return strings.Join(options.Certificate, "\n"), true, nil
  174. }
  175. if options.CertificatePath != "" {
  176. content, err := os.ReadFile(options.CertificatePath)
  177. if err != nil {
  178. return "", false, E.Cause(err, "read certificate")
  179. }
  180. return string(content), true, nil
  181. }
  182. certificateStore := service.FromContext[adapter.CertificateStore](ctx)
  183. if certificateStore == nil {
  184. return "", false, nil
  185. }
  186. store, ok := certificateStore.(appleCertificateStore)
  187. if !ok {
  188. return "", false, nil
  189. }
  190. switch store.StoreKind() {
  191. case boxConstant.CertificateStoreSystem, "":
  192. return strings.Join(store.CurrentPEM(), "\n"), false, nil
  193. case boxConstant.CertificateStoreMozilla, boxConstant.CertificateStoreChrome, boxConstant.CertificateStoreNone:
  194. return strings.Join(store.CurrentPEM(), "\n"), true, nil
  195. default:
  196. return "", false, E.New("unsupported certificate store for Apple TLS engine: ", store.StoreKind())
  197. }
  198. }