mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 20:28:32 +10:00
Compare commits
15 Commits
v1.2-beta2
...
v1.2-beta4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e99741159b | ||
|
|
6b9603227b | ||
|
|
23e8d282a3 | ||
|
|
611d6bbfc5 | ||
|
|
f26785c0ba | ||
|
|
5bcfb71737 | ||
|
|
4135c4974f | ||
|
|
222196b182 | ||
|
|
86e55c5c1c | ||
|
|
73c068b96f | ||
|
|
f516026540 | ||
|
|
3c5bc842ed | ||
|
|
d8270a66f4 | ||
|
|
123c383eae | ||
|
|
67814faf92 |
6
.github/workflows/mkdocs.yml
vendored
6
.github/workflows/mkdocs.yml
vendored
@@ -14,5 +14,7 @@ jobs:
|
|||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
- run: pip install mkdocs-material mkdocs-static-i18n
|
- run: |
|
||||||
- run: mkdocs gh-deploy -m "{sha}" --force --ignore-version --no-history
|
pip install mkdocs-material=="9.*" mkdocs-static-i18n=="0.53"
|
||||||
|
- run: |
|
||||||
|
mkdocs gh-deploy -m "{sha}" --force --ignore-version --no-history
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -5,4 +5,7 @@
|
|||||||
/site/
|
/site/
|
||||||
/bin/
|
/bin/
|
||||||
/dist/
|
/dist/
|
||||||
/sing-box
|
/sing-box
|
||||||
|
/build/
|
||||||
|
/*.jar
|
||||||
|
/*.aar
|
||||||
8
Makefile
8
Makefile
@@ -71,6 +71,14 @@ test_stdio:
|
|||||||
go mod tidy && \
|
go mod tidy && \
|
||||||
go test -v -tags "$(TAGS_TEST),force_stdio" .
|
go test -v -tags "$(TAGS_TEST),force_stdio" .
|
||||||
|
|
||||||
|
lib:
|
||||||
|
go run ./cmd/internal/build_libbox
|
||||||
|
|
||||||
|
lib_install:
|
||||||
|
go get -v -d
|
||||||
|
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.0.0-20221130124640-349ebaa752ca
|
||||||
|
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.0.0-20221130124640-349ebaa752ca
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf bin dist sing-box
|
rm -rf bin dist sing-box
|
||||||
rm -f $(shell go env GOPATH)/sing-box
|
rm -f $(shell go env GOPATH)/sing-box
|
||||||
|
|||||||
@@ -34,12 +34,15 @@ type Router interface {
|
|||||||
InterfaceFinder() control.InterfaceFinder
|
InterfaceFinder() control.InterfaceFinder
|
||||||
DefaultInterface() string
|
DefaultInterface() string
|
||||||
AutoDetectInterface() bool
|
AutoDetectInterface() bool
|
||||||
|
AutoDetectInterfaceFunc() control.Func
|
||||||
DefaultMark() int
|
DefaultMark() int
|
||||||
NetworkMonitor() tun.NetworkUpdateMonitor
|
NetworkMonitor() tun.NetworkUpdateMonitor
|
||||||
InterfaceMonitor() tun.DefaultInterfaceMonitor
|
InterfaceMonitor() tun.DefaultInterfaceMonitor
|
||||||
PackageManager() tun.PackageManager
|
PackageManager() tun.PackageManager
|
||||||
Rules() []Rule
|
Rules() []Rule
|
||||||
|
|
||||||
|
TimeService
|
||||||
|
|
||||||
ClashServer() ClashServer
|
ClashServer() ClashServer
|
||||||
SetClashServer(server ClashServer)
|
SetClashServer(server ClashServer)
|
||||||
|
|
||||||
|
|||||||
8
adapter/time.go
Normal file
8
adapter/time.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package adapter
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type TimeService interface {
|
||||||
|
Service
|
||||||
|
TimeFunc() func() time.Time
|
||||||
|
}
|
||||||
104
box.go
104
box.go
@@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/experimental"
|
"github.com/sagernet/sing-box/experimental"
|
||||||
"github.com/sagernet/sing-box/inbound"
|
"github.com/sagernet/sing-box/inbound"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
@@ -53,46 +54,52 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
|||||||
var logFactory log.Factory
|
var logFactory log.Factory
|
||||||
var observableLogFactory log.ObservableFactory
|
var observableLogFactory log.ObservableFactory
|
||||||
var logFile *os.File
|
var logFile *os.File
|
||||||
|
var logWriter io.Writer
|
||||||
if logOptions.Disabled {
|
if logOptions.Disabled {
|
||||||
observableLogFactory = log.NewNOPFactory()
|
observableLogFactory = log.NewNOPFactory()
|
||||||
logFactory = observableLogFactory
|
logFactory = observableLogFactory
|
||||||
} else {
|
} else {
|
||||||
var logWriter io.Writer
|
|
||||||
switch logOptions.Output {
|
switch logOptions.Output {
|
||||||
case "", "stderr":
|
case "":
|
||||||
|
if options.PlatformInterface != nil {
|
||||||
|
logWriter = io.Discard
|
||||||
|
} else {
|
||||||
|
logWriter = os.Stdout
|
||||||
|
}
|
||||||
|
case "stderr":
|
||||||
logWriter = os.Stderr
|
logWriter = os.Stderr
|
||||||
case "stdout":
|
case "stdout":
|
||||||
logWriter = os.Stdout
|
logWriter = os.Stdout
|
||||||
default:
|
default:
|
||||||
var err error
|
var err error
|
||||||
logFile, err = os.OpenFile(logOptions.Output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
logFile, err = os.OpenFile(C.BasePath(logOptions.Output), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
logWriter = logFile
|
logWriter = logFile
|
||||||
}
|
}
|
||||||
logFormatter := log.Formatter{
|
}
|
||||||
BaseTime: createdAt,
|
logFormatter := log.Formatter{
|
||||||
DisableColors: logOptions.DisableColor || logFile != nil,
|
BaseTime: createdAt,
|
||||||
DisableTimestamp: !logOptions.Timestamp && logFile != nil,
|
DisableColors: logOptions.DisableColor || logFile != nil,
|
||||||
FullTimestamp: logOptions.Timestamp,
|
DisableTimestamp: !logOptions.Timestamp && logFile != nil,
|
||||||
TimestampFormat: "-0700 2006-01-02 15:04:05",
|
FullTimestamp: logOptions.Timestamp,
|
||||||
}
|
TimestampFormat: "-0700 2006-01-02 15:04:05",
|
||||||
if needClashAPI {
|
}
|
||||||
observableLogFactory = log.NewObservableFactory(logFormatter, logWriter)
|
if needClashAPI {
|
||||||
logFactory = observableLogFactory
|
observableLogFactory = log.NewObservableFactory(logFormatter, logWriter, options.PlatformInterface)
|
||||||
} else {
|
logFactory = observableLogFactory
|
||||||
logFactory = log.NewFactory(logFormatter, logWriter)
|
} else {
|
||||||
}
|
logFactory = log.NewFactory(logFormatter, logWriter, options.PlatformInterface)
|
||||||
if logOptions.Level != "" {
|
}
|
||||||
logLevel, err := log.ParseLevel(logOptions.Level)
|
if logOptions.Level != "" {
|
||||||
if err != nil {
|
logLevel, err := log.ParseLevel(logOptions.Level)
|
||||||
return nil, E.Cause(err, "parse log level")
|
if err != nil {
|
||||||
}
|
return nil, E.Cause(err, "parse log level")
|
||||||
logFactory.SetLevel(logLevel)
|
|
||||||
} else {
|
|
||||||
logFactory.SetLevel(log.LevelTrace)
|
|
||||||
}
|
}
|
||||||
|
logFactory.SetLevel(logLevel)
|
||||||
|
} else {
|
||||||
|
logFactory.SetLevel(log.LevelTrace)
|
||||||
}
|
}
|
||||||
|
|
||||||
router, err := route.NewRouter(
|
router, err := route.NewRouter(
|
||||||
@@ -100,7 +107,9 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
|||||||
logFactory,
|
logFactory,
|
||||||
common.PtrValueOrDefault(options.Route),
|
common.PtrValueOrDefault(options.Route),
|
||||||
common.PtrValueOrDefault(options.DNS),
|
common.PtrValueOrDefault(options.DNS),
|
||||||
|
common.PtrValueOrDefault(options.NTP),
|
||||||
options.Inbounds,
|
options.Inbounds,
|
||||||
|
options.PlatformInterface,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "parse route options")
|
return nil, E.Cause(err, "parse route options")
|
||||||
@@ -120,6 +129,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
|||||||
router,
|
router,
|
||||||
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
||||||
inboundOptions,
|
inboundOptions,
|
||||||
|
options.PlatformInterface,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "parse inbound[", i, "]")
|
return nil, E.Cause(err, "parse inbound[", i, "]")
|
||||||
@@ -255,19 +265,43 @@ func (s *Box) Close() error {
|
|||||||
default:
|
default:
|
||||||
close(s.done)
|
close(s.done)
|
||||||
}
|
}
|
||||||
for _, in := range s.inbounds {
|
var errors error
|
||||||
in.Close()
|
for i, in := range s.inbounds {
|
||||||
|
errors = E.Append(errors, in.Close(), func(err error) error {
|
||||||
|
return E.Cause(err, "close inbound/", in.Type(), "[", i, "]")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
for _, out := range s.outbounds {
|
for i, out := range s.outbounds {
|
||||||
common.Close(out)
|
errors = E.Append(errors, common.Close(out), func(err error) error {
|
||||||
|
return E.Cause(err, "close inbound/", out.Type(), "[", i, "]")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return common.Close(
|
if err := common.Close(s.router); err != nil {
|
||||||
s.router,
|
errors = E.Append(errors, err, func(err error) error {
|
||||||
s.logFactory,
|
return E.Cause(err, "close router")
|
||||||
s.clashServer,
|
})
|
||||||
s.v2rayServer,
|
}
|
||||||
common.PtrOrNil(s.logFile),
|
if err := common.Close(s.logFactory); err != nil {
|
||||||
)
|
errors = E.Append(errors, err, func(err error) error {
|
||||||
|
return E.Cause(err, "close log factory")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := common.Close(s.clashServer); err != nil {
|
||||||
|
errors = E.Append(errors, err, func(err error) error {
|
||||||
|
return E.Cause(err, "close clash api server")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := common.Close(s.v2rayServer); err != nil {
|
||||||
|
errors = E.Append(errors, err, func(err error) error {
|
||||||
|
return E.Cause(err, "close v2ray api server")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if s.logFile != nil {
|
||||||
|
errors = E.Append(errors, s.logFile.Close(), func(err error) error {
|
||||||
|
return E.Cause(err, "close log file")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Box) Router() adapter.Router {
|
func (s *Box) Router() adapter.Router {
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
findSDK()
|
build_shared.FindSDK()
|
||||||
|
|
||||||
command := exec.Command(os.Args[1], os.Args[2:]...)
|
command := exec.Command(os.Args[1], os.Args[2:]...)
|
||||||
command.Stdout = os.Stdout
|
command.Stdout = os.Stdout
|
||||||
|
|||||||
61
cmd/internal/build_libbox/main.go
Normal file
61
cmd/internal/build_libbox/main.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
_ "github.com/sagernet/gomobile/asset"
|
||||||
|
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing/common/rw"
|
||||||
|
)
|
||||||
|
|
||||||
|
var debugEnabled bool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.BoolVar(&debugEnabled, "debug", false, "enable debug")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
build_shared.FindSDK()
|
||||||
|
build_shared.FindMobile()
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
"bind",
|
||||||
|
"-v",
|
||||||
|
"-androidapi", "21",
|
||||||
|
"-javapkg=io.nekohasekai",
|
||||||
|
"-libname=box",
|
||||||
|
}
|
||||||
|
if !debugEnabled {
|
||||||
|
args = append(args,
|
||||||
|
"-trimpath", "-ldflags=-s -w -buildid=",
|
||||||
|
"-tags", "with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api,debug",
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
args = append(args, "-tags", "with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api")
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, "./experimental/libbox")
|
||||||
|
|
||||||
|
command := exec.Command(build_shared.GoBinPath+"/gomobile", args...)
|
||||||
|
command.Stdout = os.Stdout
|
||||||
|
command.Stderr = os.Stderr
|
||||||
|
err := command.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = "libbox.aar"
|
||||||
|
copyPath := filepath.Join("..", "sing-box-for-android", "app", "libs")
|
||||||
|
if rw.FileExists(copyPath) {
|
||||||
|
copyPath, _ = filepath.Abs(copyPath)
|
||||||
|
err = rw.CopyFile(name, filepath.Join(copyPath, name))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Info("copied to ", copyPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package build_shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"go/build"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -18,7 +19,7 @@ var (
|
|||||||
androidNDKPath string
|
androidNDKPath string
|
||||||
)
|
)
|
||||||
|
|
||||||
func findSDK() {
|
func FindSDK() {
|
||||||
searchPath := []string{
|
searchPath := []string{
|
||||||
"$ANDROID_HOME",
|
"$ANDROID_HOME",
|
||||||
"$HOME/Android/Sdk",
|
"$HOME/Android/Sdk",
|
||||||
@@ -79,3 +80,13 @@ func findNDK() bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var GoBinPath string
|
||||||
|
|
||||||
|
func FindMobile() {
|
||||||
|
goBin := filepath.Join(build.Default.GOPATH, "bin")
|
||||||
|
if !rw.FileExists(goBin + "/" + "gobind") {
|
||||||
|
log.Fatal("missing gomobile installation")
|
||||||
|
}
|
||||||
|
GoBinPath = goBin
|
||||||
|
}
|
||||||
@@ -70,15 +70,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
|||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
} else if router.AutoDetectInterface() {
|
} else if router.AutoDetectInterface() {
|
||||||
const useInterfaceName = C.IsLinux
|
bindFunc := router.AutoDetectInterfaceFunc()
|
||||||
bindFunc := control.BindToInterfaceFunc(router.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
|
|
||||||
remoteAddr := M.ParseSocksaddr(address).Addr
|
|
||||||
if C.IsLinux {
|
|
||||||
return router.InterfaceMonitor().DefaultInterfaceName(remoteAddr), -1
|
|
||||||
} else {
|
|
||||||
return "", router.InterfaceMonitor().DefaultInterfaceIndex(remoteAddr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
} else if router.DefaultInterface() != "" {
|
} else if router.DefaultInterface() != "" {
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ import (
|
|||||||
const (
|
const (
|
||||||
VersionDraft29 = 0xff00001d
|
VersionDraft29 = 0xff00001d
|
||||||
Version1 = 0x1
|
Version1 = 0x1
|
||||||
Version2 = 0x709a50c4
|
Version2 = 0x6b3343cf
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
SaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99}
|
SaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99}
|
||||||
SaltV1 = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a}
|
SaltV1 = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a}
|
||||||
SaltV2 = []byte{0xa7, 0x07, 0xc2, 0x03, 0xa5, 0x9b, 0x47, 0x18, 0x4a, 0x1d, 0x62, 0xca, 0x57, 0x04, 0x06, 0xea, 0x7a, 0xe3, 0xe5, 0xd3}
|
SaltV2 = []byte{0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26, 0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func NewClient(router adapter.Router, serverAddress string, options option.Outbo
|
|||||||
} else if options.UTLS != nil && options.UTLS.Enabled {
|
} else if options.UTLS != nil && options.UTLS.Enabled {
|
||||||
return NewUTLSClient(router, serverAddress, options)
|
return NewUTLSClient(router, serverAddress, options)
|
||||||
} else {
|
} else {
|
||||||
return NewSTDClient(serverAddress, options)
|
return NewSTDClient(router, serverAddress, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ type Config interface {
|
|||||||
Clone() Config
|
Clone() Config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ConfigWithSessionIDGenerator interface {
|
||||||
|
SetSessionIDGenerator(generator func(clientHello []byte, sessionID []byte) error)
|
||||||
|
}
|
||||||
|
|
||||||
type ServerConfig interface {
|
type ServerConfig interface {
|
||||||
Config
|
Config
|
||||||
adapter.Service
|
adapter.Service
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ func NewECHClient(router adapter.Router, serverAddress string, options option.Ou
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tlsConfig cftls.Config
|
var tlsConfig cftls.Config
|
||||||
|
tlsConfig.Time = router.TimeFunc()
|
||||||
if options.DisableSNI {
|
if options.DisableSNI {
|
||||||
tlsConfig.ServerName = "127.0.0.1"
|
tlsConfig.ServerName = "127.0.0.1"
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateKeyPair(serverName string) (*tls.Certificate, error) {
|
func GenerateKeyPair(timeFunc func() time.Time, serverName string) (*tls.Certificate, error) {
|
||||||
|
if timeFunc == nil {
|
||||||
|
timeFunc = time.Now
|
||||||
|
}
|
||||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -22,8 +25,8 @@ func GenerateKeyPair(serverName string) (*tls.Certificate, error) {
|
|||||||
}
|
}
|
||||||
template := &x509.Certificate{
|
template := &x509.Certificate{
|
||||||
SerialNumber: serialNumber,
|
SerialNumber: serialNumber,
|
||||||
NotBefore: time.Now().Add(time.Hour * -1),
|
NotBefore: timeFunc().Add(time.Hour * -1),
|
||||||
NotAfter: time.Now().Add(time.Hour),
|
NotAfter: timeFunc().Add(time.Hour),
|
||||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
BasicConstraintsValid: true,
|
BasicConstraintsValid: true,
|
||||||
|
|||||||
@@ -5,17 +5,18 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/badtls"
|
"github.com/sagernet/sing-box/common/badtls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
func NewServer(ctx context.Context, router adapter.Router, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||||
if !options.Enabled {
|
if !options.Enabled {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return NewSTDServer(ctx, logger, options)
|
return NewSTDServer(ctx, router, logger, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {
|
func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
@@ -43,7 +44,7 @@ func (s *STDClientConfig) Clone() Config {
|
|||||||
return &STDClientConfig{s.config.Clone()}
|
return &STDClientConfig{s.config.Clone()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSTDClient(serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
func NewSTDClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||||
var serverName string
|
var serverName string
|
||||||
if options.ServerName != "" {
|
if options.ServerName != "" {
|
||||||
serverName = options.ServerName
|
serverName = options.ServerName
|
||||||
@@ -57,6 +58,7 @@ func NewSTDClient(serverAddress string, options option.OutboundTLSOptions) (Conf
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tlsConfig tls.Config
|
var tlsConfig tls.Config
|
||||||
|
tlsConfig.Time = router.TimeFunc()
|
||||||
if options.DisableSNI {
|
if options.DisableSNI {
|
||||||
tlsConfig.ServerName = "127.0.0.1"
|
tlsConfig.ServerName = "127.0.0.1"
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ func (c *STDServerConfig) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
func NewSTDServer(ctx context.Context, router adapter.Router, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||||
if !options.Enabled {
|
if !options.Enabled {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -175,6 +175,7 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
|||||||
} else {
|
} else {
|
||||||
tlsConfig = &tls.Config{}
|
tlsConfig = &tls.Config{}
|
||||||
}
|
}
|
||||||
|
tlsConfig.Time = router.TimeFunc()
|
||||||
if options.ServerName != "" {
|
if options.ServerName != "" {
|
||||||
tlsConfig.ServerName = options.ServerName
|
tlsConfig.ServerName = options.ServerName
|
||||||
}
|
}
|
||||||
@@ -230,7 +231,7 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
|||||||
}
|
}
|
||||||
if certificate == nil && key == nil && options.Insecure {
|
if certificate == nil && key == nil && options.Insecure {
|
||||||
tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
return GenerateKeyPair(info.ServerName)
|
return GenerateKeyPair(router.TimeFunc(), info.ServerName)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if certificate == nil {
|
if certificate == nil {
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
utls "github.com/sagernet/utls"
|
||||||
utls "github.com/refraction-networking/utls"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type UTLSClientConfig struct {
|
type UTLSClientConfig struct {
|
||||||
@@ -45,6 +44,10 @@ func (e *UTLSClientConfig) Client(conn net.Conn) Conn {
|
|||||||
return &utlsConnWrapper{utls.UClient(conn, e.config.Clone(), e.id)}
|
return &utlsConnWrapper{utls.UClient(conn, e.config.Clone(), e.id)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *UTLSClientConfig) SetSessionIDGenerator(generator func(clientHello []byte, sessionID []byte) error) {
|
||||||
|
e.config.SessionIDGenerator = generator
|
||||||
|
}
|
||||||
|
|
||||||
type utlsConnWrapper struct {
|
type utlsConnWrapper struct {
|
||||||
*utls.UConn
|
*utls.UConn
|
||||||
}
|
}
|
||||||
@@ -88,6 +91,7 @@ func NewUTLSClient(router adapter.Router, serverAddress string, options option.O
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tlsConfig utls.Config
|
var tlsConfig utls.Config
|
||||||
|
tlsConfig.Time = router.TimeFunc()
|
||||||
if options.DisableSNI {
|
if options.DisableSNI {
|
||||||
tlsConfig.ServerName = "127.0.0.1"
|
tlsConfig.ServerName = "127.0.0.1"
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -3,13 +3,28 @@ package constant
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/rw"
|
"github.com/sagernet/sing/common/rw"
|
||||||
)
|
)
|
||||||
|
|
||||||
const dirName = "sing-box"
|
const dirName = "sing-box"
|
||||||
|
|
||||||
var resourcePaths []string
|
var (
|
||||||
|
basePath string
|
||||||
|
resourcePaths []string
|
||||||
|
)
|
||||||
|
|
||||||
|
func BasePath(name string) string {
|
||||||
|
if basePath == "" || strings.HasPrefix(name, "/") {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return filepath.Join(basePath, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetBasePath(path string) {
|
||||||
|
basePath = path
|
||||||
|
}
|
||||||
|
|
||||||
func FindPath(name string) (string, bool) {
|
func FindPath(name string) (string, bool) {
|
||||||
name = os.ExpandEnv(name)
|
name = os.ExpandEnv(name)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package constant
|
package constant
|
||||||
|
|
||||||
var Version = "1.2-beta2"
|
var Version = "1.2-beta4"
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
#### 1.2-beta4
|
||||||
|
|
||||||
|
* Add [NTP service](/configuration/ntp)
|
||||||
|
* Add Add multiple server names and multi-user support for shadowtls
|
||||||
|
* Add strict mode support for shadowtls v3
|
||||||
|
|
||||||
|
#### 1.2-beta3
|
||||||
|
|
||||||
|
* Update QUIC v2 version number and initial salt
|
||||||
|
* Fix shadowtls v3 implementation
|
||||||
|
|
||||||
#### 1.2-beta2
|
#### 1.2-beta2
|
||||||
|
|
||||||
* Add [ShadowTLS protocol v3](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-v3-en.md)
|
* Add [ShadowTLS protocol v3](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-v3-en.md)
|
||||||
|
|||||||
@@ -7,13 +7,27 @@
|
|||||||
|
|
||||||
... // Listen Fields
|
... // Listen Fields
|
||||||
|
|
||||||
"version": 2,
|
"version": 3,
|
||||||
"password": "fuck me till the daylight",
|
"password": "fuck me till the daylight",
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"name": "sekai",
|
||||||
|
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||||
|
}
|
||||||
|
],
|
||||||
"handshake": {
|
"handshake": {
|
||||||
"server": "google.com",
|
"server": "google.com",
|
||||||
"server_port": 443,
|
"server_port": 443,
|
||||||
|
|
||||||
... // Dial Fields
|
... // Dial Fields
|
||||||
|
},
|
||||||
|
"handshake_for_server_name": {
|
||||||
|
"example.com": {
|
||||||
|
"server": "example.com",
|
||||||
|
"server_port": 443,
|
||||||
|
|
||||||
|
... // Dial Fields
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -32,15 +46,29 @@ ShadowTLS protocol version.
|
|||||||
|---------------|-----------------------------------------------------------------------------------------|
|
|---------------|-----------------------------------------------------------------------------------------|
|
||||||
| `1` (default) | [ShadowTLS v1](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v1) |
|
| `1` (default) | [ShadowTLS v1](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v1) |
|
||||||
| `2` | [ShadowTLS v2](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v2) |
|
| `2` | [ShadowTLS v2](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v2) |
|
||||||
|
| `3` | [ShadowTLS v3](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-v3-en.md) |
|
||||||
|
|
||||||
#### password
|
#### password
|
||||||
|
|
||||||
Set password.
|
ShadowTLS password.
|
||||||
|
|
||||||
Only available in the ShadowTLS v2 protocol.
|
Only available in the ShadowTLS protocol 2.
|
||||||
|
|
||||||
|
|
||||||
|
#### users
|
||||||
|
|
||||||
|
ShadowTLS users.
|
||||||
|
|
||||||
|
Only available in the ShadowTLS protocol 3.
|
||||||
|
|
||||||
#### handshake
|
#### handshake
|
||||||
|
|
||||||
==Required==
|
==Required==
|
||||||
|
|
||||||
Handshake server address and [Dial options](/configuration/shared/dial).
|
Handshake server address and [Dial options](/configuration/shared/dial).
|
||||||
|
|
||||||
|
#### handshake
|
||||||
|
|
||||||
|
Handshake server address and [Dial options](/configuration/shared/dial) for specific server name.
|
||||||
|
|
||||||
|
Only available in the ShadowTLS protocol 2/3.
|
||||||
|
|||||||
@@ -7,13 +7,27 @@
|
|||||||
|
|
||||||
... // 监听字段
|
... // 监听字段
|
||||||
|
|
||||||
"version": 2,
|
"version": 3,
|
||||||
"password": "fuck me till the daylight",
|
"password": "fuck me till the daylight",
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"name": "sekai",
|
||||||
|
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||||
|
}
|
||||||
|
],
|
||||||
"handshake": {
|
"handshake": {
|
||||||
"server": "google.com",
|
"server": "google.com",
|
||||||
"server_port": 443,
|
"server_port": 443,
|
||||||
|
|
||||||
... // 拨号字段
|
... // 拨号字段
|
||||||
|
},
|
||||||
|
"handshake_for_server_name": {
|
||||||
|
"example.com": {
|
||||||
|
"server": "example.com",
|
||||||
|
"server_port": 443,
|
||||||
|
|
||||||
|
... // 拨号字段
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -32,15 +46,30 @@ ShadowTLS 协议版本。
|
|||||||
|---------------|-----------------------------------------------------------------------------------------|
|
|---------------|-----------------------------------------------------------------------------------------|
|
||||||
| `1` (default) | [ShadowTLS v1](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v1) |
|
| `1` (default) | [ShadowTLS v1](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v1) |
|
||||||
| `2` | [ShadowTLS v2](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v2) |
|
| `2` | [ShadowTLS v2](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v2) |
|
||||||
|
| `3` | [ShadowTLS v3](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-v3-en.md) |
|
||||||
|
|
||||||
#### password
|
#### password
|
||||||
|
|
||||||
设置密码。
|
ShadowTLS 密码。
|
||||||
|
|
||||||
仅在 ShadowTLS v2 协议中可用。
|
仅在 ShadowTLS 协议版本 2 中可用。
|
||||||
|
|
||||||
|
#### users
|
||||||
|
|
||||||
|
ShadowTLS 用户。
|
||||||
|
|
||||||
|
仅在 ShadowTLS 协议版本 3 中可用。
|
||||||
|
|
||||||
#### handshake
|
#### handshake
|
||||||
|
|
||||||
==必填==
|
==必填==
|
||||||
|
|
||||||
握手服务器地址和 [拨号参数](/zh/configuration/shared/dial/)。
|
握手服务器地址和 [拨号参数](/zh/configuration/shared/dial/)。
|
||||||
|
|
||||||
|
#### handshake
|
||||||
|
|
||||||
|
==必填==
|
||||||
|
|
||||||
|
对于特定服务器名称的握手服务器地址和 [拨号参数](/zh/configuration/shared/dial/)。
|
||||||
|
|
||||||
|
仅在 ShadowTLS 协议版本 2/3 中可用。
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ sing-box uses JSON for configuration files.
|
|||||||
{
|
{
|
||||||
"log": {},
|
"log": {},
|
||||||
"dns": {},
|
"dns": {},
|
||||||
|
"ntp": {},
|
||||||
"inbounds": [],
|
"inbounds": [],
|
||||||
"outbounds": [],
|
"outbounds": [],
|
||||||
"route": {},
|
"route": {},
|
||||||
@@ -21,6 +22,7 @@ sing-box uses JSON for configuration files.
|
|||||||
|----------------|--------------------------------|
|
|----------------|--------------------------------|
|
||||||
| `log` | [Log](./log) |
|
| `log` | [Log](./log) |
|
||||||
| `dns` | [DNS](./dns) |
|
| `dns` | [DNS](./dns) |
|
||||||
|
| `ntp` | [NTP](./ntp) |
|
||||||
| `inbounds` | [Inbound](./inbound) |
|
| `inbounds` | [Inbound](./inbound) |
|
||||||
| `outbounds` | [Outbound](./outbound) |
|
| `outbounds` | [Outbound](./outbound) |
|
||||||
| `route` | [Route](./route) |
|
| `route` | [Route](./route) |
|
||||||
|
|||||||
50
docs/configuration/ntp/index.md
Normal file
50
docs/configuration/ntp/index.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# NTP
|
||||||
|
|
||||||
|
Built-in NTP client service.
|
||||||
|
|
||||||
|
If enabled, it will provide time for protocols like TLS/Shadowsocks/VMess, which is useful for environments where time
|
||||||
|
synchronization is not possible.
|
||||||
|
|
||||||
|
### Structure
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ntp": {
|
||||||
|
"enabled": false,
|
||||||
|
"server": "ntp.apple.com",
|
||||||
|
"server_port": 123,
|
||||||
|
"interval": "30m",
|
||||||
|
|
||||||
|
... // Dial Fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fields
|
||||||
|
|
||||||
|
#### enabled
|
||||||
|
|
||||||
|
Enable NTP service.
|
||||||
|
|
||||||
|
#### server
|
||||||
|
|
||||||
|
==Required==
|
||||||
|
|
||||||
|
NTP server address.
|
||||||
|
|
||||||
|
#### server_port
|
||||||
|
|
||||||
|
NTP server port.
|
||||||
|
|
||||||
|
123 is used by default.
|
||||||
|
|
||||||
|
#### interval
|
||||||
|
|
||||||
|
Time synchronization interval.
|
||||||
|
|
||||||
|
30 minutes is used by default.
|
||||||
|
|
||||||
|
### Dial Fields
|
||||||
|
|
||||||
|
See [Dial Fields](/configuration/shared/dial) for details.
|
||||||
49
docs/configuration/ntp/index.zh.md
Normal file
49
docs/configuration/ntp/index.zh.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# NTP
|
||||||
|
|
||||||
|
内建的 NTP 客户端服务。
|
||||||
|
|
||||||
|
如果启用,它将为像 TLS/Shadowsocks/VMess 这样的协议提供时间,这对于无法进行时间同步的环境很有用。
|
||||||
|
|
||||||
|
### 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ntp": {
|
||||||
|
"enabled": false,
|
||||||
|
"server": "ntp.apple.com",
|
||||||
|
"server_port": 123,
|
||||||
|
"interval": "30m",
|
||||||
|
|
||||||
|
... // 拨号字段
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### enabled
|
||||||
|
|
||||||
|
启用 NTP 服务。
|
||||||
|
|
||||||
|
#### server
|
||||||
|
|
||||||
|
==必填==
|
||||||
|
|
||||||
|
NTP 服务器地址。
|
||||||
|
|
||||||
|
#### server_port
|
||||||
|
|
||||||
|
NTP 服务器端口。
|
||||||
|
|
||||||
|
默认使用 123。
|
||||||
|
|
||||||
|
#### interval
|
||||||
|
|
||||||
|
时间同步间隔。
|
||||||
|
|
||||||
|
默认使用 30 分钟。
|
||||||
|
|
||||||
|
### 拨号字段
|
||||||
|
|
||||||
|
参阅 [拨号字段](/zh/configuration/shared/dial/)。
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
"type": "shadowtls",
|
"type": "shadowtls",
|
||||||
"listen": "::",
|
"listen": "::",
|
||||||
"listen_port": 4443,
|
"listen_port": 4443,
|
||||||
"version": 2,
|
"version": 3,
|
||||||
"password": "fuck me till the daylight",
|
"password": "fuck me till the daylight",
|
||||||
"handshake": {
|
"handshake": {
|
||||||
"server": "google.com",
|
"server": "google.com",
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
"tag": "shadowtls-out",
|
"tag": "shadowtls-out",
|
||||||
"server": "127.0.0.1",
|
"server": "127.0.0.1",
|
||||||
"server_port": 4443,
|
"server_port": 4443,
|
||||||
"version": 2,
|
"version": 3,
|
||||||
"password": "fuck me till the daylight",
|
"password": "fuck me till the daylight",
|
||||||
"tls": {
|
"tls": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ type Server struct {
|
|||||||
httpServer *http.Server
|
httpServer *http.Server
|
||||||
trafficManager *trafficontrol.Manager
|
trafficManager *trafficontrol.Manager
|
||||||
urlTestHistory *urltest.HistoryStorage
|
urlTestHistory *urltest.HistoryStorage
|
||||||
tcpListener net.Listener
|
|
||||||
mode string
|
mode string
|
||||||
storeSelected bool
|
storeSelected bool
|
||||||
cacheFile adapter.ClashCacheFile
|
cacheFile adapter.ClashCacheFile
|
||||||
@@ -71,6 +70,11 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
|||||||
if cachePath == "" {
|
if cachePath == "" {
|
||||||
cachePath = "cache.db"
|
cachePath = "cache.db"
|
||||||
}
|
}
|
||||||
|
if foundPath, loaded := C.FindPath(cachePath); loaded {
|
||||||
|
cachePath = foundPath
|
||||||
|
} else {
|
||||||
|
cachePath = C.BasePath(cachePath)
|
||||||
|
}
|
||||||
cacheFile, err := cachefile.Open(cachePath)
|
cacheFile, err := cachefile.Open(cachePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "open cache file")
|
return nil, E.Cause(err, "open cache file")
|
||||||
@@ -103,7 +107,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
|||||||
})
|
})
|
||||||
if options.ExternalUI != "" {
|
if options.ExternalUI != "" {
|
||||||
chiRouter.Group(func(r chi.Router) {
|
chiRouter.Group(func(r chi.Router) {
|
||||||
fs := http.StripPrefix("/ui", http.FileServer(http.Dir(os.ExpandEnv(options.ExternalUI))))
|
fs := http.StripPrefix("/ui", http.FileServer(http.Dir(C.BasePath(os.ExpandEnv(options.ExternalUI)))))
|
||||||
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusTemporaryRedirect).ServeHTTP)
|
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusTemporaryRedirect).ServeHTTP)
|
||||||
r.Get("/ui/*", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/ui/*", func(w http.ResponseWriter, r *http.Request) {
|
||||||
fs.ServeHTTP(w, r)
|
fs.ServeHTTP(w, r)
|
||||||
@@ -119,7 +123,6 @@ func (s *Server) Start() error {
|
|||||||
return E.Cause(err, "external controller listen error")
|
return E.Cause(err, "external controller listen error")
|
||||||
}
|
}
|
||||||
s.logger.Info("restful api listening at ", listener.Addr())
|
s.logger.Info("restful api listening at ", listener.Addr())
|
||||||
s.tcpListener = listener
|
|
||||||
go func() {
|
go func() {
|
||||||
err = s.httpServer.Serve(listener)
|
err = s.httpServer.Serve(listener)
|
||||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
@@ -132,7 +135,6 @@ func (s *Server) Start() error {
|
|||||||
func (s *Server) Close() error {
|
func (s *Server) Close() error {
|
||||||
return common.Close(
|
return common.Close(
|
||||||
common.PtrOrNil(s.httpServer),
|
common.PtrOrNil(s.httpServer),
|
||||||
s.tcpListener,
|
|
||||||
s.trafficManager,
|
s.trafficManager,
|
||||||
s.cacheFile,
|
s.cacheFile,
|
||||||
)
|
)
|
||||||
|
|||||||
15
experimental/libbox/config.go
Normal file
15
experimental/libbox/config.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseConfig(configContent string) (option.Options, error) {
|
||||||
|
var options option.Options
|
||||||
|
err := options.UnmarshalJSON([]byte(configContent))
|
||||||
|
if err != nil {
|
||||||
|
return option.Options{}, E.Cause(err, "decode config")
|
||||||
|
}
|
||||||
|
return options, nil
|
||||||
|
}
|
||||||
148
experimental/libbox/internal/procfs/procfs.go
Normal file
148
experimental/libbox/internal/procfs/procfs.go
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
package procfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
netIndexOfLocal = -1
|
||||||
|
netIndexOfUid = -1
|
||||||
|
nativeEndian binary.ByteOrder
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var x uint32 = 0x01020304
|
||||||
|
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
|
||||||
|
nativeEndian = binary.BigEndian
|
||||||
|
} else {
|
||||||
|
nativeEndian = binary.LittleEndian
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveSocketByProcSearch(network string, source, _ netip.AddrPort) int32 {
|
||||||
|
if netIndexOfLocal < 0 || netIndexOfUid < 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
path := "/proc/net/"
|
||||||
|
|
||||||
|
if network == N.NetworkTCP {
|
||||||
|
path += "tcp"
|
||||||
|
} else {
|
||||||
|
path += "udp"
|
||||||
|
}
|
||||||
|
|
||||||
|
if source.Addr().Is6() {
|
||||||
|
path += "6"
|
||||||
|
}
|
||||||
|
|
||||||
|
sIP := source.Addr().AsSlice()
|
||||||
|
if len(sIP) == 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes [2]byte
|
||||||
|
binary.BigEndian.PutUint16(bytes[:], source.Port())
|
||||||
|
local := fmt.Sprintf("%s:%s", hex.EncodeToString(nativeEndianIP(sIP)), hex.EncodeToString(bytes[:]))
|
||||||
|
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
reader := bufio.NewReader(file)
|
||||||
|
|
||||||
|
for {
|
||||||
|
row, _, err := reader.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Fields(string(row))
|
||||||
|
|
||||||
|
if len(fields) <= netIndexOfLocal || len(fields) <= netIndexOfUid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.EqualFold(local, fields[netIndexOfLocal]) {
|
||||||
|
uid, err := strconv.Atoi(fields[netIndexOfUid])
|
||||||
|
if err != nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return int32(uid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nativeEndianIP(ip net.IP) []byte {
|
||||||
|
result := make([]byte, len(ip))
|
||||||
|
|
||||||
|
for i := 0; i < len(ip); i += 4 {
|
||||||
|
value := binary.BigEndian.Uint32(ip[i:])
|
||||||
|
|
||||||
|
nativeEndian.PutUint32(result[i:], value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
file, err := os.Open("/proc/net/tcp")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
reader := bufio.NewReader(file)
|
||||||
|
|
||||||
|
header, _, err := reader.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
columns := strings.Fields(string(header))
|
||||||
|
|
||||||
|
var txQueue, rxQueue, tr, tmWhen bool
|
||||||
|
|
||||||
|
for idx, col := range columns {
|
||||||
|
offset := 0
|
||||||
|
|
||||||
|
if txQueue && rxQueue {
|
||||||
|
offset--
|
||||||
|
}
|
||||||
|
|
||||||
|
if tr && tmWhen {
|
||||||
|
offset--
|
||||||
|
}
|
||||||
|
|
||||||
|
switch col {
|
||||||
|
case "tx_queue":
|
||||||
|
txQueue = true
|
||||||
|
case "rx_queue":
|
||||||
|
rxQueue = true
|
||||||
|
case "tr":
|
||||||
|
tr = true
|
||||||
|
case "tm->when":
|
||||||
|
tmWhen = true
|
||||||
|
case "local_address":
|
||||||
|
netIndexOfLocal = idx + offset
|
||||||
|
case "uid":
|
||||||
|
netIndexOfUid = idx + offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
experimental/libbox/iterator.go
Normal file
31
experimental/libbox/iterator.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package libbox
|
||||||
|
|
||||||
|
import "github.com/sagernet/sing/common"
|
||||||
|
|
||||||
|
type StringIterator interface {
|
||||||
|
Next() string
|
||||||
|
HasNext() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ StringIterator = (*iterator[string])(nil)
|
||||||
|
|
||||||
|
type iterator[T any] struct {
|
||||||
|
values []T
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIterator[T any](values []T) *iterator[T] {
|
||||||
|
return &iterator[T]{values}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *iterator[T]) Next() T {
|
||||||
|
if len(i.values) == 0 {
|
||||||
|
return common.DefaultValue[T]()
|
||||||
|
}
|
||||||
|
nextValue := i.values[0]
|
||||||
|
i.values = i.values[1:]
|
||||||
|
return nextValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *iterator[T]) HasNext() bool {
|
||||||
|
return len(i.values) > 0
|
||||||
|
}
|
||||||
16
experimental/libbox/platform.go
Normal file
16
experimental/libbox/platform.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package libbox
|
||||||
|
|
||||||
|
type PlatformInterface interface {
|
||||||
|
AutoDetectInterfaceControl(fd int32) error
|
||||||
|
OpenTun(options TunOptions) (TunInterface, error)
|
||||||
|
WriteLog(message string)
|
||||||
|
UseProcFS() bool
|
||||||
|
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
|
||||||
|
PackageNameByUid(uid int32) (string, error)
|
||||||
|
UIDByPackageName(packageName string) (int32, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TunInterface interface {
|
||||||
|
FileDescriptor() int32
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
16
experimental/libbox/platform/interface.go
Normal file
16
experimental/libbox/platform/interface.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package platform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/common/process"
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
|
"github.com/sagernet/sing/common/control"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Interface interface {
|
||||||
|
AutoDetectInterfaceControl() control.Func
|
||||||
|
OpenTun(options tun.Options) (tun.Tun, error)
|
||||||
|
process.Searcher
|
||||||
|
io.Writer
|
||||||
|
}
|
||||||
35
experimental/libbox/pprof.go
Normal file
35
experimental/libbox/pprof.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
//go:build debug
|
||||||
|
|
||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
_ "net/http/pprof"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PProfServer struct {
|
||||||
|
server *http.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPProfServer(port int) *PProfServer {
|
||||||
|
return &PProfServer{
|
||||||
|
&http.Server{
|
||||||
|
Addr: ":" + strconv.Itoa(port),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PProfServer) Start() error {
|
||||||
|
ln, err := net.Listen("tcp", s.server.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go s.server.Serve(ln)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PProfServer) Close() error {
|
||||||
|
return s.server.Close()
|
||||||
|
}
|
||||||
21
experimental/libbox/pprof_stub.go
Normal file
21
experimental/libbox/pprof_stub.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
//go:build !debug
|
||||||
|
|
||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PProfServer struct{}
|
||||||
|
|
||||||
|
func NewPProfServer(port int) *PProfServer {
|
||||||
|
return &PProfServer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PProfServer) Start() error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PProfServer) Close() error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
120
experimental/libbox/service.go
Normal file
120
experimental/libbox/service.go
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box"
|
||||||
|
"github.com/sagernet/sing-box/common/process"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
|
"github.com/sagernet/sing/common/control"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BoxService struct {
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
instance *box.Box
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
||||||
|
options, err := parseConfig(configContent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
options.PlatformInterface = &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
instance, err := box.New(ctx, options)
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
return nil, E.Cause(err, "create service")
|
||||||
|
}
|
||||||
|
return &BoxService{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
instance: instance,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BoxService) Start() error {
|
||||||
|
return s.instance.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BoxService) Close() error {
|
||||||
|
s.cancel()
|
||||||
|
return s.instance.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ platform.Interface = (*platformInterfaceWrapper)(nil)
|
||||||
|
|
||||||
|
type platformInterfaceWrapper struct {
|
||||||
|
iif PlatformInterface
|
||||||
|
useProcFS bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func {
|
||||||
|
return func(network, address string, conn syscall.RawConn) error {
|
||||||
|
return control.Raw(conn, func(fd uintptr) error {
|
||||||
|
return w.iif.AutoDetectInterfaceControl(int32(fd))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) OpenTun(options tun.Options) (tun.Tun, error) {
|
||||||
|
if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 {
|
||||||
|
return nil, E.New("android: unsupported uid options")
|
||||||
|
}
|
||||||
|
if len(options.IncludeAndroidUser) > 0 {
|
||||||
|
return nil, E.New("android: unsupported android_user option")
|
||||||
|
}
|
||||||
|
|
||||||
|
optionsWrapper := tunOptions(options)
|
||||||
|
tunInterface, err := w.iif.OpenTun(&optionsWrapper)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tunFd := tunInterface.FileDescriptor()
|
||||||
|
return &nativeTun{
|
||||||
|
tunFd: int(tunFd),
|
||||||
|
tunFile: os.NewFile(uintptr(tunFd), "tun"),
|
||||||
|
tunMTU: options.MTU,
|
||||||
|
closer: tunInterface,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) Write(p []byte) (n int, err error) {
|
||||||
|
w.iif.WriteLog(string(p))
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*process.Info, error) {
|
||||||
|
var uid int32
|
||||||
|
if w.useProcFS {
|
||||||
|
uid = procfs.ResolveSocketByProcSearch(network, source, destination)
|
||||||
|
if uid == -1 {
|
||||||
|
return nil, E.New("procfs: not found")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var ipProtocol int32
|
||||||
|
switch N.NetworkName(network) {
|
||||||
|
case N.NetworkTCP:
|
||||||
|
ipProtocol = syscall.IPPROTO_TCP
|
||||||
|
case N.NetworkUDP:
|
||||||
|
ipProtocol = syscall.IPPROTO_UDP
|
||||||
|
default:
|
||||||
|
return nil, E.New("unknown network: ", network)
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
uid, err = w.iif.FindConnectionOwner(ipProtocol, source.Addr().String(), int32(source.Port()), destination.Addr().String(), int32(destination.Port()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
packageName, _ := w.iif.PackageNameByUid(uid)
|
||||||
|
return &process.Info{UserId: uid, PackageName: packageName}, nil
|
||||||
|
}
|
||||||
7
experimental/libbox/setup.go
Normal file
7
experimental/libbox/setup.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package libbox
|
||||||
|
|
||||||
|
import C "github.com/sagernet/sing-box/constant"
|
||||||
|
|
||||||
|
func SetBasePath(path string) {
|
||||||
|
C.SetBasePath(path)
|
||||||
|
}
|
||||||
109
experimental/libbox/tun.go
Normal file
109
experimental/libbox/tun.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TunOptions interface {
|
||||||
|
GetInet4Address() RoutePrefixIterator
|
||||||
|
GetInet6Address() RoutePrefixIterator
|
||||||
|
GetDNSServerAddress() (string, error)
|
||||||
|
GetMTU() int32
|
||||||
|
GetAutoRoute() bool
|
||||||
|
GetStrictRoute() bool
|
||||||
|
GetInet4RouteAddress() RoutePrefixIterator
|
||||||
|
GetInet6RouteAddress() RoutePrefixIterator
|
||||||
|
GetIncludePackage() StringIterator
|
||||||
|
GetExcludePackage() StringIterator
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoutePrefix struct {
|
||||||
|
Address string
|
||||||
|
Prefix int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoutePrefixIterator interface {
|
||||||
|
Next() *RoutePrefix
|
||||||
|
HasNext() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapRoutePrefix(prefixes []netip.Prefix) RoutePrefixIterator {
|
||||||
|
return newIterator(common.Map(prefixes, func(prefix netip.Prefix) *RoutePrefix {
|
||||||
|
return &RoutePrefix{
|
||||||
|
Address: prefix.Addr().String(),
|
||||||
|
Prefix: int32(prefix.Bits()),
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TunOptions = (*tunOptions)(nil)
|
||||||
|
|
||||||
|
type tunOptions tun.Options
|
||||||
|
|
||||||
|
func (o *tunOptions) GetInet4Address() RoutePrefixIterator {
|
||||||
|
return mapRoutePrefix(o.Inet4Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetInet6Address() RoutePrefixIterator {
|
||||||
|
return mapRoutePrefix(o.Inet6Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetDNSServerAddress() (string, error) {
|
||||||
|
if len(o.Inet4Address) == 0 || o.Inet4Address[0].Bits() == 32 {
|
||||||
|
return "", E.New("need one more IPv4 address for DNS hijacking")
|
||||||
|
}
|
||||||
|
return o.Inet4Address[0].Addr().Next().String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetMTU() int32 {
|
||||||
|
return int32(o.MTU)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetAutoRoute() bool {
|
||||||
|
return o.AutoRoute
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetStrictRoute() bool {
|
||||||
|
return o.StrictRoute
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetInet4RouteAddress() RoutePrefixIterator {
|
||||||
|
return mapRoutePrefix(o.Inet4RouteAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetInet6RouteAddress() RoutePrefixIterator {
|
||||||
|
return mapRoutePrefix(o.Inet6RouteAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetIncludePackage() StringIterator {
|
||||||
|
return newIterator(o.IncludePackage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetExcludePackage() StringIterator {
|
||||||
|
return newIterator(o.ExcludePackage)
|
||||||
|
}
|
||||||
|
|
||||||
|
type nativeTun struct {
|
||||||
|
tunFd int
|
||||||
|
tunFile *os.File
|
||||||
|
tunMTU uint32
|
||||||
|
closer io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *nativeTun) Read(p []byte) (n int, err error) {
|
||||||
|
return t.tunFile.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *nativeTun) Write(p []byte) (n int, err error) {
|
||||||
|
return t.tunFile.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *nativeTun) Close() error {
|
||||||
|
return t.closer.Close()
|
||||||
|
}
|
||||||
19
experimental/libbox/tun_gvisor.go
Normal file
19
experimental/libbox/tun_gvisor.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//go:build with_gvisor && linux
|
||||||
|
|
||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
|
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ tun.GVisorTun = (*nativeTun)(nil)
|
||||||
|
|
||||||
|
func (t *nativeTun) NewEndpoint() (stack.LinkEndpoint, error) {
|
||||||
|
return fdbased.New(&fdbased.Options{
|
||||||
|
FDs: []int{t.tunFd},
|
||||||
|
MTU: t.tunMTU,
|
||||||
|
})
|
||||||
|
}
|
||||||
21
go.mod
21
go.mod
@@ -14,22 +14,24 @@ require (
|
|||||||
github.com/go-chi/render v1.0.2
|
github.com/go-chi/render v1.0.2
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible
|
github.com/gofrs/uuid v4.4.0+incompatible
|
||||||
github.com/hashicorp/yamux v0.1.1
|
github.com/hashicorp/yamux v0.1.1
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8
|
github.com/insomniacslk/dhcp v0.0.0-20230220010740-598984875576
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||||
github.com/mholt/acmez v1.1.0
|
github.com/mholt/acmez v1.1.0
|
||||||
github.com/miekg/dns v1.1.50
|
github.com/miekg/dns v1.1.50
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0
|
github.com/oschwald/maxminddb-golang v1.10.0
|
||||||
github.com/pires/go-proxyproto v0.6.2
|
github.com/pires/go-proxyproto v0.6.2
|
||||||
github.com/refraction-networking/utls v1.2.2
|
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
|
||||||
|
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
|
||||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13
|
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b
|
||||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455
|
github.com/sagernet/sing-dns v0.1.4
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7
|
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9
|
||||||
|
github.com/sagernet/sing-shadowtls v0.0.0-20230221123345-78e50cd7b587
|
||||||
github.com/sagernet/sing-tun v0.1.1
|
github.com/sagernet/sing-tun v0.1.1
|
||||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb
|
github.com/sagernet/sing-vmess v0.1.2
|
||||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
|
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d
|
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d
|
||||||
|
github.com/sagernet/utls v0.0.0-20230220130002-c08891932056
|
||||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
|
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
|
||||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
|
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
|
||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.6.1
|
||||||
@@ -47,11 +49,9 @@ require (
|
|||||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
||||||
)
|
)
|
||||||
|
|
||||||
//replace github.com/sagernet/sing => ../sing
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ajg/form v1.5.1 // indirect
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c // indirect
|
github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||||
@@ -66,6 +66,7 @@ require (
|
|||||||
github.com/libdns/libdns v0.2.1 // indirect
|
github.com/libdns/libdns v0.2.1 // indirect
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/quic-go/qpack v0.4.0 // indirect
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||||
@@ -74,7 +75,7 @@ require (
|
|||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect
|
github.com/u-root/uio v0.0.0-20230215032506-9aa6f7e2d72c // indirect
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||||
go.uber.org/multierr v1.6.0 // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
golang.org/x/mod v0.6.0 // indirect
|
golang.org/x/mod v0.6.0 // indirect
|
||||||
|
|||||||
38
go.sum
38
go.sum
@@ -4,8 +4,8 @@ github.com/Dreamacro/clash v1.13.0 h1:gF0E0TluE1LCmuhhg0/bjqABYDmSnXkUjXjRhZxyrm
|
|||||||
github.com/Dreamacro/clash v1.13.0/go.mod h1:hf0RkWPsQ0e8oS8WVJBIRocY/1ILYzQQg9zeMwd8LsM=
|
github.com/Dreamacro/clash v1.13.0/go.mod h1:hf0RkWPsQ0e8oS8WVJBIRocY/1ILYzQQg9zeMwd8LsM=
|
||||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||||
github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE=
|
github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE=
|
||||||
github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
|
github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
|
||||||
@@ -59,8 +59,8 @@ github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Go
|
|||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 h1:Z72DOke2yOK0Ms4Z2LK1E1OrRJXOxSj5DllTz2FYTRg=
|
github.com/insomniacslk/dhcp v0.0.0-20230220010740-598984875576 h1:ehee0+xI4xtJTRJhN+PVA2GzCLB6KRgHRoZMg2lHwJU=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8/go.mod h1:m5WMe03WCvWcXjRnhvaAbAAXdCnu20J5P+mmH44ZzpE=
|
github.com/insomniacslk/dhcp v0.0.0-20230220010740-598984875576/go.mod h1:yGKD3yZIGAjEZXiIjVQ0SQ09Y/RzETOoqEOi6nXqX0Y=
|
||||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
@@ -99,6 +99,8 @@ github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7
|
|||||||
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
|
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
|
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
|
||||||
github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
@@ -112,33 +114,37 @@ github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4
|
|||||||
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||||
github.com/refraction-networking/utls v1.2.2 h1:uBE6V173CwG8MQrSBpNZHAix1fxOvuLKYyjFAu3uqo0=
|
|
||||||
github.com/refraction-networking/utls v1.2.2/go.mod h1:L1goe44KvhnTfctUffM2isnJpSjPlYShrhXDeZaoYKw=
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||||
|
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca h1:w56+kf8BeqLqllrRJ1tdwKc3sCdWOn/DuNHpY9fAiqs=
|
||||||
|
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
||||||
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13 h1:S/+YvJCEChwnckGhzqSrE/Q2m6aVWhkt1I4Pv2yCMVw=
|
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b h1:Ji2AfGlc4j9AitobOx4k3BCj7eS5nSxL1cgaL81zvlo=
|
||||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455 h1:VA/j2Jg+JURgKw2C1Dw2tpjfOZwbLXRy8PJRbJS/HEU=
|
github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0=
|
||||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455/go.mod h1:nonvn66ja+UNrQl3jzJy0EFRn15QUaCFAVXTXf6TgJ4=
|
github.com/sagernet/sing-dns v0.1.4/go.mod h1:1+6pCa48B1AI78lD+/i/dLgpw4MwfnsSpZo0Ds8wzzk=
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7 h1:Plup6oEiyLzY3HDqQ+QsUBzgBGdVmcsgf3t8h940z9U=
|
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9 h1:qS39eA4C7x+zhEkySbASrtmb6ebdy5v0y2M6mgkmSO0=
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7/go.mod h1:O5LtOs8Ivw686FqLpO0Zu+A0ROVE15VeqEK3yDRRAms=
|
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9/go.mod h1:f3mHTy5shnVM9l8UocMlJgC/1G/zdj5FuEuVXhDinGU=
|
||||||
|
github.com/sagernet/sing-shadowtls v0.0.0-20230221123345-78e50cd7b587 h1:OjIXlHT2bblZfp+ciupM4xY9+Ccpj9FsuHRtKRBv+Pg=
|
||||||
|
github.com/sagernet/sing-shadowtls v0.0.0-20230221123345-78e50cd7b587/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
|
||||||
github.com/sagernet/sing-tun v0.1.1 h1:2Hg3GAyJWzQ7Ua1j74dE+mI06vaqSBO9yD4tkTjggn4=
|
github.com/sagernet/sing-tun v0.1.1 h1:2Hg3GAyJWzQ7Ua1j74dE+mI06vaqSBO9yD4tkTjggn4=
|
||||||
github.com/sagernet/sing-tun v0.1.1/go.mod h1:WzW/SkT+Nh9uJn/FIYUE2YJHYuPwfbh8sATOzU9QDGw=
|
github.com/sagernet/sing-tun v0.1.1/go.mod h1:WzW/SkT+Nh9uJn/FIYUE2YJHYuPwfbh8sATOzU9QDGw=
|
||||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb h1:oyd3w17fXNmWVYFUe17YVHJW5CLW9X2mxJFDP/IWrAM=
|
github.com/sagernet/sing-vmess v0.1.2 h1:RbOZNAId2LrCai8epMoQXlf0XTrou0bfcw08hNBg6lM=
|
||||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb/go.mod h1:9KkmnQzTL4Gvv8U2TRAH2BOITCGsGPpHtUPP5sxn5sY=
|
github.com/sagernet/sing-vmess v0.1.2/go.mod h1:9NSj8mZTx1JIY/HF9LaYRppUsVkysIN5tEFpNZujXxY=
|
||||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
|
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
|
||||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
|
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d h1:trP/l6ZPWvQ/5Gv99Z7/t/v8iYy06akDMejxW1sznUk=
|
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d h1:trP/l6ZPWvQ/5Gv99Z7/t/v8iYy06akDMejxW1sznUk=
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d/go.mod h1:jk6Ii8Y3En+j2KQDLgdgQGwb3M6y7EL567jFnGYhN9g=
|
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d/go.mod h1:jk6Ii8Y3En+j2KQDLgdgQGwb3M6y7EL567jFnGYhN9g=
|
||||||
|
github.com/sagernet/utls v0.0.0-20230220130002-c08891932056 h1:gDXi/0uYe8dA48UyUI1LM2la5QYN0IvsDvR2H2+kFnA=
|
||||||
|
github.com/sagernet/utls v0.0.0-20230220130002-c08891932056/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
|
||||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
|
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
|
||||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
|
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
|
||||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
||||||
@@ -160,8 +166,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww=
|
github.com/u-root/uio v0.0.0-20230215032506-9aa6f7e2d72c h1:PHoGTnweZP+KIg/8Zc6+iOesrIF5yHkpb4GBDxHm7yE=
|
||||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E=
|
github.com/u-root/uio v0.0.0-20230215032506-9aa6f7e2d72c/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
|||||||
@@ -5,18 +5,19 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.Inbound) (adapter.Inbound, error) {
|
func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.Inbound, platformInterface platform.Interface) (adapter.Inbound, error) {
|
||||||
if options.Type == "" {
|
if options.Type == "" {
|
||||||
return nil, E.New("missing inbound type")
|
return nil, E.New("missing inbound type")
|
||||||
}
|
}
|
||||||
switch options.Type {
|
switch options.Type {
|
||||||
case C.TypeTun:
|
case C.TypeTun:
|
||||||
return NewTun(ctx, router, logger, options.Tag, options.TunOptions)
|
return NewTun(ctx, router, logger, options.Tag, options.TunOptions, platformInterface)
|
||||||
case C.TypeRedirect:
|
case C.TypeRedirect:
|
||||||
return NewRedirect(ctx, router, logger, options.Tag, options.RedirectOptions), nil
|
return NewRedirect(ctx, router, logger, options.Tag, options.RedirectOptions), nil
|
||||||
case C.TypeTProxy:
|
case C.TypeTProxy:
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
|||||||
authenticator: auth.NewAuthenticator(options.Users),
|
authenticator: auth.NewAuthenticator(options.Users),
|
||||||
}
|
}
|
||||||
if options.TLS != nil {
|
if options.TLS != nil {
|
||||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
tlsConfig, err := tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
if len(options.TLS.ALPN) == 0 {
|
if len(options.TLS.ALPN) == 0 {
|
||||||
options.TLS.ALPN = []string{hysteria.DefaultALPN}
|
options.TLS.ALPN = []string{hysteria.DefaultALPN}
|
||||||
}
|
}
|
||||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
tlsConfig, err := tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
|||||||
return nil, E.New("missing users")
|
return nil, E.New("missing users")
|
||||||
}
|
}
|
||||||
if options.TLS != nil {
|
if options.TLS != nil {
|
||||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
tlsConfig, err := tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
|||||||
case common.Contains(shadowaead.List, options.Method):
|
case common.Contains(shadowaead.List, options.Method):
|
||||||
inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, udpTimeout, inbound.upstreamContextHandler())
|
inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, udpTimeout, inbound.upstreamContextHandler())
|
||||||
case common.Contains(shadowaead_2022.List, options.Method):
|
case common.Contains(shadowaead_2022.List, options.Method):
|
||||||
inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler())
|
inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler(), router.TimeFunc())
|
||||||
default:
|
default:
|
||||||
err = E.New("unsupported method: ", options.Method)
|
err = E.New("unsupported method: ", options.Method)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
|
|||||||
options.Password,
|
options.Password,
|
||||||
udpTimeout,
|
udpTimeout,
|
||||||
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
|
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
|
||||||
|
router.TimeFunc(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -1,38 +1,22 @@
|
|||||||
package inbound
|
package inbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
|
||||||
"io"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/shadowtls"
|
"github.com/sagernet/sing-shadowtls"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/task"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ShadowTLS struct {
|
type ShadowTLS struct {
|
||||||
myInboundAdapter
|
myInboundAdapter
|
||||||
handshakeDialer N.Dialer
|
service *shadowtls.Service
|
||||||
handshakeAddr M.Socksaddr
|
|
||||||
version int
|
|
||||||
password string
|
|
||||||
fallbackAfter int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSInboundOptions) (*ShadowTLS, error) {
|
func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSInboundOptions) (*ShadowTLS, error) {
|
||||||
@@ -46,231 +30,41 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
|||||||
tag: tag,
|
tag: tag,
|
||||||
listenOptions: options.ListenOptions,
|
listenOptions: options.ListenOptions,
|
||||||
},
|
},
|
||||||
handshakeDialer: dialer.New(router, options.Handshake.DialerOptions),
|
|
||||||
handshakeAddr: options.Handshake.ServerOptions.Build(),
|
|
||||||
password: options.Password,
|
|
||||||
}
|
}
|
||||||
inbound.version = options.Version
|
|
||||||
switch options.Version {
|
var handshakeForServerName map[string]shadowtls.HandshakeConfig
|
||||||
case 0:
|
if options.Version > 1 {
|
||||||
fallthrough
|
handshakeForServerName = make(map[string]shadowtls.HandshakeConfig)
|
||||||
case 1:
|
for serverName, serverOptions := range options.HandshakeForServerName {
|
||||||
case 2:
|
handshakeForServerName[serverName] = shadowtls.HandshakeConfig{
|
||||||
if options.FallbackAfter == nil {
|
Server: serverOptions.ServerOptions.Build(),
|
||||||
inbound.fallbackAfter = 2
|
Dialer: dialer.New(router, serverOptions.DialerOptions),
|
||||||
} else {
|
}
|
||||||
inbound.fallbackAfter = *options.FallbackAfter
|
|
||||||
}
|
}
|
||||||
case 3:
|
|
||||||
default:
|
|
||||||
return nil, E.New("unknown shadowtls protocol version: ", options.Version)
|
|
||||||
}
|
}
|
||||||
|
service, err := shadowtls.NewService(shadowtls.ServiceConfig{
|
||||||
|
Version: options.Version,
|
||||||
|
Password: options.Password,
|
||||||
|
Users: common.Map(options.Users, func(it option.ShadowTLSUser) shadowtls.User {
|
||||||
|
return (shadowtls.User)(it)
|
||||||
|
}),
|
||||||
|
Handshake: shadowtls.HandshakeConfig{
|
||||||
|
Server: options.Handshake.ServerOptions.Build(),
|
||||||
|
Dialer: dialer.New(router, options.Handshake.DialerOptions),
|
||||||
|
},
|
||||||
|
HandshakeForServerName: handshakeForServerName,
|
||||||
|
StrictMode: options.StrictMode,
|
||||||
|
Handler: inbound.upstreamContextHandler(),
|
||||||
|
Logger: logger,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
inbound.service = service
|
||||||
inbound.connHandler = inbound
|
inbound.connHandler = inbound
|
||||||
return inbound, nil
|
return inbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
handshakeConn, err := s.handshakeDialer.DialContext(ctx, N.NetworkTCP, s.handshakeAddr)
|
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch s.version {
|
|
||||||
case 1:
|
|
||||||
var handshake task.Group
|
|
||||||
handshake.Append("client handshake", func(ctx context.Context) error {
|
|
||||||
return s.copyUntilHandshakeFinished(handshakeConn, conn)
|
|
||||||
})
|
|
||||||
handshake.Append("server handshake", func(ctx context.Context) error {
|
|
||||||
return s.copyUntilHandshakeFinished(conn, handshakeConn)
|
|
||||||
})
|
|
||||||
handshake.FastFail()
|
|
||||||
handshake.Cleanup(func() {
|
|
||||||
handshakeConn.Close()
|
|
||||||
})
|
|
||||||
err = handshake.Run(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return s.newConnection(ctx, conn, metadata)
|
|
||||||
case 2:
|
|
||||||
hashConn := shadowtls.NewHashWriteConn(conn, s.password)
|
|
||||||
go bufio.Copy(hashConn, handshakeConn)
|
|
||||||
var request *buf.Buffer
|
|
||||||
request, err = s.copyUntilHandshakeFinishedV2(ctx, handshakeConn, conn, hashConn, s.fallbackAfter)
|
|
||||||
if err == nil {
|
|
||||||
handshakeConn.Close()
|
|
||||||
return s.newConnection(ctx, bufio.NewCachedConn(shadowtls.NewConn(conn), request), metadata)
|
|
||||||
} else if err == os.ErrPermission {
|
|
||||||
s.logger.WarnContext(ctx, "fallback connection")
|
|
||||||
hashConn.Fallback()
|
|
||||||
return common.Error(bufio.Copy(handshakeConn, conn))
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
fallthrough
|
|
||||||
case 3:
|
|
||||||
var clientHelloFrame *buf.Buffer
|
|
||||||
clientHelloFrame, err = shadowtls.ExtractFrame(conn)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "read client handshake")
|
|
||||||
}
|
|
||||||
_, err = handshakeConn.Write(clientHelloFrame.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
clientHelloFrame.Release()
|
|
||||||
return E.Cause(err, "write client handshake")
|
|
||||||
}
|
|
||||||
err = shadowtls.VerifyClientHello(clientHelloFrame.Bytes(), s.password)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.WarnContext(ctx, E.Cause(err, "client hello verify failed"))
|
|
||||||
return bufio.CopyConn(ctx, conn, handshakeConn)
|
|
||||||
}
|
|
||||||
s.logger.TraceContext(ctx, "client hello verify success")
|
|
||||||
clientHelloFrame.Release()
|
|
||||||
|
|
||||||
var serverHelloFrame *buf.Buffer
|
|
||||||
serverHelloFrame, err = shadowtls.ExtractFrame(handshakeConn)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "read server handshake")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = conn.Write(serverHelloFrame.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
serverHelloFrame.Release()
|
|
||||||
return E.Cause(err, "write server handshake")
|
|
||||||
}
|
|
||||||
|
|
||||||
serverRandom := shadowtls.ExtractServerRandom(serverHelloFrame.Bytes())
|
|
||||||
|
|
||||||
if serverRandom == nil {
|
|
||||||
s.logger.WarnContext(ctx, "server random extract failed, will copy bidirectional")
|
|
||||||
return bufio.CopyConn(ctx, conn, handshakeConn)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !shadowtls.IsServerHelloSupportTLS13(serverHelloFrame.Bytes()) {
|
|
||||||
s.logger.WarnContext(ctx, "TLS 1.3 is not supported, will copy bidirectional")
|
|
||||||
return bufio.CopyConn(ctx, conn, handshakeConn)
|
|
||||||
}
|
|
||||||
|
|
||||||
serverHelloFrame.Release()
|
|
||||||
s.logger.TraceContext(ctx, "client authenticated. server random extracted: ", hex.EncodeToString(serverRandom))
|
|
||||||
|
|
||||||
hmacWrite := hmac.New(sha1.New, []byte(s.password))
|
|
||||||
hmacWrite.Write(serverRandom)
|
|
||||||
|
|
||||||
hmacAdd := hmac.New(sha1.New, []byte(s.password))
|
|
||||||
hmacAdd.Write(serverRandom)
|
|
||||||
hmacAdd.Write([]byte("S"))
|
|
||||||
|
|
||||||
hmacVerify := hmac.New(sha1.New, []byte(s.password))
|
|
||||||
hmacVerifyReset := func() {
|
|
||||||
hmacVerify.Reset()
|
|
||||||
hmacVerify.Write(serverRandom)
|
|
||||||
hmacVerify.Write([]byte("C"))
|
|
||||||
}
|
|
||||||
|
|
||||||
var clientFirstFrame *buf.Buffer
|
|
||||||
var group task.Group
|
|
||||||
var handshakeFinished bool
|
|
||||||
group.Append("client handshake relay", func(ctx context.Context) error {
|
|
||||||
clientFrame, cErr := shadowtls.CopyByFrameUntilHMACMatches(conn, handshakeConn, hmacVerify, hmacVerifyReset)
|
|
||||||
if cErr == nil {
|
|
||||||
clientFirstFrame = clientFrame
|
|
||||||
handshakeFinished = true
|
|
||||||
handshakeConn.Close()
|
|
||||||
}
|
|
||||||
return cErr
|
|
||||||
})
|
|
||||||
group.Append("server handshake relay", func(ctx context.Context) error {
|
|
||||||
cErr := shadowtls.CopyByFrameWithModification(handshakeConn, conn, s.password, serverRandom, hmacWrite)
|
|
||||||
if E.IsClosedOrCanceled(cErr) && handshakeFinished {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return cErr
|
|
||||||
})
|
|
||||||
group.Cleanup(func() {
|
|
||||||
handshakeConn.Close()
|
|
||||||
})
|
|
||||||
err = group.Run(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "handshake relay")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.logger.TraceContext(ctx, "handshake relay finished")
|
|
||||||
return s.newConnection(ctx, bufio.NewCachedConn(shadowtls.NewVerifiedConn(conn, hmacAdd, hmacVerify, nil), clientFirstFrame), metadata)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ShadowTLS) copyUntilHandshakeFinished(dst io.Writer, src io.Reader) error {
|
|
||||||
const handshake = 0x16
|
|
||||||
const changeCipherSpec = 0x14
|
|
||||||
var hasSeenChangeCipherSpec bool
|
|
||||||
var tlsHdr [5]byte
|
|
||||||
for {
|
|
||||||
_, err := io.ReadFull(src, tlsHdr[:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
length := binary.BigEndian.Uint16(tlsHdr[3:])
|
|
||||||
_, err = io.Copy(dst, io.MultiReader(bytes.NewReader(tlsHdr[:]), io.LimitReader(src, int64(length))))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if tlsHdr[0] != handshake {
|
|
||||||
if tlsHdr[0] != changeCipherSpec {
|
|
||||||
return E.New("unexpected tls frame type: ", tlsHdr[0])
|
|
||||||
}
|
|
||||||
if !hasSeenChangeCipherSpec {
|
|
||||||
hasSeenChangeCipherSpec = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasSeenChangeCipherSpec {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ShadowTLS) copyUntilHandshakeFinishedV2(ctx context.Context, dst net.Conn, src io.Reader, hash *shadowtls.HashWriteConn, fallbackAfter int) (*buf.Buffer, error) {
|
|
||||||
const applicationData = 0x17
|
|
||||||
var tlsHdr [5]byte
|
|
||||||
var applicationDataCount int
|
|
||||||
for {
|
|
||||||
_, err := io.ReadFull(src, tlsHdr[:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
length := binary.BigEndian.Uint16(tlsHdr[3:])
|
|
||||||
if tlsHdr[0] == applicationData {
|
|
||||||
data := buf.NewSize(int(length))
|
|
||||||
_, err = data.ReadFullFrom(src, int(length))
|
|
||||||
if err != nil {
|
|
||||||
data.Release()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if hash.HasContent() && length >= 8 {
|
|
||||||
checksum := hash.Sum()
|
|
||||||
if bytes.Equal(data.To(8), checksum) {
|
|
||||||
s.logger.TraceContext(ctx, "match current hashcode")
|
|
||||||
data.Advance(8)
|
|
||||||
return data, nil
|
|
||||||
} else if hash.LastSum() != nil && bytes.Equal(data.To(8), hash.LastSum()) {
|
|
||||||
s.logger.TraceContext(ctx, "match last hashcode")
|
|
||||||
data.Advance(8)
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err = io.Copy(dst, io.MultiReader(bytes.NewReader(tlsHdr[:]), data))
|
|
||||||
data.Release()
|
|
||||||
applicationDataCount++
|
|
||||||
} else {
|
|
||||||
_, err = io.Copy(dst, io.MultiReader(bytes.NewReader(tlsHdr[:]), io.LimitReader(src, int64(length))))
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if applicationDataCount > fallbackAfter {
|
|
||||||
return nil, os.ErrPermission
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,9 +100,10 @@ type tproxyPacketWriter struct {
|
|||||||
|
|
||||||
func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||||
defer buffer.Release()
|
defer buffer.Release()
|
||||||
if w.destination == destination && w.conn != nil {
|
conn := w.conn
|
||||||
_, err := w.conn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr()))
|
if w.destination == destination && conn != nil {
|
||||||
if err == nil {
|
_, err := conn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr()))
|
||||||
|
if err != nil {
|
||||||
w.conn = nil
|
w.conn = nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
|
|||||||
users: options.Users,
|
users: options.Users,
|
||||||
}
|
}
|
||||||
if options.TLS != nil {
|
if options.TLS != nil {
|
||||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
tlsConfig, err := tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/canceler"
|
"github.com/sagernet/sing-box/common/canceler"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
@@ -34,9 +35,10 @@ type Tun struct {
|
|||||||
stack string
|
stack string
|
||||||
tunIf tun.Tun
|
tunIf tun.Tun
|
||||||
tunStack tun.Stack
|
tunStack tun.Stack
|
||||||
|
platformInterface platform.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (*Tun, error) {
|
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*Tun, error) {
|
||||||
tunName := options.InterfaceName
|
tunName := options.InterfaceName
|
||||||
if tunName == "" {
|
if tunName == "" {
|
||||||
tunName = tun.CalculateInterfaceName("")
|
tunName = tun.CalculateInterfaceName("")
|
||||||
@@ -93,6 +95,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
|||||||
endpointIndependentNat: options.EndpointIndependentNat,
|
endpointIndependentNat: options.EndpointIndependentNat,
|
||||||
udpTimeout: udpTimeout,
|
udpTimeout: udpTimeout,
|
||||||
stack: options.Stack,
|
stack: options.Stack,
|
||||||
|
platformInterface: platformInterface,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,17 +140,25 @@ func (t *Tun) Tag() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tun) Start() error {
|
func (t *Tun) Start() error {
|
||||||
if C.IsAndroid {
|
if C.IsAndroid && t.platformInterface == nil {
|
||||||
t.tunOptions.BuildAndroidRules(t.router.PackageManager(), t)
|
t.tunOptions.BuildAndroidRules(t.router.PackageManager(), t)
|
||||||
}
|
}
|
||||||
tunIf, err := tun.Open(t.tunOptions)
|
var (
|
||||||
|
tunInterface tun.Tun
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if t.platformInterface != nil {
|
||||||
|
tunInterface, err = t.platformInterface.OpenTun(t.tunOptions)
|
||||||
|
} else {
|
||||||
|
tunInterface, err = tun.Open(t.tunOptions)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "configure tun interface")
|
return E.Cause(err, "configure tun interface")
|
||||||
}
|
}
|
||||||
t.tunIf = tunIf
|
t.tunIf = tunInterface
|
||||||
t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
|
t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
|
||||||
Context: t.ctx,
|
Context: t.ctx,
|
||||||
Tun: tunIf,
|
Tun: tunInterface,
|
||||||
MTU: t.tunOptions.MTU,
|
MTU: t.tunOptions.MTU,
|
||||||
Name: t.tunOptions.Name,
|
Name: t.tunOptions.Name,
|
||||||
Inet4Address: t.tunOptions.Inet4Address,
|
Inet4Address: t.tunOptions.Inet4Address,
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
|||||||
users: options.Users,
|
users: options.Users,
|
||||||
}
|
}
|
||||||
var serviceOptions []vmess.ServiceOption
|
var serviceOptions []vmess.ServiceOption
|
||||||
|
if timeFunc := router.TimeFunc(); timeFunc != nil {
|
||||||
|
serviceOptions = append(serviceOptions, vmess.ServiceWithTimeFunc(timeFunc))
|
||||||
|
}
|
||||||
if options.Transport != nil && options.Transport.Type != "" {
|
if options.Transport != nil && options.Transport.Type != "" {
|
||||||
serviceOptions = append(serviceOptions, vmess.ServiceWithDisableHeaderProtection())
|
serviceOptions = append(serviceOptions, vmess.ServiceWithDisableHeaderProtection())
|
||||||
}
|
}
|
||||||
@@ -66,7 +69,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if options.TLS != nil {
|
if options.TLS != nil {
|
||||||
inbound.tlsConfig, err = tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
inbound.tlsConfig, err = tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,16 +12,22 @@ import (
|
|||||||
var _ Factory = (*simpleFactory)(nil)
|
var _ Factory = (*simpleFactory)(nil)
|
||||||
|
|
||||||
type simpleFactory struct {
|
type simpleFactory struct {
|
||||||
formatter Formatter
|
formatter Formatter
|
||||||
writer io.Writer
|
platformFormatter Formatter
|
||||||
level Level
|
writer io.Writer
|
||||||
|
platformWriter io.Writer
|
||||||
|
level Level
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactory(formatter Formatter, writer io.Writer) Factory {
|
func NewFactory(formatter Formatter, writer io.Writer, platformWriter io.Writer) Factory {
|
||||||
return &simpleFactory{
|
return &simpleFactory{
|
||||||
formatter: formatter,
|
formatter: formatter,
|
||||||
writer: writer,
|
platformFormatter: Formatter{
|
||||||
level: LevelTrace,
|
BaseTime: formatter.BaseTime,
|
||||||
|
},
|
||||||
|
writer: writer,
|
||||||
|
platformWriter: platformWriter,
|
||||||
|
level: LevelTrace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +59,8 @@ func (l *simpleLogger) Log(ctx context.Context, level Level, args []any) {
|
|||||||
if level > l.level {
|
if level > l.level {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
message := l.formatter.Format(ctx, level, l.tag, F.ToString(args...), time.Now())
|
nowTime := time.Now()
|
||||||
|
message := l.formatter.Format(ctx, level, l.tag, F.ToString(args...), nowTime)
|
||||||
if level == LevelPanic {
|
if level == LevelPanic {
|
||||||
panic(message)
|
panic(message)
|
||||||
}
|
}
|
||||||
@@ -61,6 +68,9 @@ func (l *simpleLogger) Log(ctx context.Context, level Level, args []any) {
|
|||||||
if level == LevelFatal {
|
if level == LevelFatal {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
if l.platformWriter != nil {
|
||||||
|
l.platformWriter.Write([]byte(l.platformFormatter.Format(ctx, level, l.tag, F.ToString(args...), nowTime)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *simpleLogger) Trace(args ...any) {
|
func (l *simpleLogger) Trace(args ...any) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
var std ContextLogger
|
var std ContextLogger
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
std = NewFactory(Formatter{BaseTime: time.Now()}, os.Stderr).Logger()
|
std = NewFactory(Formatter{BaseTime: time.Now()}, os.Stderr, nil).Logger()
|
||||||
}
|
}
|
||||||
|
|
||||||
func StdLogger() ContextLogger {
|
func StdLogger() ContextLogger {
|
||||||
|
|||||||
@@ -14,19 +14,25 @@ import (
|
|||||||
var _ Factory = (*observableFactory)(nil)
|
var _ Factory = (*observableFactory)(nil)
|
||||||
|
|
||||||
type observableFactory struct {
|
type observableFactory struct {
|
||||||
formatter Formatter
|
formatter Formatter
|
||||||
writer io.Writer
|
platformFormatter Formatter
|
||||||
level Level
|
writer io.Writer
|
||||||
subscriber *observable.Subscriber[Entry]
|
platformWriter io.Writer
|
||||||
observer *observable.Observer[Entry]
|
level Level
|
||||||
|
subscriber *observable.Subscriber[Entry]
|
||||||
|
observer *observable.Observer[Entry]
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewObservableFactory(formatter Formatter, writer io.Writer) ObservableFactory {
|
func NewObservableFactory(formatter Formatter, writer io.Writer, platformWriter io.Writer) ObservableFactory {
|
||||||
factory := &observableFactory{
|
factory := &observableFactory{
|
||||||
formatter: formatter,
|
formatter: formatter,
|
||||||
writer: writer,
|
platformFormatter: Formatter{
|
||||||
level: LevelTrace,
|
BaseTime: formatter.BaseTime,
|
||||||
subscriber: observable.NewSubscriber[Entry](128),
|
},
|
||||||
|
writer: writer,
|
||||||
|
platformWriter: platformWriter,
|
||||||
|
level: LevelTrace,
|
||||||
|
subscriber: observable.NewSubscriber[Entry](128),
|
||||||
}
|
}
|
||||||
factory.observer = observable.NewObserver[Entry](factory.subscriber, 64)
|
factory.observer = observable.NewObserver[Entry](factory.subscriber, 64)
|
||||||
return factory
|
return factory
|
||||||
@@ -74,7 +80,8 @@ func (l *observableLogger) Log(ctx context.Context, level Level, args []any) {
|
|||||||
if level > l.level {
|
if level > l.level {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
message, messageSimple := l.formatter.FormatWithSimple(ctx, level, l.tag, F.ToString(args...), time.Now())
|
nowTime := time.Now()
|
||||||
|
message, messageSimple := l.formatter.FormatWithSimple(ctx, level, l.tag, F.ToString(args...), nowTime)
|
||||||
if level == LevelPanic {
|
if level == LevelPanic {
|
||||||
panic(message)
|
panic(message)
|
||||||
}
|
}
|
||||||
@@ -83,6 +90,9 @@ func (l *observableLogger) Log(ctx context.Context, level Level, args []any) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
l.subscriber.Emit(Entry{level, messageSimple})
|
l.subscriber.Emit(Entry{level, messageSimple})
|
||||||
|
if l.platformWriter != nil {
|
||||||
|
l.platformWriter.Write([]byte(l.formatter.Format(ctx, level, l.tag, F.ToString(args...), nowTime)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *observableLogger) Trace(args ...any) {
|
func (l *observableLogger) Trace(args ...any) {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ func ContextWithOverrideLevel(ctx context.Context, level Level) context.Context
|
|||||||
|
|
||||||
func OverrideLevelFromContext(origin Level, ctx context.Context) Level {
|
func OverrideLevelFromContext(origin Level, ctx context.Context) Level {
|
||||||
level, loaded := ctx.Value((*overrideLevelKey)(nil)).(Level)
|
level, loaded := ctx.Value((*overrideLevelKey)(nil)).(Level)
|
||||||
if !loaded || origin < level {
|
if !loaded || origin > level {
|
||||||
return origin
|
return origin
|
||||||
}
|
}
|
||||||
return level
|
return level
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ nav:
|
|||||||
- configuration/dns/index.md
|
- configuration/dns/index.md
|
||||||
- DNS Server: configuration/dns/server.md
|
- DNS Server: configuration/dns/server.md
|
||||||
- DNS Rule: configuration/dns/rule.md
|
- DNS Rule: configuration/dns/rule.md
|
||||||
|
- NTP:
|
||||||
|
- configuration/ntp/index.md
|
||||||
- Route:
|
- Route:
|
||||||
- configuration/route/index.md
|
- configuration/route/index.md
|
||||||
- GeoIP: configuration/route/geoip.md
|
- GeoIP: configuration/route/geoip.md
|
||||||
@@ -149,21 +151,27 @@ plugins:
|
|||||||
Features: 特性
|
Features: 特性
|
||||||
Support: 支持
|
Support: 支持
|
||||||
Change Log: 更新日志
|
Change Log: 更新日志
|
||||||
|
|
||||||
Configuration: 配置
|
Configuration: 配置
|
||||||
Log: 日志
|
Log: 日志
|
||||||
DNS Server: DNS 服务器
|
DNS Server: DNS 服务器
|
||||||
DNS Rule: DNS 规则
|
DNS Rule: DNS 规则
|
||||||
|
|
||||||
Route: 路由
|
Route: 路由
|
||||||
Route Rule: 路由规则
|
Route Rule: 路由规则
|
||||||
Protocol Sniff: 协议探测
|
Protocol Sniff: 协议探测
|
||||||
|
|
||||||
Experimental: 实验性
|
Experimental: 实验性
|
||||||
|
|
||||||
Shared: 通用
|
Shared: 通用
|
||||||
Listen Fields: 监听字段
|
Listen Fields: 监听字段
|
||||||
Dial Fields: 拨号字段
|
Dial Fields: 拨号字段
|
||||||
Multiplex: 多路复用
|
Multiplex: 多路复用
|
||||||
V2Ray Transport: V2Ray 传输层
|
V2Ray Transport: V2Ray 传输层
|
||||||
|
|
||||||
Inbound: 入站
|
Inbound: 入站
|
||||||
Outbound: 出站
|
Outbound: 出站
|
||||||
|
|
||||||
FAQ: 常见问题
|
FAQ: 常见问题
|
||||||
Known Issues: 已知问题
|
Known Issues: 已知问题
|
||||||
Examples: 示例
|
Examples: 示例
|
||||||
|
|||||||
99
ntp/service.go
Normal file
99
ntp/service.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package ntp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
"github.com/sagernet/sing/common/ntp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const timeLayout = "2006-01-02 15:04:05 -0700"
|
||||||
|
|
||||||
|
var _ adapter.TimeService = (*Service)(nil)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
server M.Socksaddr
|
||||||
|
dialer N.Dialer
|
||||||
|
logger logger.Logger
|
||||||
|
|
||||||
|
ticker *time.Ticker
|
||||||
|
clockOffset time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(ctx context.Context, router adapter.Router, logger logger.Logger, options option.NTPOptions) *Service {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
server := options.ServerOptions.Build()
|
||||||
|
if server.Port == 0 {
|
||||||
|
server.Port = 123
|
||||||
|
}
|
||||||
|
var interval time.Duration
|
||||||
|
if options.Interval > 0 {
|
||||||
|
interval = time.Duration(options.Interval)
|
||||||
|
} else {
|
||||||
|
interval = 30 * time.Minute
|
||||||
|
}
|
||||||
|
return &Service{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
server: server,
|
||||||
|
dialer: dialer.New(router, options.DialerOptions),
|
||||||
|
logger: logger,
|
||||||
|
ticker: time.NewTicker(interval),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Start() error {
|
||||||
|
err := s.update()
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "initialize time")
|
||||||
|
}
|
||||||
|
s.logger.Info("updated time: ", s.TimeFunc()().Local().Format(timeLayout))
|
||||||
|
go s.loopUpdate()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Close() error {
|
||||||
|
s.ticker.Stop()
|
||||||
|
s.cancel()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) TimeFunc() func() time.Time {
|
||||||
|
return func() time.Time {
|
||||||
|
return time.Now().Add(s.clockOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) loopUpdate() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return
|
||||||
|
case <-s.ticker.C:
|
||||||
|
}
|
||||||
|
err := s.update()
|
||||||
|
if err == nil {
|
||||||
|
s.logger.Debug("updated time: ", s.TimeFunc()().Local().Format(timeLayout))
|
||||||
|
} else {
|
||||||
|
s.logger.Warn("update time: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) update() error {
|
||||||
|
response, err := ntp.Exchange(s.ctx, s.dialer, s.server)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.clockOffset = response.ClockOffset
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -5,16 +5,19 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/json"
|
"github.com/sagernet/sing-box/common/json"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
type _Options struct {
|
type _Options struct {
|
||||||
Log *LogOptions `json:"log,omitempty"`
|
Log *LogOptions `json:"log,omitempty"`
|
||||||
DNS *DNSOptions `json:"dns,omitempty"`
|
DNS *DNSOptions `json:"dns,omitempty"`
|
||||||
Inbounds []Inbound `json:"inbounds,omitempty"`
|
NTP *NTPOptions `json:"ntp,omitempty"`
|
||||||
Outbounds []Outbound `json:"outbounds,omitempty"`
|
Inbounds []Inbound `json:"inbounds,omitempty"`
|
||||||
Route *RouteOptions `json:"route,omitempty"`
|
Outbounds []Outbound `json:"outbounds,omitempty"`
|
||||||
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
|
Route *RouteOptions `json:"route,omitempty"`
|
||||||
|
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
|
||||||
|
PlatformInterface platform.Interface `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options _Options
|
type Options _Options
|
||||||
|
|||||||
8
option/ntp.go
Normal file
8
option/ntp.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package option
|
||||||
|
|
||||||
|
type NTPOptions struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Interval Duration `json:"interval,omitempty"`
|
||||||
|
ServerOptions
|
||||||
|
DialerOptions
|
||||||
|
}
|
||||||
@@ -2,10 +2,17 @@ package option
|
|||||||
|
|
||||||
type ShadowTLSInboundOptions struct {
|
type ShadowTLSInboundOptions struct {
|
||||||
ListenOptions
|
ListenOptions
|
||||||
Version int `json:"version,omitempty"`
|
Version int `json:"version,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
FallbackAfter *int `json:"fallback_after,omitempty"`
|
Users []ShadowTLSUser `json:"users,omitempty"`
|
||||||
Handshake ShadowTLSHandshakeOptions `json:"handshake"`
|
Handshake ShadowTLSHandshakeOptions `json:"handshake,omitempty"`
|
||||||
|
HandshakeForServerName map[string]ShadowTLSHandshakeOptions `json:"handshake_for_server_name,omitempty"`
|
||||||
|
StrictMode bool `json:"strict_mode,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShadowTLSUser struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShadowTLSHandshakeOptions struct {
|
type ShadowTLSHandshakeOptions struct {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ type Shadowsocks struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) {
|
func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) {
|
||||||
method, err := shadowimpl.FetchMethod(options.Method, options.Password)
|
method, err := shadowimpl.FetchMethod(options.Method, options.Password, router.TimeFunc())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package outbound
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha1"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -13,9 +11,8 @@ import (
|
|||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/shadowtls"
|
"github.com/sagernet/sing-shadowtls"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
@@ -24,11 +21,7 @@ var _ adapter.Outbound = (*ShadowTLS)(nil)
|
|||||||
|
|
||||||
type ShadowTLS struct {
|
type ShadowTLS struct {
|
||||||
myOutboundAdapter
|
myOutboundAdapter
|
||||||
dialer N.Dialer
|
client *shadowtls.Client
|
||||||
serverAddr M.Socksaddr
|
|
||||||
tlsConfig tls.Config
|
|
||||||
version int
|
|
||||||
password string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSOutboundOptions) (*ShadowTLS, error) {
|
func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSOutboundOptions) (*ShadowTLS, error) {
|
||||||
@@ -40,86 +33,61 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
},
|
},
|
||||||
dialer: dialer.New(router, options.DialerOptions),
|
|
||||||
serverAddr: options.ServerOptions.Build(),
|
|
||||||
password: options.Password,
|
|
||||||
}
|
}
|
||||||
if options.TLS == nil || !options.TLS.Enabled {
|
if options.TLS == nil || !options.TLS.Enabled {
|
||||||
return nil, C.ErrTLSRequired
|
return nil, C.ErrTLSRequired
|
||||||
}
|
}
|
||||||
outbound.version = options.Version
|
if options.Version == 1 {
|
||||||
switch options.Version {
|
|
||||||
case 0:
|
|
||||||
fallthrough
|
|
||||||
case 1:
|
|
||||||
options.TLS.MinVersion = "1.2"
|
options.TLS.MinVersion = "1.2"
|
||||||
options.TLS.MaxVersion = "1.2"
|
options.TLS.MaxVersion = "1.2"
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
options.TLS.MinVersion = "1.3"
|
|
||||||
options.TLS.MaxVersion = "1.3"
|
|
||||||
default:
|
|
||||||
return nil, E.New("unknown shadowtls protocol version: ", options.Version)
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
if options.Version != 3 {
|
|
||||||
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
|
|
||||||
} else {
|
|
||||||
outbound.tlsConfig, err = shadowtls.NewClientTLSConfig(options.Server, common.PtrValueOrDefault(options.TLS), options.Password)
|
|
||||||
}
|
}
|
||||||
|
tlsConfig, err := tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tlsHandshakeFunc shadowtls.TLSHandshakeFunc
|
||||||
|
switch options.Version {
|
||||||
|
case 1, 2:
|
||||||
|
tlsHandshakeFunc = func(ctx context.Context, conn net.Conn, _ shadowtls.TLSSessionIDGeneratorFunc) error {
|
||||||
|
return common.Error(tls.ClientHandshake(ctx, conn, tlsConfig))
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
if idConfig, loaded := tlsConfig.(tls.ConfigWithSessionIDGenerator); loaded {
|
||||||
|
tlsHandshakeFunc = func(ctx context.Context, conn net.Conn, sessionIDGenerator shadowtls.TLSSessionIDGeneratorFunc) error {
|
||||||
|
idConfig.SetSessionIDGenerator(sessionIDGenerator)
|
||||||
|
return common.Error(tls.ClientHandshake(ctx, conn, tlsConfig))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stdTLSConfig, err := tlsConfig.Config()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tlsHandshakeFunc = shadowtls.DefaultTLSHandshakeFunc(options.Password, stdTLSConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client, err := shadowtls.NewClient(shadowtls.ClientConfig{
|
||||||
|
Version: options.Version,
|
||||||
|
Password: options.Password,
|
||||||
|
Server: options.ServerOptions.Build(),
|
||||||
|
Dialer: dialer.New(router, options.DialerOptions),
|
||||||
|
TLSHandshake: tlsHandshakeFunc,
|
||||||
|
Logger: logger,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
outbound.client = client
|
||||||
return outbound, nil
|
return outbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ShadowTLS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (s *ShadowTLS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
switch N.NetworkName(network) {
|
switch N.NetworkName(network) {
|
||||||
case N.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
|
return s.client.DialContext(ctx)
|
||||||
default:
|
default:
|
||||||
return nil, os.ErrInvalid
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
conn, err := s.dialer.DialContext(ctx, N.NetworkTCP, s.serverAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch s.version {
|
|
||||||
default:
|
|
||||||
fallthrough
|
|
||||||
case 1:
|
|
||||||
_, err = tls.ClientHandshake(ctx, conn, s.tlsConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
case 2:
|
|
||||||
hashConn := shadowtls.NewHashReadConn(conn, s.password)
|
|
||||||
_, err = tls.ClientHandshake(ctx, hashConn, s.tlsConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return shadowtls.NewClientConn(hashConn), nil
|
|
||||||
case 3:
|
|
||||||
streamWrapper := shadowtls.NewStreamWrapper(conn, s.password)
|
|
||||||
_, err = tls.ClientHandshake(ctx, streamWrapper, s.tlsConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
authorized, serverRandom, readHMAC := streamWrapper.Authorized()
|
|
||||||
if !authorized {
|
|
||||||
return nil, E.New("traffic hijacked or TLS1.3 is not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
hmacAdd := hmac.New(sha1.New, []byte(s.password))
|
|
||||||
hmacAdd.Write(serverRandom)
|
|
||||||
hmacAdd.Write([]byte("C"))
|
|
||||||
|
|
||||||
hmacVerify := hmac.New(sha1.New, []byte(s.password))
|
|
||||||
hmacVerify.Write(serverRandom)
|
|
||||||
hmacVerify.Write([]byte("S"))
|
|
||||||
|
|
||||||
return shadowtls.NewVerifiedConn(conn, hmacAdd, hmacVerify, readHMAC), nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ShadowTLS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (s *ShadowTLS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
|||||||
return nil, E.New("unknown packet encoding: ", options.PacketEncoding)
|
return nil, E.New("unknown packet encoding: ", options.PacketEncoding)
|
||||||
}
|
}
|
||||||
var clientOptions []vmess.ClientOption
|
var clientOptions []vmess.ClientOption
|
||||||
|
if timeFunc := router.TimeFunc(); timeFunc != nil {
|
||||||
|
clientOptions = append(clientOptions, vmess.ClientWithTimeFunc(timeFunc))
|
||||||
|
}
|
||||||
if options.GlobalPadding {
|
if options.GlobalPadding {
|
||||||
clientOptions = append(clientOptions, vmess.ClientWithGlobalPadding())
|
clientOptions = append(clientOptions, vmess.ClientWithGlobalPadding())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
set -e -o pipefail
|
set -e -o pipefail
|
||||||
curl -Lo go.tar.gz https://go.dev/dl/go1.20.linux-amd64.tar.gz
|
curl -Lo go.tar.gz https://go.dev/dl/go1.20.1.linux-amd64.tar.gz
|
||||||
sudo rm -rf /usr/local/go
|
sudo rm -rf /usr/local/go
|
||||||
sudo tar -C /usr/local -xzf go.tar.gz
|
sudo tar -C /usr/local -xzf go.tar.gz
|
||||||
rm go.tar.gz
|
rm go.tar.gz
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ import (
|
|||||||
"github.com/sagernet/sing-box/common/sniff"
|
"github.com/sagernet/sing-box/common/sniff"
|
||||||
"github.com/sagernet/sing-box/common/warning"
|
"github.com/sagernet/sing-box/common/warning"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing-box/ntp"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
@@ -95,11 +97,21 @@ type Router struct {
|
|||||||
interfaceMonitor tun.DefaultInterfaceMonitor
|
interfaceMonitor tun.DefaultInterfaceMonitor
|
||||||
packageManager tun.PackageManager
|
packageManager tun.PackageManager
|
||||||
processSearcher process.Searcher
|
processSearcher process.Searcher
|
||||||
|
timeService adapter.TimeService
|
||||||
clashServer adapter.ClashServer
|
clashServer adapter.ClashServer
|
||||||
v2rayServer adapter.V2RayServer
|
v2rayServer adapter.V2RayServer
|
||||||
|
platformInterface platform.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouter(ctx context.Context, logFactory log.Factory, options option.RouteOptions, dnsOptions option.DNSOptions, inbounds []option.Inbound) (*Router, error) {
|
func NewRouter(
|
||||||
|
ctx context.Context,
|
||||||
|
logFactory log.Factory,
|
||||||
|
options option.RouteOptions,
|
||||||
|
dnsOptions option.DNSOptions,
|
||||||
|
ntpOptions option.NTPOptions,
|
||||||
|
inbounds []option.Inbound,
|
||||||
|
platformInterface platform.Interface,
|
||||||
|
) (*Router, error) {
|
||||||
if options.DefaultInterface != "" {
|
if options.DefaultInterface != "" {
|
||||||
warnDefaultInterfaceOnUnsupportedPlatform.Check()
|
warnDefaultInterfaceOnUnsupportedPlatform.Check()
|
||||||
}
|
}
|
||||||
@@ -127,6 +139,7 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
|
|||||||
autoDetectInterface: options.AutoDetectInterface,
|
autoDetectInterface: options.AutoDetectInterface,
|
||||||
defaultInterface: options.DefaultInterface,
|
defaultInterface: options.DefaultInterface,
|
||||||
defaultMark: options.DefaultMark,
|
defaultMark: options.DefaultMark,
|
||||||
|
platformInterface: platformInterface,
|
||||||
}
|
}
|
||||||
router.dnsClient = dns.NewClient(dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire, router.dnsLogger)
|
router.dnsClient = dns.NewClient(dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire, router.dnsLogger)
|
||||||
for i, ruleOptions := range options.Rules {
|
for i, ruleOptions := range options.Rules {
|
||||||
@@ -248,9 +261,9 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
|
|||||||
router.transportMap = transportMap
|
router.transportMap = transportMap
|
||||||
router.transportDomainStrategy = transportDomainStrategy
|
router.transportDomainStrategy = transportDomainStrategy
|
||||||
|
|
||||||
needInterfaceMonitor := options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
|
needInterfaceMonitor := platformInterface == nil && (options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||||
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || inbound.TunOptions.AutoRoute
|
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || inbound.TunOptions.AutoRoute
|
||||||
})
|
}))
|
||||||
|
|
||||||
if needInterfaceMonitor {
|
if needInterfaceMonitor {
|
||||||
networkMonitor, err := tun.NewNetworkUpdateMonitor(router)
|
networkMonitor, err := tun.NewNetworkUpdateMonitor(router)
|
||||||
@@ -272,7 +285,7 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
|
|||||||
}
|
}
|
||||||
|
|
||||||
needFindProcess := hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess
|
needFindProcess := hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess
|
||||||
needPackageManager := C.IsAndroid && (needFindProcess || common.Any(inbounds, func(inbound option.Inbound) bool {
|
needPackageManager := C.IsAndroid && platformInterface == nil && (needFindProcess || common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||||
return len(inbound.TunOptions.IncludePackage) > 0 || len(inbound.TunOptions.ExcludePackage) > 0
|
return len(inbound.TunOptions.IncludePackage) > 0 || len(inbound.TunOptions.ExcludePackage) > 0
|
||||||
}))
|
}))
|
||||||
if needPackageManager {
|
if needPackageManager {
|
||||||
@@ -283,18 +296,25 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
|
|||||||
router.packageManager = packageManager
|
router.packageManager = packageManager
|
||||||
}
|
}
|
||||||
if needFindProcess {
|
if needFindProcess {
|
||||||
searcher, err := process.NewSearcher(process.Config{
|
if platformInterface != nil {
|
||||||
Logger: logFactory.NewLogger("router/process"),
|
router.processSearcher = platformInterface
|
||||||
PackageManager: router.packageManager,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
if err != os.ErrInvalid {
|
|
||||||
router.logger.Warn(E.Cause(err, "create process searcher"))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
router.processSearcher = searcher
|
searcher, err := process.NewSearcher(process.Config{
|
||||||
|
Logger: logFactory.NewLogger("router/process"),
|
||||||
|
PackageManager: router.packageManager,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if err != os.ErrInvalid {
|
||||||
|
router.logger.Warn(E.Cause(err, "create process searcher"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
router.processSearcher = searcher
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ntpOptions.Enabled {
|
||||||
|
router.timeService = ntp.NewService(ctx, router, logFactory.NewLogger("ntp"), ntpOptions)
|
||||||
|
}
|
||||||
return router, nil
|
return router, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,6 +473,12 @@ func (r *Router) Start() error {
|
|||||||
return E.Cause(err, "initialize DNS server[", i, "]")
|
return E.Cause(err, "initialize DNS server[", i, "]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if r.timeService != nil {
|
||||||
|
err := r.timeService.Start()
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "initialize time service")
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,6 +506,7 @@ func (r *Router) Close() error {
|
|||||||
r.interfaceMonitor,
|
r.interfaceMonitor,
|
||||||
r.networkMonitor,
|
r.networkMonitor,
|
||||||
r.packageManager,
|
r.packageManager,
|
||||||
|
r.timeService,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -737,6 +764,21 @@ func (r *Router) AutoDetectInterface() bool {
|
|||||||
return r.autoDetectInterface
|
return r.autoDetectInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Router) AutoDetectInterfaceFunc() control.Func {
|
||||||
|
if r.platformInterface != nil {
|
||||||
|
return r.platformInterface.AutoDetectInterfaceControl()
|
||||||
|
} else {
|
||||||
|
return control.BindToInterfaceFunc(r.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
|
||||||
|
remoteAddr := M.ParseSocksaddr(address).Addr
|
||||||
|
if C.IsLinux {
|
||||||
|
return r.InterfaceMonitor().DefaultInterfaceName(remoteAddr), -1
|
||||||
|
} else {
|
||||||
|
return "", r.InterfaceMonitor().DefaultInterfaceIndex(remoteAddr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Router) DefaultInterface() string {
|
func (r *Router) DefaultInterface() string {
|
||||||
return r.defaultInterface
|
return r.defaultInterface
|
||||||
}
|
}
|
||||||
@@ -761,6 +803,13 @@ func (r *Router) PackageManager() tun.PackageManager {
|
|||||||
return r.packageManager
|
return r.packageManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Router) TimeFunc() func() time.Time {
|
||||||
|
if r.timeService == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return r.timeService.TimeFunc()
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Router) ClashServer() adapter.ClashServer {
|
func (r *Router) ClashServer() adapter.ClashServer {
|
||||||
return r.clashServer
|
return r.clashServer
|
||||||
}
|
}
|
||||||
@@ -849,6 +898,8 @@ func (r *Router) prepareGeoIPDatabase() error {
|
|||||||
geoPath = "geoip.db"
|
geoPath = "geoip.db"
|
||||||
if foundPath, loaded := C.FindPath(geoPath); loaded {
|
if foundPath, loaded := C.FindPath(geoPath); loaded {
|
||||||
geoPath = foundPath
|
geoPath = foundPath
|
||||||
|
} else {
|
||||||
|
geoPath = C.BasePath(geoPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !rw.FileExists(geoPath) {
|
if !rw.FileExists(geoPath) {
|
||||||
@@ -861,7 +912,7 @@ func (r *Router) prepareGeoIPDatabase() error {
|
|||||||
}
|
}
|
||||||
r.logger.Error("download geoip database: ", err)
|
r.logger.Error("download geoip database: ", err)
|
||||||
os.Remove(geoPath)
|
os.Remove(geoPath)
|
||||||
time.Sleep(10 * time.Second)
|
// time.Sleep(10 * time.Second)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -884,6 +935,8 @@ func (r *Router) prepareGeositeDatabase() error {
|
|||||||
geoPath = "geosite.db"
|
geoPath = "geosite.db"
|
||||||
if foundPath, loaded := C.FindPath(geoPath); loaded {
|
if foundPath, loaded := C.FindPath(geoPath); loaded {
|
||||||
geoPath = foundPath
|
geoPath = foundPath
|
||||||
|
} else {
|
||||||
|
geoPath = C.BasePath(geoPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !rw.FileExists(geoPath) {
|
if !rw.FileExists(geoPath) {
|
||||||
@@ -896,7 +949,7 @@ func (r *Router) prepareGeositeDatabase() error {
|
|||||||
}
|
}
|
||||||
r.logger.Error("download geosite database: ", err)
|
r.logger.Error("download geosite database: ", err)
|
||||||
os.Remove(geoPath)
|
os.Remove(geoPath)
|
||||||
time.Sleep(10 * time.Second)
|
// time.Sleep(10 * time.Second)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -950,6 +1003,7 @@ func (r *Router) downloadGeoIPDatabase(savePath string) error {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
defer httpClient.CloseIdleConnections()
|
||||||
response, err := httpClient.Get(downloadURL)
|
response, err := httpClient.Get(downloadURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -997,6 +1051,7 @@ func (r *Router) downloadGeositeDatabase(savePath string) error {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
defer httpClient.CloseIdleConnections()
|
||||||
response, err := httpClient.Get(downloadURL)
|
response, err := httpClient.Get(downloadURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ func init() {
|
|||||||
|
|
||||||
list, err := dockerClient.ImageList(context.Background(), types.ImageListOptions{All: true})
|
list, err := dockerClient.ImageList(context.Background(), types.ImageListOptions{All: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Warn(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
imageExist := func(image string) bool {
|
imageExist := func(image string) bool {
|
||||||
|
|||||||
18
test/go.mod
18
test/go.mod
@@ -10,8 +10,8 @@ require (
|
|||||||
github.com/docker/docker v20.10.18+incompatible
|
github.com/docker/docker v20.10.18+incompatible
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible
|
github.com/gofrs/uuid v4.4.0+incompatible
|
||||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13
|
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7
|
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9
|
||||||
github.com/spyzhov/ajson v0.7.1
|
github.com/spyzhov/ajson v0.7.1
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
go.uber.org/goleak v1.2.0
|
go.uber.org/goleak v1.2.0
|
||||||
@@ -23,7 +23,7 @@ require (
|
|||||||
github.com/Dreamacro/clash v1.13.0 // indirect
|
github.com/Dreamacro/clash v1.13.0 // indirect
|
||||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||||
github.com/ajg/form v1.5.1 // indirect
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
github.com/caddyserver/certmagic v0.17.2 // indirect
|
github.com/caddyserver/certmagic v0.17.2 // indirect
|
||||||
github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c // indirect
|
github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c // indirect
|
||||||
github.com/cretz/bine v0.2.0 // indirect
|
github.com/cretz/bine v0.2.0 // indirect
|
||||||
@@ -41,7 +41,7 @@ require (
|
|||||||
github.com/google/btree v1.0.1 // indirect
|
github.com/google/btree v1.0.1 // indirect
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 // indirect
|
github.com/insomniacslk/dhcp v0.0.0-20230220010740-598984875576 // indirect
|
||||||
github.com/josharian/native v1.1.0 // indirect
|
github.com/josharian/native v1.1.0 // indirect
|
||||||
github.com/klauspost/compress v1.15.15 // indirect
|
github.com/klauspost/compress v1.15.15 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
||||||
@@ -55,6 +55,7 @@ require (
|
|||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
|
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||||
github.com/pires/go-proxyproto v0.6.2 // indirect
|
github.com/pires/go-proxyproto v0.6.2 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
@@ -62,20 +63,21 @@ require (
|
|||||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
||||||
github.com/refraction-networking/utls v1.2.2 // indirect
|
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 // indirect
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 // indirect
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
|
||||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455 // indirect
|
github.com/sagernet/sing-dns v0.1.4 // indirect
|
||||||
|
github.com/sagernet/sing-shadowtls v0.0.0-20230221075551-c5ad05179260 // indirect
|
||||||
github.com/sagernet/sing-tun v0.1.1 // indirect
|
github.com/sagernet/sing-tun v0.1.1 // indirect
|
||||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb // indirect
|
github.com/sagernet/sing-vmess v0.1.2 // indirect
|
||||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect
|
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d // indirect
|
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d // indirect
|
||||||
|
github.com/sagernet/utls v0.0.0-20230220130002-c08891932056 // indirect
|
||||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
|
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
|
||||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
|
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
|
||||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect
|
github.com/u-root/uio v0.0.0-20230215032506-9aa6f7e2d72c // indirect
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||||
go.etcd.io/bbolt v1.3.7 // indirect
|
go.etcd.io/bbolt v1.3.7 // indirect
|
||||||
go.uber.org/atomic v1.10.0 // indirect
|
go.uber.org/atomic v1.10.0 // indirect
|
||||||
|
|||||||
36
test/go.sum
36
test/go.sum
@@ -7,8 +7,8 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6
|
|||||||
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||||
github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE=
|
github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE=
|
||||||
github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
|
github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
|
||||||
@@ -67,8 +67,8 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE
|
|||||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 h1:Z72DOke2yOK0Ms4Z2LK1E1OrRJXOxSj5DllTz2FYTRg=
|
github.com/insomniacslk/dhcp v0.0.0-20230220010740-598984875576 h1:ehee0+xI4xtJTRJhN+PVA2GzCLB6KRgHRoZMg2lHwJU=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8/go.mod h1:m5WMe03WCvWcXjRnhvaAbAAXdCnu20J5P+mmH44ZzpE=
|
github.com/insomniacslk/dhcp v0.0.0-20230220010740-598984875576/go.mod h1:yGKD3yZIGAjEZXiIjVQ0SQ09Y/RzETOoqEOi6nXqX0Y=
|
||||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
@@ -114,6 +114,8 @@ github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrB
|
|||||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
|
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
|
||||||
github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
@@ -128,8 +130,6 @@ github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4
|
|||||||
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||||
github.com/refraction-networking/utls v1.2.2 h1:uBE6V173CwG8MQrSBpNZHAix1fxOvuLKYyjFAu3uqo0=
|
|
||||||
github.com/refraction-networking/utls v1.2.2/go.mod h1:L1goe44KvhnTfctUffM2isnJpSjPlYShrhXDeZaoYKw=
|
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||||
@@ -140,20 +140,24 @@ github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRK
|
|||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
||||||
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13 h1:S/+YvJCEChwnckGhzqSrE/Q2m6aVWhkt1I4Pv2yCMVw=
|
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b h1:Ji2AfGlc4j9AitobOx4k3BCj7eS5nSxL1cgaL81zvlo=
|
||||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455 h1:VA/j2Jg+JURgKw2C1Dw2tpjfOZwbLXRy8PJRbJS/HEU=
|
github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0=
|
||||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455/go.mod h1:nonvn66ja+UNrQl3jzJy0EFRn15QUaCFAVXTXf6TgJ4=
|
github.com/sagernet/sing-dns v0.1.4/go.mod h1:1+6pCa48B1AI78lD+/i/dLgpw4MwfnsSpZo0Ds8wzzk=
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7 h1:Plup6oEiyLzY3HDqQ+QsUBzgBGdVmcsgf3t8h940z9U=
|
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9 h1:qS39eA4C7x+zhEkySbASrtmb6ebdy5v0y2M6mgkmSO0=
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7/go.mod h1:O5LtOs8Ivw686FqLpO0Zu+A0ROVE15VeqEK3yDRRAms=
|
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9/go.mod h1:f3mHTy5shnVM9l8UocMlJgC/1G/zdj5FuEuVXhDinGU=
|
||||||
|
github.com/sagernet/sing-shadowtls v0.0.0-20230221075551-c5ad05179260 h1:RKeyBMI5kRnno3/WGsW4HrGnZkhISQQrnRxAKXbf5Vg=
|
||||||
|
github.com/sagernet/sing-shadowtls v0.0.0-20230221075551-c5ad05179260/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
|
||||||
github.com/sagernet/sing-tun v0.1.1 h1:2Hg3GAyJWzQ7Ua1j74dE+mI06vaqSBO9yD4tkTjggn4=
|
github.com/sagernet/sing-tun v0.1.1 h1:2Hg3GAyJWzQ7Ua1j74dE+mI06vaqSBO9yD4tkTjggn4=
|
||||||
github.com/sagernet/sing-tun v0.1.1/go.mod h1:WzW/SkT+Nh9uJn/FIYUE2YJHYuPwfbh8sATOzU9QDGw=
|
github.com/sagernet/sing-tun v0.1.1/go.mod h1:WzW/SkT+Nh9uJn/FIYUE2YJHYuPwfbh8sATOzU9QDGw=
|
||||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb h1:oyd3w17fXNmWVYFUe17YVHJW5CLW9X2mxJFDP/IWrAM=
|
github.com/sagernet/sing-vmess v0.1.2 h1:RbOZNAId2LrCai8epMoQXlf0XTrou0bfcw08hNBg6lM=
|
||||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb/go.mod h1:9KkmnQzTL4Gvv8U2TRAH2BOITCGsGPpHtUPP5sxn5sY=
|
github.com/sagernet/sing-vmess v0.1.2/go.mod h1:9NSj8mZTx1JIY/HF9LaYRppUsVkysIN5tEFpNZujXxY=
|
||||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
|
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
|
||||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
|
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d h1:trP/l6ZPWvQ/5Gv99Z7/t/v8iYy06akDMejxW1sznUk=
|
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d h1:trP/l6ZPWvQ/5Gv99Z7/t/v8iYy06akDMejxW1sznUk=
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d/go.mod h1:jk6Ii8Y3En+j2KQDLgdgQGwb3M6y7EL567jFnGYhN9g=
|
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d/go.mod h1:jk6Ii8Y3En+j2KQDLgdgQGwb3M6y7EL567jFnGYhN9g=
|
||||||
|
github.com/sagernet/utls v0.0.0-20230220130002-c08891932056 h1:gDXi/0uYe8dA48UyUI1LM2la5QYN0IvsDvR2H2+kFnA=
|
||||||
|
github.com/sagernet/utls v0.0.0-20230220130002-c08891932056/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
|
||||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
|
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
|
||||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
|
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
|
||||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
||||||
@@ -177,8 +181,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww=
|
github.com/u-root/uio v0.0.0-20230215032506-9aa6f7e2d72c h1:PHoGTnweZP+KIg/8Zc6+iOesrIF5yHkpb4GBDxHm7yE=
|
||||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E=
|
github.com/u-root/uio v0.0.0-20230215032506-9aa6f7e2d72c/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
|||||||
@@ -17,17 +17,23 @@ import (
|
|||||||
|
|
||||||
func TestShadowTLS(t *testing.T) {
|
func TestShadowTLS(t *testing.T) {
|
||||||
t.Run("v1", func(t *testing.T) {
|
t.Run("v1", func(t *testing.T) {
|
||||||
testShadowTLS(t, 1, "")
|
testShadowTLS(t, 1, "", false)
|
||||||
})
|
})
|
||||||
t.Run("v2", func(t *testing.T) {
|
t.Run("v2", func(t *testing.T) {
|
||||||
testShadowTLS(t, 2, "hello")
|
testShadowTLS(t, 2, "hello", false)
|
||||||
})
|
})
|
||||||
t.Run("v3", func(t *testing.T) {
|
t.Run("v3", func(t *testing.T) {
|
||||||
testShadowTLS(t, 3, "hello")
|
testShadowTLS(t, 3, "hello", false)
|
||||||
|
})
|
||||||
|
t.Run("v2-utls", func(t *testing.T) {
|
||||||
|
testShadowTLS(t, 2, "hello", true)
|
||||||
|
})
|
||||||
|
t.Run("v3-utls", func(t *testing.T) {
|
||||||
|
testShadowTLS(t, 3, "hello", true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testShadowTLS(t *testing.T, version int, password string) {
|
func testShadowTLS(t *testing.T, version int, password string, utlsEanbled bool) {
|
||||||
method := shadowaead_2022.List[0]
|
method := shadowaead_2022.List[0]
|
||||||
ssPassword := mkBase64(t, 16)
|
ssPassword := mkBase64(t, 16)
|
||||||
startInstance(t, option.Options{
|
startInstance(t, option.Options{
|
||||||
@@ -58,6 +64,7 @@ func testShadowTLS(t *testing.T, version int, password string) {
|
|||||||
},
|
},
|
||||||
Version: version,
|
Version: version,
|
||||||
Password: password,
|
Password: password,
|
||||||
|
Users: []option.ShadowTLSUser{{Password: password}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -82,9 +89,6 @@ func testShadowTLS(t *testing.T, version int, password string) {
|
|||||||
DialerOptions: option.DialerOptions{
|
DialerOptions: option.DialerOptions{
|
||||||
Detour: "detour",
|
Detour: "detour",
|
||||||
},
|
},
|
||||||
MultiplexOptions: &option.MultiplexOptions{
|
|
||||||
Enabled: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -98,6 +102,9 @@ func testShadowTLS(t *testing.T, version int, password string) {
|
|||||||
TLS: &option.OutboundTLSOptions{
|
TLS: &option.OutboundTLSOptions{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
ServerName: "google.com",
|
ServerName: "google.com",
|
||||||
|
UTLS: &option.OutboundUTLSOptions{
|
||||||
|
Enabled: utlsEanbled,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Version: version,
|
Version: version,
|
||||||
Password: password,
|
Password: password,
|
||||||
@@ -117,7 +124,7 @@ func testShadowTLS(t *testing.T, version int, password string) {
|
|||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
testSuit(t, clientPort, testPort)
|
testTCP(t, clientPort, testPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShadowTLSFallback(t *testing.T) {
|
func TestShadowTLSFallback(t *testing.T) {
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
package shadowtls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ N.VectorisedWriter = (*ClientConn)(nil)
|
|
||||||
|
|
||||||
type ClientConn struct {
|
|
||||||
*Conn
|
|
||||||
hashConn *HashReadConn
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClientConn(hashConn *HashReadConn) *ClientConn {
|
|
||||||
return &ClientConn{NewConn(hashConn.Conn), hashConn}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ClientConn) Write(p []byte) (n int, err error) {
|
|
||||||
if c.hashConn != nil {
|
|
||||||
sum := c.hashConn.Sum()
|
|
||||||
c.hashConn = nil
|
|
||||||
_, err = bufio.WriteVectorised(c.Conn, [][]byte{sum, p})
|
|
||||||
if err == nil {
|
|
||||||
n = len(p)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return c.Conn.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ClientConn) WriteVectorised(buffers []*buf.Buffer) error {
|
|
||||||
if c.hashConn != nil {
|
|
||||||
sum := c.hashConn.Sum()
|
|
||||||
c.hashConn = nil
|
|
||||||
return c.Conn.WriteVectorised(append([]*buf.Buffer{buf.As(sum)}, buffers...))
|
|
||||||
}
|
|
||||||
return c.Conn.WriteVectorised(buffers)
|
|
||||||
}
|
|
||||||
@@ -1,294 +0,0 @@
|
|||||||
package shadowtls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/binary"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
tlsRandomSize = 32
|
|
||||||
tlsHeaderSize = 5
|
|
||||||
tlsSessionIDSize = 32
|
|
||||||
|
|
||||||
clientHello = 1
|
|
||||||
serverHello = 2
|
|
||||||
|
|
||||||
changeCipherSpec = 20
|
|
||||||
alert = 21
|
|
||||||
handshake = 22
|
|
||||||
applicationData = 23
|
|
||||||
|
|
||||||
serverRandomIndex = tlsHeaderSize + 1 + 3 + 2
|
|
||||||
sessionIDLengthIndex = tlsHeaderSize + 1 + 3 + 2 + tlsRandomSize
|
|
||||||
tlsHmacHeaderSize = tlsHeaderSize + hmacSize
|
|
||||||
hmacSize = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
func generateSessionID(password string) func(clientHello []byte, sessionID []byte) error {
|
|
||||||
return func(clientHello []byte, sessionID []byte) error {
|
|
||||||
const sessionIDStart = 1 + 3 + 2 + tlsRandomSize + 1
|
|
||||||
if len(clientHello) < sessionIDStart+tlsSessionIDSize {
|
|
||||||
return E.New("unexpected client hello length")
|
|
||||||
}
|
|
||||||
_, err := rand.Read(sessionID[:tlsSessionIDSize-hmacSize])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hmacSHA1Hash := hmac.New(sha1.New, []byte(password))
|
|
||||||
hmacSHA1Hash.Write(clientHello[:sessionIDStart])
|
|
||||||
hmacSHA1Hash.Write(sessionID)
|
|
||||||
hmacSHA1Hash.Write(clientHello[sessionIDStart+tlsSessionIDSize:])
|
|
||||||
copy(sessionID[tlsSessionIDSize-hmacSize:], hmacSHA1Hash.Sum(nil)[:hmacSize])
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type StreamWrapper struct {
|
|
||||||
net.Conn
|
|
||||||
password string
|
|
||||||
buffer *buf.Buffer
|
|
||||||
serverRandom []byte
|
|
||||||
readHMAC hash.Hash
|
|
||||||
readHMACKey []byte
|
|
||||||
authorized bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStreamWrapper(conn net.Conn, password string) *StreamWrapper {
|
|
||||||
return &StreamWrapper{
|
|
||||||
Conn: conn,
|
|
||||||
password: password,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *StreamWrapper) Authorized() (bool, []byte, hash.Hash) {
|
|
||||||
return w.authorized, w.serverRandom, w.readHMAC
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *StreamWrapper) Read(p []byte) (n int, err error) {
|
|
||||||
if w.buffer != nil {
|
|
||||||
if !w.buffer.IsEmpty() {
|
|
||||||
return w.buffer.Read(p)
|
|
||||||
}
|
|
||||||
w.buffer.Release()
|
|
||||||
w.buffer = nil
|
|
||||||
}
|
|
||||||
var tlsHeader [tlsHeaderSize]byte
|
|
||||||
_, err = io.ReadFull(w.Conn, tlsHeader[:])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
length := int(binary.BigEndian.Uint16(tlsHeader[3:tlsHeaderSize]))
|
|
||||||
w.buffer = buf.NewSize(tlsHeaderSize + length)
|
|
||||||
common.Must1(w.buffer.Write(tlsHeader[:]))
|
|
||||||
_, err = w.buffer.ReadFullFrom(w.Conn, length)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buffer := w.buffer.Bytes()
|
|
||||||
switch tlsHeader[0] {
|
|
||||||
case handshake:
|
|
||||||
if len(buffer) > serverRandomIndex+tlsRandomSize && buffer[5] == serverHello {
|
|
||||||
w.serverRandom = make([]byte, tlsRandomSize)
|
|
||||||
copy(w.serverRandom, buffer[serverRandomIndex:serverRandomIndex+tlsRandomSize])
|
|
||||||
w.readHMAC = hmac.New(sha1.New, []byte(w.password))
|
|
||||||
w.readHMAC.Write(w.serverRandom)
|
|
||||||
w.readHMACKey = kdf(w.password, w.serverRandom)
|
|
||||||
}
|
|
||||||
case applicationData:
|
|
||||||
w.authorized = false
|
|
||||||
if len(buffer) > tlsHmacHeaderSize && w.readHMAC != nil {
|
|
||||||
w.readHMAC.Write(buffer[tlsHmacHeaderSize:])
|
|
||||||
if hmac.Equal(w.readHMAC.Sum(nil)[:hmacSize], buffer[tlsHeaderSize:tlsHmacHeaderSize]) {
|
|
||||||
xorSlice(buffer[tlsHmacHeaderSize:], w.readHMACKey)
|
|
||||||
copy(buffer[hmacSize:], buffer[:tlsHeaderSize])
|
|
||||||
binary.BigEndian.PutUint16(buffer[hmacSize+3:], uint16(len(buffer)-tlsHmacHeaderSize))
|
|
||||||
w.buffer.Advance(hmacSize)
|
|
||||||
w.authorized = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return w.buffer.Read(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func kdf(password string, serverRandom []byte) []byte {
|
|
||||||
hasher := sha256.New()
|
|
||||||
hasher.Write([]byte(password))
|
|
||||||
hasher.Write(serverRandom)
|
|
||||||
return hasher.Sum(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func xorSlice(data []byte, key []byte) {
|
|
||||||
for i := range data {
|
|
||||||
data[i] ^= key[i%len(key)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ N.VectorisedWriter = (*VerifiedConn)(nil)
|
|
||||||
|
|
||||||
type VerifiedConn struct {
|
|
||||||
net.Conn
|
|
||||||
writer N.VectorisedWriter
|
|
||||||
hmacAdd hash.Hash
|
|
||||||
hmacVerify hash.Hash
|
|
||||||
hmacIgnore hash.Hash
|
|
||||||
|
|
||||||
buffer *buf.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewVerifiedConn(
|
|
||||||
conn net.Conn,
|
|
||||||
hmacAdd hash.Hash,
|
|
||||||
hmacVerify hash.Hash,
|
|
||||||
hmacIgnore hash.Hash,
|
|
||||||
) *VerifiedConn {
|
|
||||||
return &VerifiedConn{
|
|
||||||
Conn: conn,
|
|
||||||
writer: bufio.NewVectorisedWriter(conn),
|
|
||||||
hmacAdd: hmacAdd,
|
|
||||||
hmacVerify: hmacVerify,
|
|
||||||
hmacIgnore: hmacIgnore,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *VerifiedConn) Read(b []byte) (n int, err error) {
|
|
||||||
if c.buffer != nil {
|
|
||||||
if !c.buffer.IsEmpty() {
|
|
||||||
return c.buffer.Read(b)
|
|
||||||
}
|
|
||||||
c.buffer.Release()
|
|
||||||
c.buffer = nil
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
var tlsHeader [tlsHeaderSize]byte
|
|
||||||
_, err = io.ReadFull(c.Conn, tlsHeader[:])
|
|
||||||
if err != nil {
|
|
||||||
sendAlert(c.Conn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
length := int(binary.BigEndian.Uint16(tlsHeader[3:tlsHeaderSize]))
|
|
||||||
c.buffer = buf.NewSize(tlsHeaderSize + length)
|
|
||||||
common.Must1(c.buffer.Write(tlsHeader[:]))
|
|
||||||
_, err = c.buffer.ReadFullFrom(c.Conn, length)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buffer := c.buffer.Bytes()
|
|
||||||
switch buffer[0] {
|
|
||||||
case alert:
|
|
||||||
err = E.Cause(net.ErrClosed, "remote alert")
|
|
||||||
return
|
|
||||||
case applicationData:
|
|
||||||
if c.hmacIgnore != nil {
|
|
||||||
if verifyApplicationData(buffer, c.hmacIgnore, false) {
|
|
||||||
c.buffer.Release()
|
|
||||||
c.buffer = nil
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
c.hmacIgnore = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !verifyApplicationData(buffer, c.hmacVerify, true) {
|
|
||||||
sendAlert(c.Conn)
|
|
||||||
err = E.New("application data verification failed")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.buffer.Advance(tlsHmacHeaderSize)
|
|
||||||
default:
|
|
||||||
sendAlert(c.Conn)
|
|
||||||
err = E.New("unexpected TLS record type: ", buffer[0])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return c.buffer.Read(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *VerifiedConn) Write(p []byte) (n int, err error) {
|
|
||||||
pTotal := len(p)
|
|
||||||
for len(p) > 0 {
|
|
||||||
var pWrite []byte
|
|
||||||
if len(p) > 16384 {
|
|
||||||
pWrite = p[:16384]
|
|
||||||
p = p[16384:]
|
|
||||||
} else {
|
|
||||||
pWrite = p
|
|
||||||
p = nil
|
|
||||||
}
|
|
||||||
_, err = c.write(pWrite)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
n = pTotal
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *VerifiedConn) write(p []byte) (n int, err error) {
|
|
||||||
var header [tlsHmacHeaderSize]byte
|
|
||||||
header[0] = applicationData
|
|
||||||
header[1] = 3
|
|
||||||
header[2] = 3
|
|
||||||
binary.BigEndian.PutUint16(header[3:tlsHeaderSize], hmacSize+uint16(len(p)))
|
|
||||||
c.hmacAdd.Write(p)
|
|
||||||
hmacHash := c.hmacAdd.Sum(nil)[:hmacSize]
|
|
||||||
c.hmacAdd.Write(hmacHash)
|
|
||||||
copy(header[tlsHeaderSize:], hmacHash)
|
|
||||||
_, err = bufio.WriteVectorised(c.writer, [][]byte{common.Dup(header[:]), p})
|
|
||||||
if err == nil {
|
|
||||||
n = len(p)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *VerifiedConn) WriteVectorised(buffers []*buf.Buffer) error {
|
|
||||||
var header [tlsHmacHeaderSize]byte
|
|
||||||
header[0] = applicationData
|
|
||||||
header[1] = 3
|
|
||||||
header[2] = 3
|
|
||||||
binary.BigEndian.PutUint16(header[3:tlsHeaderSize], hmacSize+uint16(buf.LenMulti(buffers)))
|
|
||||||
for _, buffer := range buffers {
|
|
||||||
c.hmacAdd.Write(buffer.Bytes())
|
|
||||||
}
|
|
||||||
c.hmacAdd.Write(c.hmacAdd.Sum(nil)[:hmacSize])
|
|
||||||
copy(header[tlsHeaderSize:], c.hmacAdd.Sum(nil)[:hmacSize])
|
|
||||||
return c.writer.WriteVectorised(append([]*buf.Buffer{buf.As(header[:])}, buffers...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifyApplicationData(frame []byte, hmac hash.Hash, update bool) bool {
|
|
||||||
if frame[1] != 3 || frame[2] != 3 || len(frame) < tlsHmacHeaderSize {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
hmac.Write(frame[tlsHmacHeaderSize:])
|
|
||||||
hmacHash := hmac.Sum(nil)[:hmacSize]
|
|
||||||
if update {
|
|
||||||
hmac.Write(hmacHash)
|
|
||||||
}
|
|
||||||
return bytes.Equal(frame[tlsHeaderSize:tlsHeaderSize+hmacSize], hmacHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendAlert(writer io.Writer) {
|
|
||||||
const recordSize = 31
|
|
||||||
record := [recordSize]byte{
|
|
||||||
alert,
|
|
||||||
3,
|
|
||||||
3,
|
|
||||||
0,
|
|
||||||
recordSize - tlsHeaderSize,
|
|
||||||
}
|
|
||||||
_, err := rand.Read(record[tlsHeaderSize:])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writer.Write(record[:])
|
|
||||||
}
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
package shadowtls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/x509"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/tls"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ tls.Config = (*ClientTLSConfig)(nil)
|
|
||||||
|
|
||||||
type ClientTLSConfig struct {
|
|
||||||
config *sTLSConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClientTLSConfig(serverAddress string, options option.OutboundTLSOptions, password string) (*ClientTLSConfig, error) {
|
|
||||||
if options.ECH != nil && options.ECH.Enabled {
|
|
||||||
return nil, E.New("ECH is not supported in shadowtls v3")
|
|
||||||
} else if options.UTLS != nil && options.UTLS.Enabled {
|
|
||||||
return nil, E.New("UTLS is not supported in shadowtls v3")
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverName string
|
|
||||||
if options.ServerName != "" {
|
|
||||||
serverName = options.ServerName
|
|
||||||
} else if serverAddress != "" {
|
|
||||||
if _, err := netip.ParseAddr(serverName); err != nil {
|
|
||||||
serverName = serverAddress
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if serverName == "" && !options.Insecure {
|
|
||||||
return nil, E.New("missing server_name or insecure=true")
|
|
||||||
}
|
|
||||||
|
|
||||||
var tlsConfig sTLSConfig
|
|
||||||
tlsConfig.SessionIDGenerator = generateSessionID(password)
|
|
||||||
if options.DisableSNI {
|
|
||||||
tlsConfig.ServerName = "127.0.0.1"
|
|
||||||
} else {
|
|
||||||
tlsConfig.ServerName = serverName
|
|
||||||
}
|
|
||||||
if options.Insecure {
|
|
||||||
tlsConfig.InsecureSkipVerify = options.Insecure
|
|
||||||
} else if options.DisableSNI {
|
|
||||||
tlsConfig.InsecureSkipVerify = true
|
|
||||||
tlsConfig.VerifyConnection = func(state sTLSConnectionState) error {
|
|
||||||
verifyOptions := x509.VerifyOptions{
|
|
||||||
DNSName: serverName,
|
|
||||||
Intermediates: x509.NewCertPool(),
|
|
||||||
}
|
|
||||||
for _, cert := range state.PeerCertificates[1:] {
|
|
||||||
verifyOptions.Intermediates.AddCert(cert)
|
|
||||||
}
|
|
||||||
_, err := state.PeerCertificates[0].Verify(verifyOptions)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(options.ALPN) > 0 {
|
|
||||||
tlsConfig.NextProtos = options.ALPN
|
|
||||||
}
|
|
||||||
if options.MinVersion != "" {
|
|
||||||
minVersion, err := tls.ParseTLSVersion(options.MinVersion)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "parse min_version")
|
|
||||||
}
|
|
||||||
tlsConfig.MinVersion = minVersion
|
|
||||||
}
|
|
||||||
if options.MaxVersion != "" {
|
|
||||||
maxVersion, err := tls.ParseTLSVersion(options.MaxVersion)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "parse max_version")
|
|
||||||
}
|
|
||||||
tlsConfig.MaxVersion = maxVersion
|
|
||||||
}
|
|
||||||
if options.CipherSuites != nil {
|
|
||||||
find:
|
|
||||||
for _, cipherSuite := range options.CipherSuites {
|
|
||||||
for _, tlsCipherSuite := range sTLSCipherSuites() {
|
|
||||||
if cipherSuite == tlsCipherSuite.Name {
|
|
||||||
tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
|
|
||||||
continue find
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var certificate []byte
|
|
||||||
if options.Certificate != "" {
|
|
||||||
certificate = []byte(options.Certificate)
|
|
||||||
} else if options.CertificatePath != "" {
|
|
||||||
content, err := os.ReadFile(options.CertificatePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "read certificate")
|
|
||||||
}
|
|
||||||
certificate = content
|
|
||||||
}
|
|
||||||
if len(certificate) > 0 {
|
|
||||||
certPool := x509.NewCertPool()
|
|
||||||
if !certPool.AppendCertsFromPEM(certificate) {
|
|
||||||
return nil, E.New("failed to parse certificate:\n\n", certificate)
|
|
||||||
}
|
|
||||||
tlsConfig.RootCAs = certPool
|
|
||||||
}
|
|
||||||
return &ClientTLSConfig{&tlsConfig}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ClientTLSConfig) ServerName() string {
|
|
||||||
return c.config.ServerName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ClientTLSConfig) SetServerName(serverName string) {
|
|
||||||
c.config.ServerName = serverName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ClientTLSConfig) NextProtos() []string {
|
|
||||||
return c.config.NextProtos
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ClientTLSConfig) SetNextProtos(nextProto []string) {
|
|
||||||
c.config.NextProtos = nextProto
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ClientTLSConfig) Config() (*tls.STDConfig, error) {
|
|
||||||
return nil, E.New("unsupported usage for ShadowTLS")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ClientTLSConfig) Client(conn net.Conn) tls.Conn {
|
|
||||||
return &shadowTLSConnWrapper{sTLSClient(conn, c.config)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ClientTLSConfig) Clone() tls.Config {
|
|
||||||
return &ClientTLSConfig{c.config.Clone()}
|
|
||||||
}
|
|
||||||
|
|
||||||
type shadowTLSConnWrapper struct {
|
|
||||||
*sTLSConn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *shadowTLSConnWrapper) ConnectionState() tls.ConnectionState {
|
|
||||||
state := c.sTLSConn.ConnectionState()
|
|
||||||
return tls.ConnectionState{
|
|
||||||
Version: state.Version,
|
|
||||||
HandshakeComplete: state.HandshakeComplete,
|
|
||||||
DidResume: state.DidResume,
|
|
||||||
CipherSuite: state.CipherSuite,
|
|
||||||
NegotiatedProtocol: state.NegotiatedProtocol,
|
|
||||||
ServerName: state.ServerName,
|
|
||||||
PeerCertificates: state.PeerCertificates,
|
|
||||||
VerifiedChains: state.VerifiedChains,
|
|
||||||
SignedCertificateTimestamps: state.SignedCertificateTimestamps,
|
|
||||||
OCSPResponse: state.OCSPResponse,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//go:build !go1.20
|
|
||||||
|
|
||||||
package shadowtls
|
|
||||||
|
|
||||||
import sTLS "github.com/sagernet/sing-box/transport/shadowtls/tls_go119"
|
|
||||||
|
|
||||||
type (
|
|
||||||
sTLSConfig = sTLS.Config
|
|
||||||
sTLSConnectionState = sTLS.ConnectionState
|
|
||||||
sTLSConn = sTLS.Conn
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
sTLSCipherSuites = sTLS.CipherSuites
|
|
||||||
sTLSClient = sTLS.Client
|
|
||||||
)
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//go:build go1.20
|
|
||||||
|
|
||||||
package shadowtls
|
|
||||||
|
|
||||||
import sTLS "github.com/sagernet/sing-box/transport/shadowtls/tls"
|
|
||||||
|
|
||||||
type (
|
|
||||||
sTLSConfig = sTLS.Config
|
|
||||||
sTLSConnectionState = sTLS.ConnectionState
|
|
||||||
sTLSConn = sTLS.Conn
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
sTLSCipherSuites = sTLS.CipherSuites
|
|
||||||
sTLSClient = sTLS.Client
|
|
||||||
)
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
package shadowtls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/tls"
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ N.VectorisedWriter = (*Conn)(nil)
|
|
||||||
|
|
||||||
type Conn struct {
|
|
||||||
net.Conn
|
|
||||||
writer N.VectorisedWriter
|
|
||||||
readRemaining int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConn(conn net.Conn) *Conn {
|
|
||||||
return &Conn{
|
|
||||||
Conn: conn,
|
|
||||||
writer: bufio.NewVectorisedWriter(conn),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) Read(p []byte) (n int, err error) {
|
|
||||||
if c.readRemaining > 0 {
|
|
||||||
if len(p) > c.readRemaining {
|
|
||||||
p = p[:c.readRemaining]
|
|
||||||
}
|
|
||||||
n, err = c.Conn.Read(p)
|
|
||||||
c.readRemaining -= n
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var tlsHeader [5]byte
|
|
||||||
_, err = io.ReadFull(c.Conn, common.Dup(tlsHeader[:]))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
length := int(binary.BigEndian.Uint16(tlsHeader[3:5]))
|
|
||||||
if tlsHeader[0] != 23 {
|
|
||||||
return 0, E.New("unexpected TLS record type: ", tlsHeader[0])
|
|
||||||
}
|
|
||||||
readLen := len(p)
|
|
||||||
if readLen > length {
|
|
||||||
readLen = length
|
|
||||||
}
|
|
||||||
n, err = c.Conn.Read(p[:readLen])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.readRemaining = length - n
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) Write(p []byte) (n int, err error) {
|
|
||||||
var header [5]byte
|
|
||||||
defer common.KeepAlive(header)
|
|
||||||
header[0] = 23
|
|
||||||
for len(p) > 16384 {
|
|
||||||
binary.BigEndian.PutUint16(header[1:3], tls.VersionTLS12)
|
|
||||||
binary.BigEndian.PutUint16(header[3:5], uint16(16384))
|
|
||||||
_, err = bufio.WriteVectorised(c.writer, [][]byte{common.Dup(header[:]), p[:16384]})
|
|
||||||
common.KeepAlive(header)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n += 16384
|
|
||||||
p = p[16384:]
|
|
||||||
}
|
|
||||||
binary.BigEndian.PutUint16(header[1:3], tls.VersionTLS12)
|
|
||||||
binary.BigEndian.PutUint16(header[3:5], uint16(len(p)))
|
|
||||||
_, err = bufio.WriteVectorised(c.writer, [][]byte{common.Dup(header[:]), p})
|
|
||||||
if err == nil {
|
|
||||||
n += len(p)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) WriteVectorised(buffers []*buf.Buffer) error {
|
|
||||||
var header [5]byte
|
|
||||||
defer common.KeepAlive(header)
|
|
||||||
header[0] = 23
|
|
||||||
dataLen := buf.LenMulti(buffers)
|
|
||||||
binary.BigEndian.PutUint16(header[1:3], tls.VersionTLS12)
|
|
||||||
binary.BigEndian.PutUint16(header[3:5], uint16(dataLen))
|
|
||||||
return c.writer.WriteVectorised(append([]*buf.Buffer{buf.As(header[:])}, buffers...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) Upstream() any {
|
|
||||||
return c.Conn
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
package shadowtls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha1"
|
|
||||||
"hash"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HashReadConn struct {
|
|
||||||
net.Conn
|
|
||||||
hmac hash.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHashReadConn(conn net.Conn, password string) *HashReadConn {
|
|
||||||
return &HashReadConn{
|
|
||||||
conn,
|
|
||||||
hmac.New(sha1.New, []byte(password)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *HashReadConn) Read(b []byte) (n int, err error) {
|
|
||||||
n, err = c.Conn.Read(b)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = c.hmac.Write(b[:n])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *HashReadConn) Sum() []byte {
|
|
||||||
return c.hmac.Sum(nil)[:8]
|
|
||||||
}
|
|
||||||
|
|
||||||
type HashWriteConn struct {
|
|
||||||
net.Conn
|
|
||||||
hmac hash.Hash
|
|
||||||
hasContent bool
|
|
||||||
lastSum []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHashWriteConn(conn net.Conn, password string) *HashWriteConn {
|
|
||||||
return &HashWriteConn{
|
|
||||||
Conn: conn,
|
|
||||||
hmac: hmac.New(sha1.New, []byte(password)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *HashWriteConn) Write(p []byte) (n int, err error) {
|
|
||||||
if c.hmac != nil {
|
|
||||||
if c.hasContent {
|
|
||||||
c.lastSum = c.Sum()
|
|
||||||
}
|
|
||||||
c.hmac.Write(p)
|
|
||||||
c.hasContent = true
|
|
||||||
}
|
|
||||||
return c.Conn.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *HashWriteConn) Sum() []byte {
|
|
||||||
return c.hmac.Sum(nil)[:8]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *HashWriteConn) LastSum() []byte {
|
|
||||||
return c.lastSum
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *HashWriteConn) Fallback() {
|
|
||||||
c.hmac = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *HashWriteConn) HasContent() bool {
|
|
||||||
return c.hasContent
|
|
||||||
}
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
package shadowtls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/binary"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/rw"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExtractFrame(conn net.Conn) (*buf.Buffer, error) {
|
|
||||||
var tlsHeader [tlsHeaderSize]byte
|
|
||||||
_, err := io.ReadFull(conn, tlsHeader[:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
length := int(binary.BigEndian.Uint16(tlsHeader[3:]))
|
|
||||||
buffer := buf.NewSize(tlsHeaderSize + length)
|
|
||||||
common.Must1(buffer.Write(tlsHeader[:]))
|
|
||||||
_, err = buffer.ReadFullFrom(conn, length)
|
|
||||||
if err != nil {
|
|
||||||
buffer.Release()
|
|
||||||
}
|
|
||||||
return buffer, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func VerifyClientHello(frame []byte, password string) error {
|
|
||||||
const minLen = tlsHeaderSize + 1 + 3 + 2 + tlsRandomSize + 1 + tlsSessionIDSize
|
|
||||||
const hmacIndex = sessionIDLengthIndex + 1 + tlsSessionIDSize - hmacSize
|
|
||||||
if len(frame) < minLen {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
} else if frame[0] != handshake {
|
|
||||||
return E.New("unexpected record type")
|
|
||||||
} else if frame[5] != clientHello {
|
|
||||||
return E.New("unexpected handshake type")
|
|
||||||
} else if frame[sessionIDLengthIndex] != tlsSessionIDSize {
|
|
||||||
return E.New("unexpected session id length")
|
|
||||||
}
|
|
||||||
hmacSHA1Hash := hmac.New(sha1.New, []byte(password))
|
|
||||||
hmacSHA1Hash.Write(frame[tlsHeaderSize:hmacIndex])
|
|
||||||
hmacSHA1Hash.Write(rw.ZeroBytes[:4])
|
|
||||||
hmacSHA1Hash.Write(frame[hmacIndex+hmacSize:])
|
|
||||||
if !hmac.Equal(frame[hmacIndex:hmacIndex+hmacSize], hmacSHA1Hash.Sum(nil)[:hmacSize]) {
|
|
||||||
return E.New("hmac mismatch")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExtractServerRandom(frame []byte) []byte {
|
|
||||||
const minLen = tlsHeaderSize + 1 + 3 + 2 + tlsRandomSize
|
|
||||||
|
|
||||||
if len(frame) < minLen || frame[0] != handshake || frame[5] != serverHello {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
serverRandom := make([]byte, tlsRandomSize)
|
|
||||||
copy(serverRandom, frame[serverRandomIndex:serverRandomIndex+tlsRandomSize])
|
|
||||||
return serverRandom
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsServerHelloSupportTLS13(frame []byte) bool {
|
|
||||||
if len(frame) < sessionIDLengthIndex {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
reader := bytes.NewReader(frame[sessionIDLengthIndex:])
|
|
||||||
|
|
||||||
var sessionIdLength uint8
|
|
||||||
err := binary.Read(reader, binary.BigEndian, &sessionIdLength)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
_, err = io.CopyN(io.Discard, reader, int64(sessionIdLength))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = io.CopyN(io.Discard, reader, 3)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var extensionListLength uint16
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &extensionListLength)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i := uint16(0); i < extensionListLength; i++ {
|
|
||||||
var extensionType uint16
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &extensionType)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
var extensionLength uint16
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &extensionLength)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if extensionType != 43 {
|
|
||||||
_, err = io.CopyN(io.Discard, reader, int64(extensionLength))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if extensionLength != 2 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
var extensionValue uint16
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &extensionValue)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return extensionValue == 0x0304
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyByFrameUntilHMACMatches(conn net.Conn, handshakeConn net.Conn, hmacVerify hash.Hash, hmacReset func()) (*buf.Buffer, error) {
|
|
||||||
for {
|
|
||||||
frameBuffer, err := ExtractFrame(conn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "read client record")
|
|
||||||
}
|
|
||||||
frame := frameBuffer.Bytes()
|
|
||||||
if len(frame) > tlsHmacHeaderSize && frame[0] == applicationData {
|
|
||||||
hmacReset()
|
|
||||||
hmacVerify.Write(frame[tlsHmacHeaderSize:])
|
|
||||||
hmacHash := hmacVerify.Sum(nil)[:4]
|
|
||||||
if bytes.Equal(hmacHash, frame[tlsHeaderSize:tlsHmacHeaderSize]) {
|
|
||||||
hmacReset()
|
|
||||||
hmacVerify.Write(frame[tlsHmacHeaderSize:])
|
|
||||||
hmacVerify.Write(frame[tlsHeaderSize:tlsHmacHeaderSize])
|
|
||||||
frameBuffer.Advance(tlsHmacHeaderSize)
|
|
||||||
return frameBuffer, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err = handshakeConn.Write(frame)
|
|
||||||
frameBuffer.Release()
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "write clint frame")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyByFrameWithModification(conn net.Conn, handshakeConn net.Conn, password string, serverRandom []byte, hmacWrite hash.Hash) error {
|
|
||||||
writeKey := kdf(password, serverRandom)
|
|
||||||
writer := bufio.NewVectorisedWriter(handshakeConn)
|
|
||||||
for {
|
|
||||||
frameBuffer, err := ExtractFrame(conn)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "read server record")
|
|
||||||
}
|
|
||||||
frame := frameBuffer.Bytes()
|
|
||||||
if frame[0] == applicationData {
|
|
||||||
xorSlice(frame[tlsHeaderSize:], writeKey)
|
|
||||||
hmacWrite.Write(frame[tlsHeaderSize:])
|
|
||||||
binary.BigEndian.PutUint16(frame[3:], uint16(len(frame)-tlsHeaderSize+hmacSize))
|
|
||||||
hmacHash := hmacWrite.Sum(nil)[:4]
|
|
||||||
_, err = bufio.WriteVectorised(writer, [][]byte{frame[:tlsHeaderSize], hmacHash, frame[tlsHeaderSize:]})
|
|
||||||
frameBuffer.Release()
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "write modified server frame")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_, err = handshakeConn.Write(frame)
|
|
||||||
frameBuffer.Release()
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "write server frame")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# tls
|
|
||||||
|
|
||||||
crypto/tls fork for shadowtls v3
|
|
||||||
|
|
||||||
version: go1.20.0
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
type alert uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
// alert level
|
|
||||||
alertLevelWarning = 1
|
|
||||||
alertLevelError = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
alertCloseNotify alert = 0
|
|
||||||
alertUnexpectedMessage alert = 10
|
|
||||||
alertBadRecordMAC alert = 20
|
|
||||||
alertDecryptionFailed alert = 21
|
|
||||||
alertRecordOverflow alert = 22
|
|
||||||
alertDecompressionFailure alert = 30
|
|
||||||
alertHandshakeFailure alert = 40
|
|
||||||
alertBadCertificate alert = 42
|
|
||||||
alertUnsupportedCertificate alert = 43
|
|
||||||
alertCertificateRevoked alert = 44
|
|
||||||
alertCertificateExpired alert = 45
|
|
||||||
alertCertificateUnknown alert = 46
|
|
||||||
alertIllegalParameter alert = 47
|
|
||||||
alertUnknownCA alert = 48
|
|
||||||
alertAccessDenied alert = 49
|
|
||||||
alertDecodeError alert = 50
|
|
||||||
alertDecryptError alert = 51
|
|
||||||
alertExportRestriction alert = 60
|
|
||||||
alertProtocolVersion alert = 70
|
|
||||||
alertInsufficientSecurity alert = 71
|
|
||||||
alertInternalError alert = 80
|
|
||||||
alertInappropriateFallback alert = 86
|
|
||||||
alertUserCanceled alert = 90
|
|
||||||
alertNoRenegotiation alert = 100
|
|
||||||
alertMissingExtension alert = 109
|
|
||||||
alertUnsupportedExtension alert = 110
|
|
||||||
alertCertificateUnobtainable alert = 111
|
|
||||||
alertUnrecognizedName alert = 112
|
|
||||||
alertBadCertificateStatusResponse alert = 113
|
|
||||||
alertBadCertificateHashValue alert = 114
|
|
||||||
alertUnknownPSKIdentity alert = 115
|
|
||||||
alertCertificateRequired alert = 116
|
|
||||||
alertNoApplicationProtocol alert = 120
|
|
||||||
)
|
|
||||||
|
|
||||||
var alertText = map[alert]string{
|
|
||||||
alertCloseNotify: "close notify",
|
|
||||||
alertUnexpectedMessage: "unexpected message",
|
|
||||||
alertBadRecordMAC: "bad record MAC",
|
|
||||||
alertDecryptionFailed: "decryption failed",
|
|
||||||
alertRecordOverflow: "record overflow",
|
|
||||||
alertDecompressionFailure: "decompression failure",
|
|
||||||
alertHandshakeFailure: "handshake failure",
|
|
||||||
alertBadCertificate: "bad certificate",
|
|
||||||
alertUnsupportedCertificate: "unsupported certificate",
|
|
||||||
alertCertificateRevoked: "revoked certificate",
|
|
||||||
alertCertificateExpired: "expired certificate",
|
|
||||||
alertCertificateUnknown: "unknown certificate",
|
|
||||||
alertIllegalParameter: "illegal parameter",
|
|
||||||
alertUnknownCA: "unknown certificate authority",
|
|
||||||
alertAccessDenied: "access denied",
|
|
||||||
alertDecodeError: "error decoding message",
|
|
||||||
alertDecryptError: "error decrypting message",
|
|
||||||
alertExportRestriction: "export restriction",
|
|
||||||
alertProtocolVersion: "protocol version not supported",
|
|
||||||
alertInsufficientSecurity: "insufficient security level",
|
|
||||||
alertInternalError: "internal error",
|
|
||||||
alertInappropriateFallback: "inappropriate fallback",
|
|
||||||
alertUserCanceled: "user canceled",
|
|
||||||
alertNoRenegotiation: "no renegotiation",
|
|
||||||
alertMissingExtension: "missing extension",
|
|
||||||
alertUnsupportedExtension: "unsupported extension",
|
|
||||||
alertCertificateUnobtainable: "certificate unobtainable",
|
|
||||||
alertUnrecognizedName: "unrecognized name",
|
|
||||||
alertBadCertificateStatusResponse: "bad certificate status response",
|
|
||||||
alertBadCertificateHashValue: "bad certificate hash value",
|
|
||||||
alertUnknownPSKIdentity: "unknown PSK identity",
|
|
||||||
alertCertificateRequired: "certificate required",
|
|
||||||
alertNoApplicationProtocol: "no application protocol",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e alert) String() string {
|
|
||||||
s, ok := alertText[e]
|
|
||||||
if ok {
|
|
||||||
return "tls: " + s
|
|
||||||
}
|
|
||||||
return "tls: alert(" + strconv.Itoa(int(e)) + ")"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e alert) Error() string {
|
|
||||||
return e.String()
|
|
||||||
}
|
|
||||||
@@ -1,293 +0,0 @@
|
|||||||
// Copyright 2017 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/ed25519"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rsa"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// verifyHandshakeSignature verifies a signature against pre-hashed
|
|
||||||
// (if required) handshake contents.
|
|
||||||
func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error {
|
|
||||||
switch sigType {
|
|
||||||
case signatureECDSA:
|
|
||||||
pubKey, ok := pubkey.(*ecdsa.PublicKey)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected an ECDSA public key, got %T", pubkey)
|
|
||||||
}
|
|
||||||
if !ecdsa.VerifyASN1(pubKey, signed, sig) {
|
|
||||||
return errors.New("ECDSA verification failure")
|
|
||||||
}
|
|
||||||
case signatureEd25519:
|
|
||||||
pubKey, ok := pubkey.(ed25519.PublicKey)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected an Ed25519 public key, got %T", pubkey)
|
|
||||||
}
|
|
||||||
if !ed25519.Verify(pubKey, signed, sig) {
|
|
||||||
return errors.New("Ed25519 verification failure")
|
|
||||||
}
|
|
||||||
case signaturePKCS1v15:
|
|
||||||
pubKey, ok := pubkey.(*rsa.PublicKey)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected an RSA public key, got %T", pubkey)
|
|
||||||
}
|
|
||||||
if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, signed, sig); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case signatureRSAPSS:
|
|
||||||
pubKey, ok := pubkey.(*rsa.PublicKey)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected an RSA public key, got %T", pubkey)
|
|
||||||
}
|
|
||||||
signOpts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
|
|
||||||
if err := rsa.VerifyPSS(pubKey, hashFunc, signed, sig, signOpts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return errors.New("internal error: unknown signature type")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
serverSignatureContext = "TLS 1.3, server CertificateVerify\x00"
|
|
||||||
clientSignatureContext = "TLS 1.3, client CertificateVerify\x00"
|
|
||||||
)
|
|
||||||
|
|
||||||
var signaturePadding = []byte{
|
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
||||||
}
|
|
||||||
|
|
||||||
// signedMessage returns the pre-hashed (if necessary) message to be signed by
|
|
||||||
// certificate keys in TLS 1.3. See RFC 8446, Section 4.4.3.
|
|
||||||
func signedMessage(sigHash crypto.Hash, context string, transcript hash.Hash) []byte {
|
|
||||||
if sigHash == directSigning {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
b.Write(signaturePadding)
|
|
||||||
io.WriteString(b, context)
|
|
||||||
b.Write(transcript.Sum(nil))
|
|
||||||
return b.Bytes()
|
|
||||||
}
|
|
||||||
h := sigHash.New()
|
|
||||||
h.Write(signaturePadding)
|
|
||||||
io.WriteString(h, context)
|
|
||||||
h.Write(transcript.Sum(nil))
|
|
||||||
return h.Sum(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeAndHashFromSignatureScheme returns the corresponding signature type and
|
|
||||||
// crypto.Hash for a given TLS SignatureScheme.
|
|
||||||
func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType uint8, hash crypto.Hash, err error) {
|
|
||||||
switch signatureAlgorithm {
|
|
||||||
case PKCS1WithSHA1, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512:
|
|
||||||
sigType = signaturePKCS1v15
|
|
||||||
case PSSWithSHA256, PSSWithSHA384, PSSWithSHA512:
|
|
||||||
sigType = signatureRSAPSS
|
|
||||||
case ECDSAWithSHA1, ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512:
|
|
||||||
sigType = signatureECDSA
|
|
||||||
case Ed25519:
|
|
||||||
sigType = signatureEd25519
|
|
||||||
default:
|
|
||||||
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
|
|
||||||
}
|
|
||||||
switch signatureAlgorithm {
|
|
||||||
case PKCS1WithSHA1, ECDSAWithSHA1:
|
|
||||||
hash = crypto.SHA1
|
|
||||||
case PKCS1WithSHA256, PSSWithSHA256, ECDSAWithP256AndSHA256:
|
|
||||||
hash = crypto.SHA256
|
|
||||||
case PKCS1WithSHA384, PSSWithSHA384, ECDSAWithP384AndSHA384:
|
|
||||||
hash = crypto.SHA384
|
|
||||||
case PKCS1WithSHA512, PSSWithSHA512, ECDSAWithP521AndSHA512:
|
|
||||||
hash = crypto.SHA512
|
|
||||||
case Ed25519:
|
|
||||||
hash = directSigning
|
|
||||||
default:
|
|
||||||
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
|
|
||||||
}
|
|
||||||
return sigType, hash, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// legacyTypeAndHashFromPublicKey returns the fixed signature type and crypto.Hash for
|
|
||||||
// a given public key used with TLS 1.0 and 1.1, before the introduction of
|
|
||||||
// signature algorithm negotiation.
|
|
||||||
func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash crypto.Hash, err error) {
|
|
||||||
switch pub.(type) {
|
|
||||||
case *rsa.PublicKey:
|
|
||||||
return signaturePKCS1v15, crypto.MD5SHA1, nil
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
return signatureECDSA, crypto.SHA1, nil
|
|
||||||
case ed25519.PublicKey:
|
|
||||||
// RFC 8422 specifies support for Ed25519 in TLS 1.0 and 1.1,
|
|
||||||
// but it requires holding on to a handshake transcript to do a
|
|
||||||
// full signature, and not even OpenSSL bothers with the
|
|
||||||
// complexity, so we can't even test it properly.
|
|
||||||
return 0, 0, fmt.Errorf("tls: Ed25519 public keys are not supported before TLS 1.2")
|
|
||||||
default:
|
|
||||||
return 0, 0, fmt.Errorf("tls: unsupported public key: %T", pub)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var rsaSignatureSchemes = []struct {
|
|
||||||
scheme SignatureScheme
|
|
||||||
minModulusBytes int
|
|
||||||
maxVersion uint16
|
|
||||||
}{
|
|
||||||
// RSA-PSS is used with PSSSaltLengthEqualsHash, and requires
|
|
||||||
// emLen >= hLen + sLen + 2
|
|
||||||
{PSSWithSHA256, crypto.SHA256.Size()*2 + 2, VersionTLS13},
|
|
||||||
{PSSWithSHA384, crypto.SHA384.Size()*2 + 2, VersionTLS13},
|
|
||||||
{PSSWithSHA512, crypto.SHA512.Size()*2 + 2, VersionTLS13},
|
|
||||||
// PKCS #1 v1.5 uses prefixes from hashPrefixes in crypto/rsa, and requires
|
|
||||||
// emLen >= len(prefix) + hLen + 11
|
|
||||||
// TLS 1.3 dropped support for PKCS #1 v1.5 in favor of RSA-PSS.
|
|
||||||
{PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11, VersionTLS12},
|
|
||||||
{PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11, VersionTLS12},
|
|
||||||
{PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11, VersionTLS12},
|
|
||||||
{PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11, VersionTLS12},
|
|
||||||
}
|
|
||||||
|
|
||||||
// signatureSchemesForCertificate returns the list of supported SignatureSchemes
|
|
||||||
// for a given certificate, based on the public key and the protocol version,
|
|
||||||
// and optionally filtered by its explicit SupportedSignatureAlgorithms.
|
|
||||||
//
|
|
||||||
// This function must be kept in sync with supportedSignatureAlgorithms.
|
|
||||||
// FIPS filtering is applied in the caller, selectSignatureScheme.
|
|
||||||
func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme {
|
|
||||||
priv, ok := cert.PrivateKey.(crypto.Signer)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var sigAlgs []SignatureScheme
|
|
||||||
switch pub := priv.Public().(type) {
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
if version != VersionTLS13 {
|
|
||||||
// In TLS 1.2 and earlier, ECDSA algorithms are not
|
|
||||||
// constrained to a single curve.
|
|
||||||
sigAlgs = []SignatureScheme{
|
|
||||||
ECDSAWithP256AndSHA256,
|
|
||||||
ECDSAWithP384AndSHA384,
|
|
||||||
ECDSAWithP521AndSHA512,
|
|
||||||
ECDSAWithSHA1,
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
switch pub.Curve {
|
|
||||||
case elliptic.P256():
|
|
||||||
sigAlgs = []SignatureScheme{ECDSAWithP256AndSHA256}
|
|
||||||
case elliptic.P384():
|
|
||||||
sigAlgs = []SignatureScheme{ECDSAWithP384AndSHA384}
|
|
||||||
case elliptic.P521():
|
|
||||||
sigAlgs = []SignatureScheme{ECDSAWithP521AndSHA512}
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case *rsa.PublicKey:
|
|
||||||
size := pub.Size()
|
|
||||||
sigAlgs = make([]SignatureScheme, 0, len(rsaSignatureSchemes))
|
|
||||||
for _, candidate := range rsaSignatureSchemes {
|
|
||||||
if size >= candidate.minModulusBytes && version <= candidate.maxVersion {
|
|
||||||
sigAlgs = append(sigAlgs, candidate.scheme)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case ed25519.PublicKey:
|
|
||||||
sigAlgs = []SignatureScheme{Ed25519}
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if cert.SupportedSignatureAlgorithms != nil {
|
|
||||||
var filteredSigAlgs []SignatureScheme
|
|
||||||
for _, sigAlg := range sigAlgs {
|
|
||||||
if isSupportedSignatureAlgorithm(sigAlg, cert.SupportedSignatureAlgorithms) {
|
|
||||||
filteredSigAlgs = append(filteredSigAlgs, sigAlg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return filteredSigAlgs
|
|
||||||
}
|
|
||||||
return sigAlgs
|
|
||||||
}
|
|
||||||
|
|
||||||
// selectSignatureScheme picks a SignatureScheme from the peer's preference list
|
|
||||||
// that works with the selected certificate. It's only called for protocol
|
|
||||||
// versions that support signature algorithms, so TLS 1.2 and 1.3.
|
|
||||||
func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureScheme) (SignatureScheme, error) {
|
|
||||||
supportedAlgs := signatureSchemesForCertificate(vers, c)
|
|
||||||
if len(supportedAlgs) == 0 {
|
|
||||||
return 0, unsupportedCertificateError(c)
|
|
||||||
}
|
|
||||||
if len(peerAlgs) == 0 && vers == VersionTLS12 {
|
|
||||||
// For TLS 1.2, if the client didn't send signature_algorithms then we
|
|
||||||
// can assume that it supports SHA1. See RFC 5246, Section 7.4.1.4.1.
|
|
||||||
peerAlgs = []SignatureScheme{PKCS1WithSHA1, ECDSAWithSHA1}
|
|
||||||
}
|
|
||||||
// Pick signature scheme in the peer's preference order, as our
|
|
||||||
// preference order is not configurable.
|
|
||||||
for _, preferredAlg := range peerAlgs {
|
|
||||||
if needFIPS() && !isSupportedSignatureAlgorithm(preferredAlg, fipsSupportedSignatureAlgorithms) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
|
|
||||||
return preferredAlg, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0, errors.New("tls: peer doesn't support any of the certificate's signature algorithms")
|
|
||||||
}
|
|
||||||
|
|
||||||
// unsupportedCertificateError returns a helpful error for certificates with
|
|
||||||
// an unsupported private key.
|
|
||||||
func unsupportedCertificateError(cert *Certificate) error {
|
|
||||||
switch cert.PrivateKey.(type) {
|
|
||||||
case rsa.PrivateKey, ecdsa.PrivateKey:
|
|
||||||
return fmt.Errorf("tls: unsupported certificate: private key is %T, expected *%T",
|
|
||||||
cert.PrivateKey, cert.PrivateKey)
|
|
||||||
case *ed25519.PrivateKey:
|
|
||||||
return fmt.Errorf("tls: unsupported certificate: private key is *ed25519.PrivateKey, expected ed25519.PrivateKey")
|
|
||||||
}
|
|
||||||
|
|
||||||
signer, ok := cert.PrivateKey.(crypto.Signer)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("tls: certificate private key (%T) does not implement crypto.Signer",
|
|
||||||
cert.PrivateKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch pub := signer.Public().(type) {
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
switch pub.Curve {
|
|
||||||
case elliptic.P256():
|
|
||||||
case elliptic.P384():
|
|
||||||
case elliptic.P521():
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("tls: unsupported certificate curve (%s)", pub.Curve.Params().Name)
|
|
||||||
}
|
|
||||||
case *rsa.PublicKey:
|
|
||||||
return fmt.Errorf("tls: certificate RSA key size too small for supported signature algorithms")
|
|
||||||
case ed25519.PublicKey:
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("tls: unsupported certificate key (%T)", pub)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cert.SupportedSignatureAlgorithms != nil {
|
|
||||||
return fmt.Errorf("tls: peer doesn't support the certificate custom signature algorithms")
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("tls: internal error: unsupported key (%T)", cert.PrivateKey)
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
// Copyright 2017 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build boringcrypto
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/internal/boring/fipstls"
|
|
||||||
)
|
|
||||||
|
|
||||||
// needFIPS returns fipstls.Required(); it avoids a new import in common.go.
|
|
||||||
func needFIPS() bool {
|
|
||||||
return fipstls.Required()
|
|
||||||
}
|
|
||||||
|
|
||||||
// fipsMinVersion replaces c.minVersion in FIPS-only mode.
|
|
||||||
func fipsMinVersion(c *Config) uint16 {
|
|
||||||
// FIPS requires TLS 1.2.
|
|
||||||
return VersionTLS12
|
|
||||||
}
|
|
||||||
|
|
||||||
// fipsMaxVersion replaces c.maxVersion in FIPS-only mode.
|
|
||||||
func fipsMaxVersion(c *Config) uint16 {
|
|
||||||
// FIPS requires TLS 1.2.
|
|
||||||
return VersionTLS12
|
|
||||||
}
|
|
||||||
|
|
||||||
// default defaultFIPSCurvePreferences is the FIPS-allowed curves,
|
|
||||||
// in preference order (most preferable first).
|
|
||||||
var defaultFIPSCurvePreferences = []CurveID{CurveP256, CurveP384, CurveP521}
|
|
||||||
|
|
||||||
// fipsCurvePreferences replaces c.curvePreferences in FIPS-only mode.
|
|
||||||
func fipsCurvePreferences(c *Config) []CurveID {
|
|
||||||
if c == nil || len(c.CurvePreferences) == 0 {
|
|
||||||
return defaultFIPSCurvePreferences
|
|
||||||
}
|
|
||||||
var list []CurveID
|
|
||||||
for _, id := range c.CurvePreferences {
|
|
||||||
for _, allowed := range defaultFIPSCurvePreferences {
|
|
||||||
if id == allowed {
|
|
||||||
list = append(list, id)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
// defaultCipherSuitesFIPS are the FIPS-allowed cipher suites.
|
|
||||||
var defaultCipherSuitesFIPS = []uint16{
|
|
||||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
|
||||||
TLS_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
TLS_RSA_WITH_AES_256_GCM_SHA384,
|
|
||||||
}
|
|
||||||
|
|
||||||
// fipsCipherSuites replaces c.cipherSuites in FIPS-only mode.
|
|
||||||
func fipsCipherSuites(c *Config) []uint16 {
|
|
||||||
if c == nil || c.CipherSuites == nil {
|
|
||||||
return defaultCipherSuitesFIPS
|
|
||||||
}
|
|
||||||
list := make([]uint16, 0, len(defaultCipherSuitesFIPS))
|
|
||||||
for _, id := range c.CipherSuites {
|
|
||||||
for _, allowed := range defaultCipherSuitesFIPS {
|
|
||||||
if id == allowed {
|
|
||||||
list = append(list, id)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
// fipsSupportedSignatureAlgorithms currently are a subset of
|
|
||||||
// defaultSupportedSignatureAlgorithms without Ed25519 and SHA-1.
|
|
||||||
var fipsSupportedSignatureAlgorithms = []SignatureScheme{
|
|
||||||
PSSWithSHA256,
|
|
||||||
PSSWithSHA384,
|
|
||||||
PSSWithSHA512,
|
|
||||||
PKCS1WithSHA256,
|
|
||||||
ECDSAWithP256AndSHA256,
|
|
||||||
PKCS1WithSHA384,
|
|
||||||
ECDSAWithP384AndSHA384,
|
|
||||||
PKCS1WithSHA512,
|
|
||||||
ECDSAWithP521AndSHA512,
|
|
||||||
}
|
|
||||||
|
|
||||||
// supportedSignatureAlgorithms returns the supported signature algorithms.
|
|
||||||
func supportedSignatureAlgorithms() []SignatureScheme {
|
|
||||||
if !needFIPS() {
|
|
||||||
return defaultSupportedSignatureAlgorithms
|
|
||||||
}
|
|
||||||
return fipsSupportedSignatureAlgorithms
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
// Copyright 2022 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/x509"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
)
|
|
||||||
|
|
||||||
type cacheEntry struct {
|
|
||||||
refs atomic.Int64
|
|
||||||
cert *x509.Certificate
|
|
||||||
}
|
|
||||||
|
|
||||||
// certCache implements an intern table for reference counted x509.Certificates,
|
|
||||||
// implemented in a similar fashion to BoringSSL's CRYPTO_BUFFER_POOL. This
|
|
||||||
// allows for a single x509.Certificate to be kept in memory and referenced from
|
|
||||||
// multiple Conns. Returned references should not be mutated by callers. Certificates
|
|
||||||
// are still safe to use after they are removed from the cache.
|
|
||||||
//
|
|
||||||
// Certificates are returned wrapped in a activeCert struct that should be held by
|
|
||||||
// the caller. When references to the activeCert are freed, the number of references
|
|
||||||
// to the certificate in the cache is decremented. Once the number of references
|
|
||||||
// reaches zero, the entry is evicted from the cache.
|
|
||||||
//
|
|
||||||
// The main difference between this implementation and CRYPTO_BUFFER_POOL is that
|
|
||||||
// CRYPTO_BUFFER_POOL is a more generic structure which supports blobs of data,
|
|
||||||
// rather than specific structures. Since we only care about x509.Certificates,
|
|
||||||
// certCache is implemented as a specific cache, rather than a generic one.
|
|
||||||
//
|
|
||||||
// See https://boringssl.googlesource.com/boringssl/+/master/include/openssl/pool.h
|
|
||||||
// and https://boringssl.googlesource.com/boringssl/+/master/crypto/pool/pool.c
|
|
||||||
// for the BoringSSL reference.
|
|
||||||
type certCache struct {
|
|
||||||
sync.Map
|
|
||||||
}
|
|
||||||
|
|
||||||
var clientCertCache = new(certCache)
|
|
||||||
|
|
||||||
// activeCert is a handle to a certificate held in the cache. Once there are
|
|
||||||
// no alive activeCerts for a given certificate, the certificate is removed
|
|
||||||
// from the cache by a finalizer.
|
|
||||||
type activeCert struct {
|
|
||||||
cert *x509.Certificate
|
|
||||||
}
|
|
||||||
|
|
||||||
// active increments the number of references to the entry, wraps the
|
|
||||||
// certificate in the entry in a activeCert, and sets the finalizer.
|
|
||||||
//
|
|
||||||
// Note that there is a race between active and the finalizer set on the
|
|
||||||
// returned activeCert, triggered if active is called after the ref count is
|
|
||||||
// decremented such that refs may be > 0 when evict is called. We consider this
|
|
||||||
// safe, since the caller holding an activeCert for an entry that is no longer
|
|
||||||
// in the cache is fine, with the only side effect being the memory overhead of
|
|
||||||
// there being more than one distinct reference to a certificate alive at once.
|
|
||||||
func (cc *certCache) active(e *cacheEntry) *activeCert {
|
|
||||||
e.refs.Add(1)
|
|
||||||
a := &activeCert{e.cert}
|
|
||||||
runtime.SetFinalizer(a, func(_ *activeCert) {
|
|
||||||
if e.refs.Add(-1) == 0 {
|
|
||||||
cc.evict(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// evict removes a cacheEntry from the cache.
|
|
||||||
func (cc *certCache) evict(e *cacheEntry) {
|
|
||||||
cc.Delete(string(e.cert.Raw))
|
|
||||||
}
|
|
||||||
|
|
||||||
// newCert returns a x509.Certificate parsed from der. If there is already a copy
|
|
||||||
// of the certificate in the cache, a reference to the existing certificate will
|
|
||||||
// be returned. Otherwise, a fresh certificate will be added to the cache, and
|
|
||||||
// the reference returned. The returned reference should not be mutated.
|
|
||||||
func (cc *certCache) newCert(der []byte) (*activeCert, error) {
|
|
||||||
if entry, ok := cc.Load(string(der)); ok {
|
|
||||||
return cc.active(entry.(*cacheEntry)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
cert, err := x509.ParseCertificate(der)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := &cacheEntry{cert: cert}
|
|
||||||
if entry, loaded := cc.LoadOrStore(string(der), entry); loaded {
|
|
||||||
return cc.active(entry.(*cacheEntry)), nil
|
|
||||||
}
|
|
||||||
return cc.active(entry), nil
|
|
||||||
}
|
|
||||||
@@ -1,701 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/des"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/rc4"
|
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/sha256"
|
|
||||||
"fmt"
|
|
||||||
"hash"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
|
||||||
"golang.org/x/sys/cpu"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CipherSuite is a TLS cipher suite. Note that most functions in this package
|
|
||||||
// accept and expose cipher suite IDs instead of this type.
|
|
||||||
type CipherSuite struct {
|
|
||||||
ID uint16
|
|
||||||
Name string
|
|
||||||
|
|
||||||
// Supported versions is the list of TLS protocol versions that can
|
|
||||||
// negotiate this cipher suite.
|
|
||||||
SupportedVersions []uint16
|
|
||||||
|
|
||||||
// Insecure is true if the cipher suite has known security issues
|
|
||||||
// due to its primitives, design, or implementation.
|
|
||||||
Insecure bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
supportedUpToTLS12 = []uint16{VersionTLS10, VersionTLS11, VersionTLS12}
|
|
||||||
supportedOnlyTLS12 = []uint16{VersionTLS12}
|
|
||||||
supportedOnlyTLS13 = []uint16{VersionTLS13}
|
|
||||||
)
|
|
||||||
|
|
||||||
// CipherSuites returns a list of cipher suites currently implemented by this
|
|
||||||
// package, excluding those with security issues, which are returned by
|
|
||||||
// InsecureCipherSuites.
|
|
||||||
//
|
|
||||||
// The list is sorted by ID. Note that the default cipher suites selected by
|
|
||||||
// this package might depend on logic that can't be captured by a static list,
|
|
||||||
// and might not match those returned by this function.
|
|
||||||
func CipherSuites() []*CipherSuite {
|
|
||||||
return []*CipherSuite{
|
|
||||||
{TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
|
|
||||||
{TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
|
|
||||||
{TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
|
|
||||||
{TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
|
|
||||||
|
|
||||||
{TLS_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256", supportedOnlyTLS13, false},
|
|
||||||
{TLS_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384", supportedOnlyTLS13, false},
|
|
||||||
{TLS_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256", supportedOnlyTLS13, false},
|
|
||||||
|
|
||||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
|
|
||||||
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
|
|
||||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
|
|
||||||
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
|
|
||||||
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
|
|
||||||
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
|
|
||||||
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
|
|
||||||
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
|
|
||||||
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
|
|
||||||
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// InsecureCipherSuites returns a list of cipher suites currently implemented by
|
|
||||||
// this package and which have security issues.
|
|
||||||
//
|
|
||||||
// Most applications should not use the cipher suites in this list, and should
|
|
||||||
// only use those returned by CipherSuites.
|
|
||||||
func InsecureCipherSuites() []*CipherSuite {
|
|
||||||
// This list includes RC4, CBC_SHA256, and 3DES cipher suites. See
|
|
||||||
// cipherSuitesPreferenceOrder for details.
|
|
||||||
return []*CipherSuite{
|
|
||||||
{TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
|
||||||
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
|
|
||||||
{TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
|
||||||
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
|
||||||
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
|
||||||
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
|
|
||||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
|
||||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CipherSuiteName returns the standard name for the passed cipher suite ID
|
|
||||||
// (e.g. "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"), or a fallback representation
|
|
||||||
// of the ID value if the cipher suite is not implemented by this package.
|
|
||||||
func CipherSuiteName(id uint16) string {
|
|
||||||
for _, c := range CipherSuites() {
|
|
||||||
if c.ID == id {
|
|
||||||
return c.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, c := range InsecureCipherSuites() {
|
|
||||||
if c.ID == id {
|
|
||||||
return c.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("0x%04X", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// suiteECDHE indicates that the cipher suite involves elliptic curve
|
|
||||||
// Diffie-Hellman. This means that it should only be selected when the
|
|
||||||
// client indicates that it supports ECC with a curve and point format
|
|
||||||
// that we're happy with.
|
|
||||||
suiteECDHE = 1 << iota
|
|
||||||
// suiteECSign indicates that the cipher suite involves an ECDSA or
|
|
||||||
// EdDSA signature and therefore may only be selected when the server's
|
|
||||||
// certificate is ECDSA or EdDSA. If this is not set then the cipher suite
|
|
||||||
// is RSA based.
|
|
||||||
suiteECSign
|
|
||||||
// suiteTLS12 indicates that the cipher suite should only be advertised
|
|
||||||
// and accepted when using TLS 1.2.
|
|
||||||
suiteTLS12
|
|
||||||
// suiteSHA384 indicates that the cipher suite uses SHA384 as the
|
|
||||||
// handshake hash.
|
|
||||||
suiteSHA384
|
|
||||||
)
|
|
||||||
|
|
||||||
// A cipherSuite is a TLS 1.0–1.2 cipher suite, and defines the key exchange
|
|
||||||
// mechanism, as well as the cipher+MAC pair or the AEAD.
|
|
||||||
type cipherSuite struct {
|
|
||||||
id uint16
|
|
||||||
// the lengths, in bytes, of the key material needed for each component.
|
|
||||||
keyLen int
|
|
||||||
macLen int
|
|
||||||
ivLen int
|
|
||||||
ka func(version uint16) keyAgreement
|
|
||||||
// flags is a bitmask of the suite* values, above.
|
|
||||||
flags int
|
|
||||||
cipher func(key, iv []byte, isRead bool) any
|
|
||||||
mac func(key []byte) hash.Hash
|
|
||||||
aead func(key, fixedNonce []byte) aead
|
|
||||||
}
|
|
||||||
|
|
||||||
var cipherSuites = []*cipherSuite{ // TODO: replace with a map, since the order doesn't matter.
|
|
||||||
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
|
|
||||||
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
|
|
||||||
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
|
|
||||||
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadAESGCM},
|
|
||||||
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
|
||||||
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
|
||||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil},
|
|
||||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
|
|
||||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, cipherAES, macSHA256, nil},
|
|
||||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
|
|
||||||
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
|
|
||||||
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
|
|
||||||
{TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM},
|
|
||||||
{TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
|
||||||
{TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil},
|
|
||||||
{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
|
|
||||||
{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
|
|
||||||
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
|
|
||||||
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
|
|
||||||
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil},
|
|
||||||
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil},
|
|
||||||
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherRC4, macSHA1, nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
// selectCipherSuite returns the first TLS 1.0–1.2 cipher suite from ids which
|
|
||||||
// is also in supportedIDs and passes the ok filter.
|
|
||||||
func selectCipherSuite(ids, supportedIDs []uint16, ok func(*cipherSuite) bool) *cipherSuite {
|
|
||||||
for _, id := range ids {
|
|
||||||
candidate := cipherSuiteByID(id)
|
|
||||||
if candidate == nil || !ok(candidate) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, suppID := range supportedIDs {
|
|
||||||
if id == suppID {
|
|
||||||
return candidate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// A cipherSuiteTLS13 defines only the pair of the AEAD algorithm and hash
|
|
||||||
// algorithm to be used with HKDF. See RFC 8446, Appendix B.4.
|
|
||||||
type cipherSuiteTLS13 struct {
|
|
||||||
id uint16
|
|
||||||
keyLen int
|
|
||||||
aead func(key, fixedNonce []byte) aead
|
|
||||||
hash crypto.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
var cipherSuitesTLS13 = []*cipherSuiteTLS13{ // TODO: replace with a map.
|
|
||||||
{TLS_AES_128_GCM_SHA256, 16, aeadAESGCMTLS13, crypto.SHA256},
|
|
||||||
{TLS_CHACHA20_POLY1305_SHA256, 32, aeadChaCha20Poly1305, crypto.SHA256},
|
|
||||||
{TLS_AES_256_GCM_SHA384, 32, aeadAESGCMTLS13, crypto.SHA384},
|
|
||||||
}
|
|
||||||
|
|
||||||
// cipherSuitesPreferenceOrder is the order in which we'll select (on the
|
|
||||||
// server) or advertise (on the client) TLS 1.0–1.2 cipher suites.
|
|
||||||
//
|
|
||||||
// Cipher suites are filtered but not reordered based on the application and
|
|
||||||
// peer's preferences, meaning we'll never select a suite lower in this list if
|
|
||||||
// any higher one is available. This makes it more defensible to keep weaker
|
|
||||||
// cipher suites enabled, especially on the server side where we get the last
|
|
||||||
// word, since there are no known downgrade attacks on cipher suites selection.
|
|
||||||
//
|
|
||||||
// The list is sorted by applying the following priority rules, stopping at the
|
|
||||||
// first (most important) applicable one:
|
|
||||||
//
|
|
||||||
// - Anything else comes before RC4
|
|
||||||
//
|
|
||||||
// RC4 has practically exploitable biases. See https://www.rc4nomore.com.
|
|
||||||
//
|
|
||||||
// - Anything else comes before CBC_SHA256
|
|
||||||
//
|
|
||||||
// SHA-256 variants of the CBC ciphersuites don't implement any Lucky13
|
|
||||||
// countermeasures. See http://www.isg.rhul.ac.uk/tls/Lucky13.html and
|
|
||||||
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
|
|
||||||
//
|
|
||||||
// - Anything else comes before 3DES
|
|
||||||
//
|
|
||||||
// 3DES has 64-bit blocks, which makes it fundamentally susceptible to
|
|
||||||
// birthday attacks. See https://sweet32.info.
|
|
||||||
//
|
|
||||||
// - ECDHE comes before anything else
|
|
||||||
//
|
|
||||||
// Once we got the broken stuff out of the way, the most important
|
|
||||||
// property a cipher suite can have is forward secrecy. We don't
|
|
||||||
// implement FFDHE, so that means ECDHE.
|
|
||||||
//
|
|
||||||
// - AEADs come before CBC ciphers
|
|
||||||
//
|
|
||||||
// Even with Lucky13 countermeasures, MAC-then-Encrypt CBC cipher suites
|
|
||||||
// are fundamentally fragile, and suffered from an endless sequence of
|
|
||||||
// padding oracle attacks. See https://eprint.iacr.org/2015/1129,
|
|
||||||
// https://www.imperialviolet.org/2014/12/08/poodleagain.html, and
|
|
||||||
// https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/.
|
|
||||||
//
|
|
||||||
// - AES comes before ChaCha20
|
|
||||||
//
|
|
||||||
// When AES hardware is available, AES-128-GCM and AES-256-GCM are faster
|
|
||||||
// than ChaCha20Poly1305.
|
|
||||||
//
|
|
||||||
// When AES hardware is not available, AES-128-GCM is one or more of: much
|
|
||||||
// slower, way more complex, and less safe (because not constant time)
|
|
||||||
// than ChaCha20Poly1305.
|
|
||||||
//
|
|
||||||
// We use this list if we think both peers have AES hardware, and
|
|
||||||
// cipherSuitesPreferenceOrderNoAES otherwise.
|
|
||||||
//
|
|
||||||
// - AES-128 comes before AES-256
|
|
||||||
//
|
|
||||||
// The only potential advantages of AES-256 are better multi-target
|
|
||||||
// margins, and hypothetical post-quantum properties. Neither apply to
|
|
||||||
// TLS, and AES-256 is slower due to its four extra rounds (which don't
|
|
||||||
// contribute to the advantages above).
|
|
||||||
//
|
|
||||||
// - ECDSA comes before RSA
|
|
||||||
//
|
|
||||||
// The relative order of ECDSA and RSA cipher suites doesn't matter,
|
|
||||||
// as they depend on the certificate. Pick one to get a stable order.
|
|
||||||
var cipherSuitesPreferenceOrder = []uint16{
|
|
||||||
// AEADs w/ ECDHE
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
||||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
|
||||||
|
|
||||||
// CBC w/ ECDHE
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
|
||||||
|
|
||||||
// AEADs w/o ECDHE
|
|
||||||
TLS_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
TLS_RSA_WITH_AES_256_GCM_SHA384,
|
|
||||||
|
|
||||||
// CBC w/o ECDHE
|
|
||||||
TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
TLS_RSA_WITH_AES_256_CBC_SHA,
|
|
||||||
|
|
||||||
// 3DES
|
|
||||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
||||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
||||||
|
|
||||||
// CBC_SHA256
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
|
||||||
TLS_RSA_WITH_AES_128_CBC_SHA256,
|
|
||||||
|
|
||||||
// RC4
|
|
||||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
|
||||||
TLS_RSA_WITH_RC4_128_SHA,
|
|
||||||
}
|
|
||||||
|
|
||||||
var cipherSuitesPreferenceOrderNoAES = []uint16{
|
|
||||||
// ChaCha20Poly1305
|
|
||||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
|
||||||
|
|
||||||
// AES-GCM w/ ECDHE
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
||||||
|
|
||||||
// The rest of cipherSuitesPreferenceOrder.
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
|
||||||
TLS_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
TLS_RSA_WITH_AES_256_GCM_SHA384,
|
|
||||||
TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
TLS_RSA_WITH_AES_256_CBC_SHA,
|
|
||||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
||||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
|
||||||
TLS_RSA_WITH_AES_128_CBC_SHA256,
|
|
||||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
|
||||||
TLS_RSA_WITH_RC4_128_SHA,
|
|
||||||
}
|
|
||||||
|
|
||||||
// disabledCipherSuites are not used unless explicitly listed in
|
|
||||||
// Config.CipherSuites. They MUST be at the end of cipherSuitesPreferenceOrder.
|
|
||||||
var disabledCipherSuites = []uint16{
|
|
||||||
// CBC_SHA256
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
|
||||||
TLS_RSA_WITH_AES_128_CBC_SHA256,
|
|
||||||
|
|
||||||
// RC4
|
|
||||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
|
||||||
TLS_RSA_WITH_RC4_128_SHA,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
defaultCipherSuitesLen = len(cipherSuitesPreferenceOrder) - len(disabledCipherSuites)
|
|
||||||
defaultCipherSuites = cipherSuitesPreferenceOrder[:defaultCipherSuitesLen]
|
|
||||||
)
|
|
||||||
|
|
||||||
// defaultCipherSuitesTLS13 is also the preference order, since there are no
|
|
||||||
// disabled by default TLS 1.3 cipher suites. The same AES vs ChaCha20 logic as
|
|
||||||
// cipherSuitesPreferenceOrder applies.
|
|
||||||
var defaultCipherSuitesTLS13 = []uint16{
|
|
||||||
TLS_AES_128_GCM_SHA256,
|
|
||||||
TLS_AES_256_GCM_SHA384,
|
|
||||||
TLS_CHACHA20_POLY1305_SHA256,
|
|
||||||
}
|
|
||||||
|
|
||||||
var defaultCipherSuitesTLS13NoAES = []uint16{
|
|
||||||
TLS_CHACHA20_POLY1305_SHA256,
|
|
||||||
TLS_AES_128_GCM_SHA256,
|
|
||||||
TLS_AES_256_GCM_SHA384,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
|
|
||||||
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
|
||||||
// Keep in sync with crypto/aes/cipher_s390x.go.
|
|
||||||
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
|
|
||||||
(cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
|
|
||||||
|
|
||||||
hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
|
|
||||||
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
|
|
||||||
runtime.GOARCH == "s390x" && hasGCMAsmS390X
|
|
||||||
)
|
|
||||||
|
|
||||||
var aesgcmCiphers = map[uint16]bool{
|
|
||||||
// TLS 1.2
|
|
||||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true,
|
|
||||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: true,
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: true,
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: true,
|
|
||||||
// TLS 1.3
|
|
||||||
TLS_AES_128_GCM_SHA256: true,
|
|
||||||
TLS_AES_256_GCM_SHA384: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
var nonAESGCMAEADCiphers = map[uint16]bool{
|
|
||||||
// TLS 1.2
|
|
||||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: true,
|
|
||||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: true,
|
|
||||||
// TLS 1.3
|
|
||||||
TLS_CHACHA20_POLY1305_SHA256: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// aesgcmPreferred returns whether the first known cipher in the preference list
|
|
||||||
// is an AES-GCM cipher, implying the peer has hardware support for it.
|
|
||||||
func aesgcmPreferred(ciphers []uint16) bool {
|
|
||||||
for _, cID := range ciphers {
|
|
||||||
if c := cipherSuiteByID(cID); c != nil {
|
|
||||||
return aesgcmCiphers[cID]
|
|
||||||
}
|
|
||||||
if c := cipherSuiteTLS13ByID(cID); c != nil {
|
|
||||||
return aesgcmCiphers[cID]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func cipherRC4(key, iv []byte, isRead bool) any {
|
|
||||||
cipher, _ := rc4.NewCipher(key)
|
|
||||||
return cipher
|
|
||||||
}
|
|
||||||
|
|
||||||
func cipher3DES(key, iv []byte, isRead bool) any {
|
|
||||||
block, _ := des.NewTripleDESCipher(key)
|
|
||||||
if isRead {
|
|
||||||
return cipher.NewCBCDecrypter(block, iv)
|
|
||||||
}
|
|
||||||
return cipher.NewCBCEncrypter(block, iv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cipherAES(key, iv []byte, isRead bool) any {
|
|
||||||
block, _ := aes.NewCipher(key)
|
|
||||||
if isRead {
|
|
||||||
return cipher.NewCBCDecrypter(block, iv)
|
|
||||||
}
|
|
||||||
return cipher.NewCBCEncrypter(block, iv)
|
|
||||||
}
|
|
||||||
|
|
||||||
// macSHA1 returns a SHA-1 based constant time MAC.
|
|
||||||
func macSHA1(key []byte) hash.Hash {
|
|
||||||
h := sha1.New
|
|
||||||
// The BoringCrypto SHA1 does not have a constant-time
|
|
||||||
// checksum function, so don't try to use it.
|
|
||||||
// if !boring.Enabled {
|
|
||||||
h = newConstantTimeHash(h)
|
|
||||||
//}
|
|
||||||
return hmac.New(h, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// macSHA256 returns a SHA-256 based MAC. This is only supported in TLS 1.2 and
|
|
||||||
// is currently only used in disabled-by-default cipher suites.
|
|
||||||
func macSHA256(key []byte) hash.Hash {
|
|
||||||
return hmac.New(sha256.New, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
type aead interface {
|
|
||||||
cipher.AEAD
|
|
||||||
|
|
||||||
// explicitNonceLen returns the number of bytes of explicit nonce
|
|
||||||
// included in each record. This is eight for older AEADs and
|
|
||||||
// zero for modern ones.
|
|
||||||
explicitNonceLen() int
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
aeadNonceLength = 12
|
|
||||||
noncePrefixLength = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
// prefixNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
|
|
||||||
// each call.
|
|
||||||
type prefixNonceAEAD struct {
|
|
||||||
// nonce contains the fixed part of the nonce in the first four bytes.
|
|
||||||
nonce [aeadNonceLength]byte
|
|
||||||
aead cipher.AEAD
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *prefixNonceAEAD) NonceSize() int { return aeadNonceLength - noncePrefixLength }
|
|
||||||
func (f *prefixNonceAEAD) Overhead() int { return f.aead.Overhead() }
|
|
||||||
func (f *prefixNonceAEAD) explicitNonceLen() int { return f.NonceSize() }
|
|
||||||
|
|
||||||
func (f *prefixNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
|
|
||||||
copy(f.nonce[4:], nonce)
|
|
||||||
return f.aead.Seal(out, f.nonce[:], plaintext, additionalData)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *prefixNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
|
||||||
copy(f.nonce[4:], nonce)
|
|
||||||
return f.aead.Open(out, f.nonce[:], ciphertext, additionalData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// xorNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce
|
|
||||||
// before each call.
|
|
||||||
type xorNonceAEAD struct {
|
|
||||||
nonceMask [aeadNonceLength]byte
|
|
||||||
aead cipher.AEAD
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number
|
|
||||||
func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() }
|
|
||||||
func (f *xorNonceAEAD) explicitNonceLen() int { return 0 }
|
|
||||||
|
|
||||||
func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
|
|
||||||
for i, b := range nonce {
|
|
||||||
f.nonceMask[4+i] ^= b
|
|
||||||
}
|
|
||||||
result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData)
|
|
||||||
for i, b := range nonce {
|
|
||||||
f.nonceMask[4+i] ^= b
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
|
||||||
for i, b := range nonce {
|
|
||||||
f.nonceMask[4+i] ^= b
|
|
||||||
}
|
|
||||||
result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData)
|
|
||||||
for i, b := range nonce {
|
|
||||||
f.nonceMask[4+i] ^= b
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func aeadAESGCM(key, noncePrefix []byte) aead {
|
|
||||||
if len(noncePrefix) != noncePrefixLength {
|
|
||||||
panic("tls: internal error: wrong nonce length")
|
|
||||||
}
|
|
||||||
aes, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
var aead cipher.AEAD
|
|
||||||
//if boring.Enabled {
|
|
||||||
// aead, err = boring.NewGCMTLS(aes)
|
|
||||||
//} else {
|
|
||||||
// boring.Unreachable()
|
|
||||||
aead, err = cipher.NewGCM(aes)
|
|
||||||
//}
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := &prefixNonceAEAD{aead: aead}
|
|
||||||
copy(ret.nonce[:], noncePrefix)
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func aeadAESGCMTLS13(key, nonceMask []byte) aead {
|
|
||||||
if len(nonceMask) != aeadNonceLength {
|
|
||||||
panic("tls: internal error: wrong nonce length")
|
|
||||||
}
|
|
||||||
aes, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
aead, err := cipher.NewGCM(aes)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := &xorNonceAEAD{aead: aead}
|
|
||||||
copy(ret.nonceMask[:], nonceMask)
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func aeadChaCha20Poly1305(key, nonceMask []byte) aead {
|
|
||||||
if len(nonceMask) != aeadNonceLength {
|
|
||||||
panic("tls: internal error: wrong nonce length")
|
|
||||||
}
|
|
||||||
aead, err := chacha20poly1305.New(key)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := &xorNonceAEAD{aead: aead}
|
|
||||||
copy(ret.nonceMask[:], nonceMask)
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
type constantTimeHash interface {
|
|
||||||
hash.Hash
|
|
||||||
ConstantTimeSum(b []byte) []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// cthWrapper wraps any hash.Hash that implements ConstantTimeSum, and replaces
|
|
||||||
// with that all calls to Sum. It's used to obtain a ConstantTimeSum-based HMAC.
|
|
||||||
type cthWrapper struct {
|
|
||||||
h constantTimeHash
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cthWrapper) Size() int { return c.h.Size() }
|
|
||||||
func (c *cthWrapper) BlockSize() int { return c.h.BlockSize() }
|
|
||||||
func (c *cthWrapper) Reset() { c.h.Reset() }
|
|
||||||
func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) }
|
|
||||||
func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) }
|
|
||||||
|
|
||||||
func newConstantTimeHash(h func() hash.Hash) func() hash.Hash {
|
|
||||||
// boring.Unreachable()
|
|
||||||
return func() hash.Hash {
|
|
||||||
return &cthWrapper{h().(constantTimeHash)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, Section 6.2.3.
|
|
||||||
func tls10MAC(h hash.Hash, out, seq, header, data, extra []byte) []byte {
|
|
||||||
h.Reset()
|
|
||||||
h.Write(seq)
|
|
||||||
h.Write(header)
|
|
||||||
h.Write(data)
|
|
||||||
res := h.Sum(out)
|
|
||||||
if extra != nil {
|
|
||||||
h.Write(extra)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func rsaKA(version uint16) keyAgreement {
|
|
||||||
return rsaKeyAgreement{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ecdheECDSAKA(version uint16) keyAgreement {
|
|
||||||
return &ecdheKeyAgreement{
|
|
||||||
isRSA: false,
|
|
||||||
version: version,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ecdheRSAKA(version uint16) keyAgreement {
|
|
||||||
return &ecdheKeyAgreement{
|
|
||||||
isRSA: true,
|
|
||||||
version: version,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mutualCipherSuite returns a cipherSuite given a list of supported
|
|
||||||
// ciphersuites and the id requested by the peer.
|
|
||||||
func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
|
|
||||||
for _, id := range have {
|
|
||||||
if id == want {
|
|
||||||
return cipherSuiteByID(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func cipherSuiteByID(id uint16) *cipherSuite {
|
|
||||||
for _, cipherSuite := range cipherSuites {
|
|
||||||
if cipherSuite.id == id {
|
|
||||||
return cipherSuite
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mutualCipherSuiteTLS13(have []uint16, want uint16) *cipherSuiteTLS13 {
|
|
||||||
for _, id := range have {
|
|
||||||
if id == want {
|
|
||||||
return cipherSuiteTLS13ByID(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 {
|
|
||||||
for _, cipherSuite := range cipherSuitesTLS13 {
|
|
||||||
if cipherSuite.id == id {
|
|
||||||
return cipherSuite
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// A list of cipher suite IDs that are, or have been, implemented by this
|
|
||||||
// package.
|
|
||||||
//
|
|
||||||
// See https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
|
|
||||||
const (
|
|
||||||
// TLS 1.0 - 1.2 cipher suites.
|
|
||||||
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
|
|
||||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
|
|
||||||
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
|
|
||||||
TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
|
|
||||||
TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003c
|
|
||||||
TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009c
|
|
||||||
TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009d
|
|
||||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a
|
|
||||||
TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
|
|
||||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
|
|
||||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
|
|
||||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023
|
|
||||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc027
|
|
||||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
|
|
||||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030
|
|
||||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c
|
|
||||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca8
|
|
||||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9
|
|
||||||
|
|
||||||
// TLS 1.3 cipher suites.
|
|
||||||
TLS_AES_128_GCM_SHA256 uint16 = 0x1301
|
|
||||||
TLS_AES_256_GCM_SHA384 uint16 = 0x1302
|
|
||||||
TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303
|
|
||||||
|
|
||||||
// TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator
|
|
||||||
// that the client is doing version fallback. See RFC 7507.
|
|
||||||
TLS_FALLBACK_SCSV uint16 = 0x5600
|
|
||||||
|
|
||||||
// Legacy names for the corresponding cipher suites with the correct _SHA256
|
|
||||||
// suffix, retained for backward compatibility.
|
|
||||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
|
|
||||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
|
||||||
)
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,116 +0,0 @@
|
|||||||
// Code generated by "stringer -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go"; DO NOT EDIT.
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
func _() {
|
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
|
||||||
// Re-run the stringer command to generate them again.
|
|
||||||
var x [1]struct{}
|
|
||||||
_ = x[PKCS1WithSHA256-1025]
|
|
||||||
_ = x[PKCS1WithSHA384-1281]
|
|
||||||
_ = x[PKCS1WithSHA512-1537]
|
|
||||||
_ = x[PSSWithSHA256-2052]
|
|
||||||
_ = x[PSSWithSHA384-2053]
|
|
||||||
_ = x[PSSWithSHA512-2054]
|
|
||||||
_ = x[ECDSAWithP256AndSHA256-1027]
|
|
||||||
_ = x[ECDSAWithP384AndSHA384-1283]
|
|
||||||
_ = x[ECDSAWithP521AndSHA512-1539]
|
|
||||||
_ = x[Ed25519-2055]
|
|
||||||
_ = x[PKCS1WithSHA1-513]
|
|
||||||
_ = x[ECDSAWithSHA1-515]
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
_SignatureScheme_name_0 = "PKCS1WithSHA1"
|
|
||||||
_SignatureScheme_name_1 = "ECDSAWithSHA1"
|
|
||||||
_SignatureScheme_name_2 = "PKCS1WithSHA256"
|
|
||||||
_SignatureScheme_name_3 = "ECDSAWithP256AndSHA256"
|
|
||||||
_SignatureScheme_name_4 = "PKCS1WithSHA384"
|
|
||||||
_SignatureScheme_name_5 = "ECDSAWithP384AndSHA384"
|
|
||||||
_SignatureScheme_name_6 = "PKCS1WithSHA512"
|
|
||||||
_SignatureScheme_name_7 = "ECDSAWithP521AndSHA512"
|
|
||||||
_SignatureScheme_name_8 = "PSSWithSHA256PSSWithSHA384PSSWithSHA512Ed25519"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
_SignatureScheme_index_8 = [...]uint8{0, 13, 26, 39, 46}
|
|
||||||
)
|
|
||||||
|
|
||||||
func (i SignatureScheme) String() string {
|
|
||||||
switch {
|
|
||||||
case i == 513:
|
|
||||||
return _SignatureScheme_name_0
|
|
||||||
case i == 515:
|
|
||||||
return _SignatureScheme_name_1
|
|
||||||
case i == 1025:
|
|
||||||
return _SignatureScheme_name_2
|
|
||||||
case i == 1027:
|
|
||||||
return _SignatureScheme_name_3
|
|
||||||
case i == 1281:
|
|
||||||
return _SignatureScheme_name_4
|
|
||||||
case i == 1283:
|
|
||||||
return _SignatureScheme_name_5
|
|
||||||
case i == 1537:
|
|
||||||
return _SignatureScheme_name_6
|
|
||||||
case i == 1539:
|
|
||||||
return _SignatureScheme_name_7
|
|
||||||
case 2052 <= i && i <= 2055:
|
|
||||||
i -= 2052
|
|
||||||
return _SignatureScheme_name_8[_SignatureScheme_index_8[i]:_SignatureScheme_index_8[i+1]]
|
|
||||||
default:
|
|
||||||
return "SignatureScheme(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func _() {
|
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
|
||||||
// Re-run the stringer command to generate them again.
|
|
||||||
var x [1]struct{}
|
|
||||||
_ = x[CurveP256-23]
|
|
||||||
_ = x[CurveP384-24]
|
|
||||||
_ = x[CurveP521-25]
|
|
||||||
_ = x[X25519-29]
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
_CurveID_name_0 = "CurveP256CurveP384CurveP521"
|
|
||||||
_CurveID_name_1 = "X25519"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
_CurveID_index_0 = [...]uint8{0, 9, 18, 27}
|
|
||||||
)
|
|
||||||
|
|
||||||
func (i CurveID) String() string {
|
|
||||||
switch {
|
|
||||||
case 23 <= i && i <= 25:
|
|
||||||
i -= 23
|
|
||||||
return _CurveID_name_0[_CurveID_index_0[i]:_CurveID_index_0[i+1]]
|
|
||||||
case i == 29:
|
|
||||||
return _CurveID_name_1
|
|
||||||
default:
|
|
||||||
return "CurveID(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func _() {
|
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
|
||||||
// Re-run the stringer command to generate them again.
|
|
||||||
var x [1]struct{}
|
|
||||||
_ = x[NoClientCert-0]
|
|
||||||
_ = x[RequestClientCert-1]
|
|
||||||
_ = x[RequireAnyClientCert-2]
|
|
||||||
_ = x[VerifyClientCertIfGiven-3]
|
|
||||||
_ = x[RequireAndVerifyClientCert-4]
|
|
||||||
}
|
|
||||||
|
|
||||||
const _ClientAuthType_name = "NoClientCertRequestClientCertRequireAnyClientCertVerifyClientCertIfGivenRequireAndVerifyClientCert"
|
|
||||||
|
|
||||||
var _ClientAuthType_index = [...]uint8{0, 12, 29, 49, 72, 98}
|
|
||||||
|
|
||||||
func (i ClientAuthType) String() string {
|
|
||||||
if i < 0 || i >= ClientAuthType(len(_ClientAuthType_index)-1) {
|
|
||||||
return "ClientAuthType(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
||||||
return _ClientAuthType_name[_ClientAuthType_index[i]:_ClientAuthType_index[i+1]]
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,692 +0,0 @@
|
|||||||
// Copyright 2018 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/rsa"
|
|
||||||
"errors"
|
|
||||||
"hash"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"crypto/ecdh"
|
|
||||||
)
|
|
||||||
|
|
||||||
type clientHandshakeStateTLS13 struct {
|
|
||||||
c *Conn
|
|
||||||
ctx context.Context
|
|
||||||
serverHello *serverHelloMsg
|
|
||||||
hello *clientHelloMsg
|
|
||||||
ecdheKey *ecdh.PrivateKey
|
|
||||||
|
|
||||||
session *ClientSessionState
|
|
||||||
earlySecret []byte
|
|
||||||
binderKey []byte
|
|
||||||
|
|
||||||
certReq *certificateRequestMsgTLS13
|
|
||||||
usingPSK bool
|
|
||||||
sentDummyCCS bool
|
|
||||||
suite *cipherSuiteTLS13
|
|
||||||
transcript hash.Hash
|
|
||||||
masterSecret []byte
|
|
||||||
trafficSecret []byte // client_application_traffic_secret_0
|
|
||||||
}
|
|
||||||
|
|
||||||
// handshake requires hs.c, hs.hello, hs.serverHello, hs.ecdheKey, and,
|
|
||||||
// optionally, hs.session, hs.earlySecret and hs.binderKey to be set.
|
|
||||||
func (hs *clientHandshakeStateTLS13) handshake() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
if needFIPS() {
|
|
||||||
return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The server must not select TLS 1.3 in a renegotiation. See RFC 8446,
|
|
||||||
// sections 4.1.2 and 4.1.3.
|
|
||||||
if c.handshakes > 0 {
|
|
||||||
c.sendAlert(alertProtocolVersion)
|
|
||||||
return errors.New("tls: server selected TLS 1.3 in a renegotiation")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consistency check on the presence of a keyShare and its parameters.
|
|
||||||
if hs.ecdheKey == nil || len(hs.hello.keyShares) != 1 {
|
|
||||||
return c.sendAlert(alertInternalError)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := hs.checkServerHelloOrHRR(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.transcript = hs.suite.hash.New()
|
|
||||||
hs.transcript.Write(hs.hello.marshal())
|
|
||||||
|
|
||||||
if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
|
|
||||||
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.processHelloRetryRequest(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.transcript.Write(hs.serverHello.marshal())
|
|
||||||
|
|
||||||
c.buffering = true
|
|
||||||
if err := hs.processServerHello(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.establishHandshakeKeys(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.readServerParameters(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.readServerCertificate(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.readServerFinished(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.sendClientCertificate(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.sendClientFinished(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := c.flush(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.isHandshakeComplete.Store(true)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkServerHelloOrHRR does validity checks that apply to both ServerHello and
|
|
||||||
// HelloRetryRequest messages. It sets hs.suite.
|
|
||||||
func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
if hs.serverHello.supportedVersion == 0 {
|
|
||||||
c.sendAlert(alertMissingExtension)
|
|
||||||
return errors.New("tls: server selected TLS 1.3 using the legacy version field")
|
|
||||||
}
|
|
||||||
|
|
||||||
if hs.serverHello.supportedVersion != VersionTLS13 {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: server selected an invalid version after a HelloRetryRequest")
|
|
||||||
}
|
|
||||||
|
|
||||||
if hs.serverHello.vers != VersionTLS12 {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: server sent an incorrect legacy version")
|
|
||||||
}
|
|
||||||
|
|
||||||
if hs.serverHello.ocspStapling ||
|
|
||||||
hs.serverHello.ticketSupported ||
|
|
||||||
hs.serverHello.secureRenegotiationSupported ||
|
|
||||||
len(hs.serverHello.secureRenegotiation) != 0 ||
|
|
||||||
len(hs.serverHello.alpnProtocol) != 0 ||
|
|
||||||
len(hs.serverHello.scts) != 0 {
|
|
||||||
c.sendAlert(alertUnsupportedExtension)
|
|
||||||
return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: server did not echo the legacy session ID")
|
|
||||||
}
|
|
||||||
|
|
||||||
if hs.serverHello.compressionMethod != compressionNone {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: server selected unsupported compression format")
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedSuite := mutualCipherSuiteTLS13(hs.hello.cipherSuites, hs.serverHello.cipherSuite)
|
|
||||||
if hs.suite != nil && selectedSuite != hs.suite {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: server changed cipher suite after a HelloRetryRequest")
|
|
||||||
}
|
|
||||||
if selectedSuite == nil {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: server chose an unconfigured cipher suite")
|
|
||||||
}
|
|
||||||
hs.suite = selectedSuite
|
|
||||||
c.cipherSuite = hs.suite.id
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
|
|
||||||
// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
|
|
||||||
func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
|
|
||||||
if hs.sentDummyCCS {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
hs.sentDummyCCS = true
|
|
||||||
|
|
||||||
_, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// processHelloRetryRequest handles the HRR in hs.serverHello, modifies and
|
|
||||||
// resends hs.hello, and reads the new ServerHello into hs.serverHello.
|
|
||||||
func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
// The first ClientHello gets double-hashed into the transcript upon a
|
|
||||||
// HelloRetryRequest. (The idea is that the server might offload transcript
|
|
||||||
// storage to the client in the cookie.) See RFC 8446, Section 4.4.1.
|
|
||||||
chHash := hs.transcript.Sum(nil)
|
|
||||||
hs.transcript.Reset()
|
|
||||||
hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
|
||||||
hs.transcript.Write(chHash)
|
|
||||||
hs.transcript.Write(hs.serverHello.marshal())
|
|
||||||
|
|
||||||
// The only HelloRetryRequest extensions we support are key_share and
|
|
||||||
// cookie, and clients must abort the handshake if the HRR would not result
|
|
||||||
// in any change in the ClientHello.
|
|
||||||
if hs.serverHello.selectedGroup == 0 && hs.serverHello.cookie == nil {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: server sent an unnecessary HelloRetryRequest message")
|
|
||||||
}
|
|
||||||
|
|
||||||
if hs.serverHello.cookie != nil {
|
|
||||||
hs.hello.cookie = hs.serverHello.cookie
|
|
||||||
}
|
|
||||||
|
|
||||||
if hs.serverHello.serverShare.group != 0 {
|
|
||||||
c.sendAlert(alertDecodeError)
|
|
||||||
return errors.New("tls: received malformed key_share extension")
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the server sent a key_share extension selecting a group, ensure it's
|
|
||||||
// a group we advertised but did not send a key share for, and send a key
|
|
||||||
// share for it this time.
|
|
||||||
if curveID := hs.serverHello.selectedGroup; curveID != 0 {
|
|
||||||
curveOK := false
|
|
||||||
for _, id := range hs.hello.supportedCurves {
|
|
||||||
if id == curveID {
|
|
||||||
curveOK = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !curveOK {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: server selected unsupported group")
|
|
||||||
}
|
|
||||||
if sentID, _ := curveIDForCurve(hs.ecdheKey.Curve()); sentID == curveID {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
|
|
||||||
}
|
|
||||||
if _, ok := curveForCurveID(curveID); !ok {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return errors.New("tls: CurvePreferences includes unsupported curve")
|
|
||||||
}
|
|
||||||
key, err := generateECDHEKey(c.config.rand(), curveID)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hs.ecdheKey = key
|
|
||||||
hs.hello.keyShares = []keyShare{{group: curveID, data: key.PublicKey().Bytes()}}
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.hello.raw = nil
|
|
||||||
if len(hs.hello.pskIdentities) > 0 {
|
|
||||||
pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
|
|
||||||
if pskSuite == nil {
|
|
||||||
return c.sendAlert(alertInternalError)
|
|
||||||
}
|
|
||||||
if pskSuite.hash == hs.suite.hash {
|
|
||||||
// Update binders and obfuscated_ticket_age.
|
|
||||||
ticketAge := uint32(c.config.time().Sub(hs.session.receivedAt) / time.Millisecond)
|
|
||||||
hs.hello.pskIdentities[0].obfuscatedTicketAge = ticketAge + hs.session.ageAdd
|
|
||||||
|
|
||||||
transcript := hs.suite.hash.New()
|
|
||||||
transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
|
||||||
transcript.Write(chHash)
|
|
||||||
transcript.Write(hs.serverHello.marshal())
|
|
||||||
transcript.Write(hs.hello.marshalWithoutBinders())
|
|
||||||
pskBinders := [][]byte{hs.suite.finishedHash(hs.binderKey, transcript)}
|
|
||||||
hs.hello.updateBinders(pskBinders)
|
|
||||||
} else {
|
|
||||||
// Server selected a cipher suite incompatible with the PSK.
|
|
||||||
hs.hello.pskIdentities = nil
|
|
||||||
hs.hello.pskBinders = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.transcript.Write(hs.hello.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
serverHello, ok := msg.(*serverHelloMsg)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return unexpectedMessageError(serverHello, msg)
|
|
||||||
}
|
|
||||||
hs.serverHello = serverHello
|
|
||||||
|
|
||||||
if err := hs.checkServerHelloOrHRR(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *clientHandshakeStateTLS13) processServerHello() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return errors.New("tls: server sent two HelloRetryRequest messages")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(hs.serverHello.cookie) != 0 {
|
|
||||||
c.sendAlert(alertUnsupportedExtension)
|
|
||||||
return errors.New("tls: server sent a cookie in a normal ServerHello")
|
|
||||||
}
|
|
||||||
|
|
||||||
if hs.serverHello.selectedGroup != 0 {
|
|
||||||
c.sendAlert(alertDecodeError)
|
|
||||||
return errors.New("tls: malformed key_share extension")
|
|
||||||
}
|
|
||||||
|
|
||||||
if hs.serverHello.serverShare.group == 0 {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: server did not send a key share")
|
|
||||||
}
|
|
||||||
if sentID, _ := curveIDForCurve(hs.ecdheKey.Curve()); hs.serverHello.serverShare.group != sentID {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: server selected unsupported group")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hs.serverHello.selectedIdentityPresent {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: server selected an invalid PSK")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(hs.hello.pskIdentities) != 1 || hs.session == nil {
|
|
||||||
return c.sendAlert(alertInternalError)
|
|
||||||
}
|
|
||||||
pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
|
|
||||||
if pskSuite == nil {
|
|
||||||
return c.sendAlert(alertInternalError)
|
|
||||||
}
|
|
||||||
if pskSuite.hash != hs.suite.hash {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: server selected an invalid PSK and cipher suite pair")
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.usingPSK = true
|
|
||||||
c.didResume = true
|
|
||||||
c.peerCertificates = hs.session.serverCertificates
|
|
||||||
c.verifiedChains = hs.session.verifiedChains
|
|
||||||
c.ocspResponse = hs.session.ocspResponse
|
|
||||||
c.scts = hs.session.scts
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
peerKey, err := hs.ecdheKey.Curve().NewPublicKey(hs.serverHello.serverShare.data)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: invalid server key share")
|
|
||||||
}
|
|
||||||
sharedKey, err := hs.ecdheKey.ECDH(peerKey)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: invalid server key share")
|
|
||||||
}
|
|
||||||
|
|
||||||
earlySecret := hs.earlySecret
|
|
||||||
if !hs.usingPSK {
|
|
||||||
earlySecret = hs.suite.extract(nil, nil)
|
|
||||||
}
|
|
||||||
handshakeSecret := hs.suite.extract(sharedKey,
|
|
||||||
hs.suite.deriveSecret(earlySecret, "derived", nil))
|
|
||||||
|
|
||||||
clientSecret := hs.suite.deriveSecret(handshakeSecret,
|
|
||||||
clientHandshakeTrafficLabel, hs.transcript)
|
|
||||||
c.out.setTrafficSecret(hs.suite, clientSecret)
|
|
||||||
serverSecret := hs.suite.deriveSecret(handshakeSecret,
|
|
||||||
serverHandshakeTrafficLabel, hs.transcript)
|
|
||||||
c.in.setTrafficSecret(hs.suite, serverSecret)
|
|
||||||
|
|
||||||
err = c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.hello.random, serverSecret)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.masterSecret = hs.suite.extract(nil,
|
|
||||||
hs.suite.deriveSecret(handshakeSecret, "derived", nil))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *clientHandshakeStateTLS13) readServerParameters() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
msg, err := c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
encryptedExtensions, ok := msg.(*encryptedExtensionsMsg)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return unexpectedMessageError(encryptedExtensions, msg)
|
|
||||||
}
|
|
||||||
hs.transcript.Write(encryptedExtensions.marshal())
|
|
||||||
|
|
||||||
if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol); err != nil {
|
|
||||||
c.sendAlert(alertUnsupportedExtension)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.clientProtocol = encryptedExtensions.alpnProtocol
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
// Either a PSK or a certificate is always used, but not both.
|
|
||||||
// See RFC 8446, Section 4.1.1.
|
|
||||||
if hs.usingPSK {
|
|
||||||
// Make sure the connection is still being verified whether or not this
|
|
||||||
// is a resumption. Resumptions currently don't reverify certificates so
|
|
||||||
// they don't call verifyServerCertificate. See Issue 31641.
|
|
||||||
if c.config.VerifyConnection != nil {
|
|
||||||
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
|
|
||||||
c.sendAlert(alertBadCertificate)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
certReq, ok := msg.(*certificateRequestMsgTLS13)
|
|
||||||
if ok {
|
|
||||||
hs.transcript.Write(certReq.marshal())
|
|
||||||
|
|
||||||
hs.certReq = certReq
|
|
||||||
|
|
||||||
msg, err = c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
certMsg, ok := msg.(*certificateMsgTLS13)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return unexpectedMessageError(certMsg, msg)
|
|
||||||
}
|
|
||||||
if len(certMsg.certificate.Certificate) == 0 {
|
|
||||||
c.sendAlert(alertDecodeError)
|
|
||||||
return errors.New("tls: received empty certificates message")
|
|
||||||
}
|
|
||||||
hs.transcript.Write(certMsg.marshal())
|
|
||||||
|
|
||||||
c.scts = certMsg.certificate.SignedCertificateTimestamps
|
|
||||||
c.ocspResponse = certMsg.certificate.OCSPStaple
|
|
||||||
|
|
||||||
if err := c.verifyServerCertificate(certMsg.certificate.Certificate); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err = c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
certVerify, ok := msg.(*certificateVerifyMsg)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return unexpectedMessageError(certVerify, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// See RFC 8446, Section 4.4.3.
|
|
||||||
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: certificate used with invalid signature algorithm")
|
|
||||||
}
|
|
||||||
sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
|
|
||||||
if err != nil {
|
|
||||||
return c.sendAlert(alertInternalError)
|
|
||||||
}
|
|
||||||
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: certificate used with invalid signature algorithm")
|
|
||||||
}
|
|
||||||
signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
|
|
||||||
if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
|
|
||||||
sigHash, signed, certVerify.signature); err != nil {
|
|
||||||
c.sendAlert(alertDecryptError)
|
|
||||||
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.transcript.Write(certVerify.marshal())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *clientHandshakeStateTLS13) readServerFinished() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
msg, err := c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
finished, ok := msg.(*finishedMsg)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return unexpectedMessageError(finished, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedMAC := hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
|
|
||||||
if !hmac.Equal(expectedMAC, finished.verifyData) {
|
|
||||||
c.sendAlert(alertDecryptError)
|
|
||||||
return errors.New("tls: invalid server finished hash")
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.transcript.Write(finished.marshal())
|
|
||||||
|
|
||||||
// Derive secrets that take context through the server Finished.
|
|
||||||
|
|
||||||
hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
|
|
||||||
clientApplicationTrafficLabel, hs.transcript)
|
|
||||||
serverSecret := hs.suite.deriveSecret(hs.masterSecret,
|
|
||||||
serverApplicationTrafficLabel, hs.transcript)
|
|
||||||
c.in.setTrafficSecret(hs.suite, serverSecret)
|
|
||||||
|
|
||||||
err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.hello.random, serverSecret)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *clientHandshakeStateTLS13) sendClientCertificate() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
if hs.certReq == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
cert, err := c.getClientCertificate(&CertificateRequestInfo{
|
|
||||||
AcceptableCAs: hs.certReq.certificateAuthorities,
|
|
||||||
SignatureSchemes: hs.certReq.supportedSignatureAlgorithms,
|
|
||||||
Version: c.vers,
|
|
||||||
ctx: hs.ctx,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
certMsg := new(certificateMsgTLS13)
|
|
||||||
|
|
||||||
certMsg.certificate = *cert
|
|
||||||
certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0
|
|
||||||
certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0
|
|
||||||
|
|
||||||
hs.transcript.Write(certMsg.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we sent an empty certificate message, skip the CertificateVerify.
|
|
||||||
if len(cert.Certificate) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
certVerifyMsg := new(certificateVerifyMsg)
|
|
||||||
certVerifyMsg.hasSignatureAlgorithm = true
|
|
||||||
|
|
||||||
certVerifyMsg.signatureAlgorithm, err = selectSignatureScheme(c.vers, cert, hs.certReq.supportedSignatureAlgorithms)
|
|
||||||
if err != nil {
|
|
||||||
// getClientCertificate returned a certificate incompatible with the
|
|
||||||
// CertificateRequestInfo supported signature algorithms.
|
|
||||||
c.sendAlert(alertHandshakeFailure)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerifyMsg.signatureAlgorithm)
|
|
||||||
if err != nil {
|
|
||||||
return c.sendAlert(alertInternalError)
|
|
||||||
}
|
|
||||||
|
|
||||||
signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
|
|
||||||
signOpts := crypto.SignerOpts(sigHash)
|
|
||||||
if sigType == signatureRSAPSS {
|
|
||||||
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
|
|
||||||
}
|
|
||||||
sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return errors.New("tls: failed to sign handshake: " + err.Error())
|
|
||||||
}
|
|
||||||
certVerifyMsg.signature = sig
|
|
||||||
|
|
||||||
hs.transcript.Write(certVerifyMsg.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *clientHandshakeStateTLS13) sendClientFinished() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
finished := &finishedMsg{
|
|
||||||
verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.transcript.Write(finished.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.out.setTrafficSecret(hs.suite, hs.trafficSecret)
|
|
||||||
|
|
||||||
if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil {
|
|
||||||
c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
|
|
||||||
resumptionLabel, hs.transcript)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error {
|
|
||||||
if !c.isClient {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return errors.New("tls: received new session ticket from a client")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// See RFC 8446, Section 4.6.1.
|
|
||||||
if msg.lifetime == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
lifetime := time.Duration(msg.lifetime) * time.Second
|
|
||||||
if lifetime > maxSessionTicketLifetime {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: received a session ticket with invalid lifetime")
|
|
||||||
}
|
|
||||||
|
|
||||||
cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
|
|
||||||
if cipherSuite == nil || c.resumptionSecret == nil {
|
|
||||||
return c.sendAlert(alertInternalError)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the resumption_master_secret and nonce instead of deriving the PSK
|
|
||||||
// to do the least amount of work on NewSessionTicket messages before we
|
|
||||||
// know if the ticket will be used. Forward secrecy of resumed connections
|
|
||||||
// is guaranteed by the requirement for pskModeDHE.
|
|
||||||
session := &ClientSessionState{
|
|
||||||
sessionTicket: msg.label,
|
|
||||||
vers: c.vers,
|
|
||||||
cipherSuite: c.cipherSuite,
|
|
||||||
masterSecret: c.resumptionSecret,
|
|
||||||
serverCertificates: c.peerCertificates,
|
|
||||||
verifiedChains: c.verifiedChains,
|
|
||||||
receivedAt: c.config.time(),
|
|
||||||
nonce: msg.nonce,
|
|
||||||
useBy: c.config.time().Add(lifetime),
|
|
||||||
ageAdd: msg.ageAdd,
|
|
||||||
ocspResponse: c.ocspResponse,
|
|
||||||
scts: c.scts,
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
|
|
||||||
c.config.ClientSessionCache.Put(cacheKey, session)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,880 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/ed25519"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/subtle"
|
|
||||||
"crypto/x509"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// serverHandshakeState contains details of a server handshake in progress.
|
|
||||||
// It's discarded once the handshake has completed.
|
|
||||||
type serverHandshakeState struct {
|
|
||||||
c *Conn
|
|
||||||
ctx context.Context
|
|
||||||
clientHello *clientHelloMsg
|
|
||||||
hello *serverHelloMsg
|
|
||||||
suite *cipherSuite
|
|
||||||
ecdheOk bool
|
|
||||||
ecSignOk bool
|
|
||||||
rsaDecryptOk bool
|
|
||||||
rsaSignOk bool
|
|
||||||
sessionState *sessionState
|
|
||||||
finishedHash finishedHash
|
|
||||||
masterSecret []byte
|
|
||||||
cert *Certificate
|
|
||||||
}
|
|
||||||
|
|
||||||
// serverHandshake performs a TLS handshake as a server.
|
|
||||||
func (c *Conn) serverHandshake(ctx context.Context) error {
|
|
||||||
clientHello, err := c.readClientHello(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.vers == VersionTLS13 {
|
|
||||||
hs := serverHandshakeStateTLS13{
|
|
||||||
c: c,
|
|
||||||
ctx: ctx,
|
|
||||||
clientHello: clientHello,
|
|
||||||
}
|
|
||||||
return hs.handshake()
|
|
||||||
}
|
|
||||||
|
|
||||||
hs := serverHandshakeState{
|
|
||||||
c: c,
|
|
||||||
ctx: ctx,
|
|
||||||
clientHello: clientHello,
|
|
||||||
}
|
|
||||||
return hs.handshake()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeState) handshake() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
if err := hs.processClientHello(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// For an overview of TLS handshaking, see RFC 5246, Section 7.3.
|
|
||||||
c.buffering = true
|
|
||||||
if hs.checkForResumption() {
|
|
||||||
// The client has included a session ticket and so we do an abbreviated handshake.
|
|
||||||
c.didResume = true
|
|
||||||
if err := hs.doResumeHandshake(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.establishKeys(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.sendSessionTicket(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.sendFinished(c.serverFinished[:]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := c.flush(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.clientFinishedIsFirst = false
|
|
||||||
if err := hs.readFinished(nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The client didn't include a session ticket, or it wasn't
|
|
||||||
// valid so we do a full handshake.
|
|
||||||
if err := hs.pickCipherSuite(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.doFullHandshake(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.establishKeys(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.readFinished(c.clientFinished[:]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.clientFinishedIsFirst = true
|
|
||||||
c.buffering = true
|
|
||||||
if err := hs.sendSessionTicket(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.sendFinished(nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := c.flush(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random)
|
|
||||||
c.isHandshakeComplete.Store(true)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// readClientHello reads a ClientHello message and selects the protocol version.
|
|
||||||
func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) {
|
|
||||||
msg, err := c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
clientHello, ok := msg.(*clientHelloMsg)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return nil, unexpectedMessageError(clientHello, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
var configForClient *Config
|
|
||||||
originalConfig := c.config
|
|
||||||
if c.config.GetConfigForClient != nil {
|
|
||||||
chi := clientHelloInfo(ctx, c, clientHello)
|
|
||||||
if configForClient, err = c.config.GetConfigForClient(chi); err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return nil, err
|
|
||||||
} else if configForClient != nil {
|
|
||||||
c.config = configForClient
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.ticketKeys = originalConfig.ticketKeys(configForClient)
|
|
||||||
|
|
||||||
clientVersions := clientHello.supportedVersions
|
|
||||||
if len(clientHello.supportedVersions) == 0 {
|
|
||||||
clientVersions = supportedVersionsFromMax(clientHello.vers)
|
|
||||||
}
|
|
||||||
c.vers, ok = c.config.mutualVersion(roleServer, clientVersions)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertProtocolVersion)
|
|
||||||
return nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
|
|
||||||
}
|
|
||||||
c.haveVers = true
|
|
||||||
c.in.version = c.vers
|
|
||||||
c.out.version = c.vers
|
|
||||||
|
|
||||||
return clientHello, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeState) processClientHello() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
hs.hello = new(serverHelloMsg)
|
|
||||||
hs.hello.vers = c.vers
|
|
||||||
|
|
||||||
foundCompression := false
|
|
||||||
// We only support null compression, so check that the client offered it.
|
|
||||||
for _, compression := range hs.clientHello.compressionMethods {
|
|
||||||
if compression == compressionNone {
|
|
||||||
foundCompression = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !foundCompression {
|
|
||||||
c.sendAlert(alertHandshakeFailure)
|
|
||||||
return errors.New("tls: client does not support uncompressed connections")
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.hello.random = make([]byte, 32)
|
|
||||||
serverRandom := hs.hello.random
|
|
||||||
// Downgrade protection canaries. See RFC 8446, Section 4.1.3.
|
|
||||||
maxVers := c.config.maxSupportedVersion(roleServer)
|
|
||||||
if maxVers >= VersionTLS12 && c.vers < maxVers || testingOnlyForceDowngradeCanary {
|
|
||||||
if c.vers == VersionTLS12 {
|
|
||||||
copy(serverRandom[24:], downgradeCanaryTLS12)
|
|
||||||
} else {
|
|
||||||
copy(serverRandom[24:], downgradeCanaryTLS11)
|
|
||||||
}
|
|
||||||
serverRandom = serverRandom[:24]
|
|
||||||
}
|
|
||||||
_, err := io.ReadFull(c.config.rand(), serverRandom)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(hs.clientHello.secureRenegotiation) != 0 {
|
|
||||||
c.sendAlert(alertHandshakeFailure)
|
|
||||||
return errors.New("tls: initial handshake had non-empty renegotiation extension")
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.hello.secureRenegotiationSupported = hs.clientHello.secureRenegotiationSupported
|
|
||||||
hs.hello.compressionMethod = compressionNone
|
|
||||||
if len(hs.clientHello.serverName) > 0 {
|
|
||||||
c.serverName = hs.clientHello.serverName
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertNoApplicationProtocol)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hs.hello.alpnProtocol = selectedProto
|
|
||||||
c.clientProtocol = selectedProto
|
|
||||||
|
|
||||||
hs.cert, err = c.config.getCertificate(clientHelloInfo(hs.ctx, c, hs.clientHello))
|
|
||||||
if err != nil {
|
|
||||||
if err == errNoCertificates {
|
|
||||||
c.sendAlert(alertUnrecognizedName)
|
|
||||||
} else {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if hs.clientHello.scts {
|
|
||||||
hs.hello.scts = hs.cert.SignedCertificateTimestamps
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.ecdheOk = supportsECDHE(c.config, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints)
|
|
||||||
|
|
||||||
if hs.ecdheOk && len(hs.clientHello.supportedPoints) > 0 {
|
|
||||||
// Although omitting the ec_point_formats extension is permitted, some
|
|
||||||
// old OpenSSL version will refuse to handshake if not present.
|
|
||||||
//
|
|
||||||
// Per RFC 4492, section 5.1.2, implementations MUST support the
|
|
||||||
// uncompressed point format. See golang.org/issue/31943.
|
|
||||||
hs.hello.supportedPoints = []uint8{pointFormatUncompressed}
|
|
||||||
}
|
|
||||||
|
|
||||||
if priv, ok := hs.cert.PrivateKey.(crypto.Signer); ok {
|
|
||||||
switch priv.Public().(type) {
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
hs.ecSignOk = true
|
|
||||||
case ed25519.PublicKey:
|
|
||||||
hs.ecSignOk = true
|
|
||||||
case *rsa.PublicKey:
|
|
||||||
hs.rsaSignOk = true
|
|
||||||
default:
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if priv, ok := hs.cert.PrivateKey.(crypto.Decrypter); ok {
|
|
||||||
switch priv.Public().(type) {
|
|
||||||
case *rsa.PublicKey:
|
|
||||||
hs.rsaDecryptOk = true
|
|
||||||
default:
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return fmt.Errorf("tls: unsupported decryption key type (%T)", priv.Public())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// negotiateALPN picks a shared ALPN protocol that both sides support in server
|
|
||||||
// preference order. If ALPN is not configured or the peer doesn't support it,
|
|
||||||
// it returns "" and no error.
|
|
||||||
func negotiateALPN(serverProtos, clientProtos []string) (string, error) {
|
|
||||||
if len(serverProtos) == 0 || len(clientProtos) == 0 {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
var http11fallback bool
|
|
||||||
for _, s := range serverProtos {
|
|
||||||
for _, c := range clientProtos {
|
|
||||||
if s == c {
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
if s == "h2" && c == "http/1.1" {
|
|
||||||
http11fallback = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// As a special case, let http/1.1 clients connect to h2 servers as if they
|
|
||||||
// didn't support ALPN. We used not to enforce protocol overlap, so over
|
|
||||||
// time a number of HTTP servers were configured with only "h2", but
|
|
||||||
// expected to accept connections from "http/1.1" clients. See Issue 46310.
|
|
||||||
if http11fallback {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("tls: client requested unsupported application protocols (%s)", clientProtos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// supportsECDHE returns whether ECDHE key exchanges can be used with this
|
|
||||||
// pre-TLS 1.3 client.
|
|
||||||
func supportsECDHE(c *Config, supportedCurves []CurveID, supportedPoints []uint8) bool {
|
|
||||||
supportsCurve := false
|
|
||||||
for _, curve := range supportedCurves {
|
|
||||||
if c.supportsCurve(curve) {
|
|
||||||
supportsCurve = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
supportsPointFormat := false
|
|
||||||
for _, pointFormat := range supportedPoints {
|
|
||||||
if pointFormat == pointFormatUncompressed {
|
|
||||||
supportsPointFormat = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Per RFC 8422, Section 5.1.2, if the Supported Point Formats extension is
|
|
||||||
// missing, uncompressed points are supported. If supportedPoints is empty,
|
|
||||||
// the extension must be missing, as an empty extension body is rejected by
|
|
||||||
// the parser. See https://go.dev/issue/49126.
|
|
||||||
if len(supportedPoints) == 0 {
|
|
||||||
supportsPointFormat = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return supportsCurve && supportsPointFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeState) pickCipherSuite() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
preferenceOrder := cipherSuitesPreferenceOrder
|
|
||||||
if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) {
|
|
||||||
preferenceOrder = cipherSuitesPreferenceOrderNoAES
|
|
||||||
}
|
|
||||||
|
|
||||||
configCipherSuites := c.config.cipherSuites()
|
|
||||||
preferenceList := make([]uint16, 0, len(configCipherSuites))
|
|
||||||
for _, suiteID := range preferenceOrder {
|
|
||||||
for _, id := range configCipherSuites {
|
|
||||||
if id == suiteID {
|
|
||||||
preferenceList = append(preferenceList, id)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.suite = selectCipherSuite(preferenceList, hs.clientHello.cipherSuites, hs.cipherSuiteOk)
|
|
||||||
if hs.suite == nil {
|
|
||||||
c.sendAlert(alertHandshakeFailure)
|
|
||||||
return errors.New("tls: no cipher suite supported by both client and server")
|
|
||||||
}
|
|
||||||
c.cipherSuite = hs.suite.id
|
|
||||||
|
|
||||||
for _, id := range hs.clientHello.cipherSuites {
|
|
||||||
if id == TLS_FALLBACK_SCSV {
|
|
||||||
// The client is doing a fallback connection. See RFC 7507.
|
|
||||||
if hs.clientHello.vers < c.config.maxSupportedVersion(roleServer) {
|
|
||||||
c.sendAlert(alertInappropriateFallback)
|
|
||||||
return errors.New("tls: client using inappropriate protocol fallback")
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeState) cipherSuiteOk(c *cipherSuite) bool {
|
|
||||||
if c.flags&suiteECDHE != 0 {
|
|
||||||
if !hs.ecdheOk {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if c.flags&suiteECSign != 0 {
|
|
||||||
if !hs.ecSignOk {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} else if !hs.rsaSignOk {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} else if !hs.rsaDecryptOk {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if hs.c.vers < VersionTLS12 && c.flags&suiteTLS12 != 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkForResumption reports whether we should perform resumption on this connection.
|
|
||||||
func (hs *serverHandshakeState) checkForResumption() bool {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
if c.config.SessionTicketsDisabled {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
plaintext, usedOldKey := c.decryptTicket(hs.clientHello.sessionTicket)
|
|
||||||
if plaintext == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
hs.sessionState = &sessionState{usedOldKey: usedOldKey}
|
|
||||||
ok := hs.sessionState.unmarshal(plaintext)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
createdAt := time.Unix(int64(hs.sessionState.createdAt), 0)
|
|
||||||
if c.config.time().Sub(createdAt) > maxSessionTicketLifetime {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Never resume a session for a different TLS version.
|
|
||||||
if c.vers != hs.sessionState.vers {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
cipherSuiteOk := false
|
|
||||||
// Check that the client is still offering the ciphersuite in the session.
|
|
||||||
for _, id := range hs.clientHello.cipherSuites {
|
|
||||||
if id == hs.sessionState.cipherSuite {
|
|
||||||
cipherSuiteOk = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !cipherSuiteOk {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that we also support the ciphersuite from the session.
|
|
||||||
hs.suite = selectCipherSuite([]uint16{hs.sessionState.cipherSuite},
|
|
||||||
c.config.cipherSuites(), hs.cipherSuiteOk)
|
|
||||||
if hs.suite == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionHasClientCerts := len(hs.sessionState.certificates) != 0
|
|
||||||
needClientCerts := requiresClientCert(c.config.ClientAuth)
|
|
||||||
if needClientCerts && !sessionHasClientCerts {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeState) doResumeHandshake() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
hs.hello.cipherSuite = hs.suite.id
|
|
||||||
c.cipherSuite = hs.suite.id
|
|
||||||
// We echo the client's session ID in the ServerHello to let it know
|
|
||||||
// that we're doing a resumption.
|
|
||||||
hs.hello.sessionId = hs.clientHello.sessionId
|
|
||||||
hs.hello.ticketSupported = hs.sessionState.usedOldKey
|
|
||||||
hs.finishedHash = newFinishedHash(c.vers, hs.suite)
|
|
||||||
hs.finishedHash.discardHandshakeBuffer()
|
|
||||||
hs.finishedHash.Write(hs.clientHello.marshal())
|
|
||||||
hs.finishedHash.Write(hs.hello.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.processCertsFromClient(Certificate{
|
|
||||||
Certificate: hs.sessionState.certificates,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.config.VerifyConnection != nil {
|
|
||||||
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
|
|
||||||
c.sendAlert(alertBadCertificate)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.masterSecret = hs.sessionState.masterSecret
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeState) doFullHandshake() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
|
|
||||||
hs.hello.ocspStapling = true
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled
|
|
||||||
hs.hello.cipherSuite = hs.suite.id
|
|
||||||
|
|
||||||
hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite)
|
|
||||||
if c.config.ClientAuth == NoClientCert {
|
|
||||||
// No need to keep a full record of the handshake if client
|
|
||||||
// certificates won't be used.
|
|
||||||
hs.finishedHash.discardHandshakeBuffer()
|
|
||||||
}
|
|
||||||
hs.finishedHash.Write(hs.clientHello.marshal())
|
|
||||||
hs.finishedHash.Write(hs.hello.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
certMsg := new(certificateMsg)
|
|
||||||
certMsg.certificates = hs.cert.Certificate
|
|
||||||
hs.finishedHash.Write(certMsg.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if hs.hello.ocspStapling {
|
|
||||||
certStatus := new(certificateStatusMsg)
|
|
||||||
certStatus.response = hs.cert.OCSPStaple
|
|
||||||
hs.finishedHash.Write(certStatus.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, certStatus.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
keyAgreement := hs.suite.ka(c.vers)
|
|
||||||
skx, err := keyAgreement.generateServerKeyExchange(c.config, hs.cert, hs.clientHello, hs.hello)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertHandshakeFailure)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if skx != nil {
|
|
||||||
hs.finishedHash.Write(skx.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, skx.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var certReq *certificateRequestMsg
|
|
||||||
if c.config.ClientAuth >= RequestClientCert {
|
|
||||||
// Request a client certificate
|
|
||||||
certReq = new(certificateRequestMsg)
|
|
||||||
certReq.certificateTypes = []byte{
|
|
||||||
byte(certTypeRSASign),
|
|
||||||
byte(certTypeECDSASign),
|
|
||||||
}
|
|
||||||
if c.vers >= VersionTLS12 {
|
|
||||||
certReq.hasSignatureAlgorithm = true
|
|
||||||
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
|
|
||||||
}
|
|
||||||
|
|
||||||
// An empty list of certificateAuthorities signals to
|
|
||||||
// the client that it may send any certificate in response
|
|
||||||
// to our request. When we know the CAs we trust, then
|
|
||||||
// we can send them down, so that the client can choose
|
|
||||||
// an appropriate certificate to give to us.
|
|
||||||
if c.config.ClientCAs != nil {
|
|
||||||
certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
|
|
||||||
}
|
|
||||||
hs.finishedHash.Write(certReq.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
helloDone := new(serverHelloDoneMsg)
|
|
||||||
hs.finishedHash.Write(helloDone.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, helloDone.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := c.flush(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var pub crypto.PublicKey // public key for client auth, if any
|
|
||||||
|
|
||||||
msg, err := c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we requested a client certificate, then the client must send a
|
|
||||||
// certificate message, even if it's empty.
|
|
||||||
if c.config.ClientAuth >= RequestClientCert {
|
|
||||||
certMsg, ok := msg.(*certificateMsg)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return unexpectedMessageError(certMsg, msg)
|
|
||||||
}
|
|
||||||
hs.finishedHash.Write(certMsg.marshal())
|
|
||||||
|
|
||||||
if err := c.processCertsFromClient(Certificate{
|
|
||||||
Certificate: certMsg.certificates,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(certMsg.certificates) != 0 {
|
|
||||||
pub = c.peerCertificates[0].PublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err = c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.config.VerifyConnection != nil {
|
|
||||||
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
|
|
||||||
c.sendAlert(alertBadCertificate)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get client key exchange
|
|
||||||
ckx, ok := msg.(*clientKeyExchangeMsg)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return unexpectedMessageError(ckx, msg)
|
|
||||||
}
|
|
||||||
hs.finishedHash.Write(ckx.marshal())
|
|
||||||
|
|
||||||
preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertHandshakeFailure)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
|
|
||||||
if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.clientHello.random, hs.masterSecret); err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we received a client cert in response to our certificate request message,
|
|
||||||
// the client will send us a certificateVerifyMsg immediately after the
|
|
||||||
// clientKeyExchangeMsg. This message is a digest of all preceding
|
|
||||||
// handshake-layer messages that is signed using the private key corresponding
|
|
||||||
// to the client's certificate. This allows us to verify that the client is in
|
|
||||||
// possession of the private key of the certificate.
|
|
||||||
if len(c.peerCertificates) > 0 {
|
|
||||||
msg, err = c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
certVerify, ok := msg.(*certificateVerifyMsg)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return unexpectedMessageError(certVerify, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
var sigType uint8
|
|
||||||
var sigHash crypto.Hash
|
|
||||||
if c.vers >= VersionTLS12 {
|
|
||||||
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, certReq.supportedSignatureAlgorithms) {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: client certificate used with invalid signature algorithm")
|
|
||||||
}
|
|
||||||
sigType, sigHash, err = typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
|
|
||||||
if err != nil {
|
|
||||||
return c.sendAlert(alertInternalError)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(pub)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash)
|
|
||||||
if err := verifyHandshakeSignature(sigType, pub, sigHash, signed, certVerify.signature); err != nil {
|
|
||||||
c.sendAlert(alertDecryptError)
|
|
||||||
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.finishedHash.Write(certVerify.marshal())
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.finishedHash.discardHandshakeBuffer()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeState) establishKeys() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
|
|
||||||
|
|
||||||
var clientCipher, serverCipher any
|
|
||||||
var clientHash, serverHash hash.Hash
|
|
||||||
|
|
||||||
if hs.suite.aead == nil {
|
|
||||||
clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */)
|
|
||||||
clientHash = hs.suite.mac(clientMAC)
|
|
||||||
serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
|
|
||||||
serverHash = hs.suite.mac(serverMAC)
|
|
||||||
} else {
|
|
||||||
clientCipher = hs.suite.aead(clientKey, clientIV)
|
|
||||||
serverCipher = hs.suite.aead(serverKey, serverIV)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
|
|
||||||
c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeState) readFinished(out []byte) error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
if err := c.readChangeCipherSpec(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
clientFinished, ok := msg.(*finishedMsg)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return unexpectedMessageError(clientFinished, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
verify := hs.finishedHash.clientSum(hs.masterSecret)
|
|
||||||
if len(verify) != len(clientFinished.verifyData) ||
|
|
||||||
subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
|
|
||||||
c.sendAlert(alertHandshakeFailure)
|
|
||||||
return errors.New("tls: client's Finished message is incorrect")
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.finishedHash.Write(clientFinished.marshal())
|
|
||||||
copy(out, verify)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeState) sendSessionTicket() error {
|
|
||||||
// ticketSupported is set in a resumption handshake if the
|
|
||||||
// ticket from the client was encrypted with an old session
|
|
||||||
// ticket key and thus a refreshed ticket should be sent.
|
|
||||||
if !hs.hello.ticketSupported {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
c := hs.c
|
|
||||||
m := new(newSessionTicketMsg)
|
|
||||||
|
|
||||||
createdAt := uint64(c.config.time().Unix())
|
|
||||||
if hs.sessionState != nil {
|
|
||||||
// If this is re-wrapping an old key, then keep
|
|
||||||
// the original time it was created.
|
|
||||||
createdAt = hs.sessionState.createdAt
|
|
||||||
}
|
|
||||||
|
|
||||||
var certsFromClient [][]byte
|
|
||||||
for _, cert := range c.peerCertificates {
|
|
||||||
certsFromClient = append(certsFromClient, cert.Raw)
|
|
||||||
}
|
|
||||||
state := sessionState{
|
|
||||||
vers: c.vers,
|
|
||||||
cipherSuite: hs.suite.id,
|
|
||||||
createdAt: createdAt,
|
|
||||||
masterSecret: hs.masterSecret,
|
|
||||||
certificates: certsFromClient,
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
m.ticket, err = c.encryptTicket(state.marshal())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.finishedHash.Write(m.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeState) sendFinished(out []byte) error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
finished := new(finishedMsg)
|
|
||||||
finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
|
|
||||||
hs.finishedHash.Write(finished.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(out, finished.verifyData)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// processCertsFromClient takes a chain of client certificates either from a
|
|
||||||
// Certificates message or from a sessionState and verifies them. It returns
|
|
||||||
// the public key of the leaf certificate.
|
|
||||||
func (c *Conn) processCertsFromClient(certificate Certificate) error {
|
|
||||||
certificates := certificate.Certificate
|
|
||||||
certs := make([]*x509.Certificate, len(certificates))
|
|
||||||
var err error
|
|
||||||
for i, asn1Data := range certificates {
|
|
||||||
if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
|
|
||||||
c.sendAlert(alertBadCertificate)
|
|
||||||
return errors.New("tls: failed to parse client certificate: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(certs) == 0 && requiresClientCert(c.config.ClientAuth) {
|
|
||||||
c.sendAlert(alertBadCertificate)
|
|
||||||
return errors.New("tls: client didn't provide a certificate")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
|
|
||||||
opts := x509.VerifyOptions{
|
|
||||||
Roots: c.config.ClientCAs,
|
|
||||||
CurrentTime: c.config.time(),
|
|
||||||
Intermediates: x509.NewCertPool(),
|
|
||||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, cert := range certs[1:] {
|
|
||||||
opts.Intermediates.AddCert(cert)
|
|
||||||
}
|
|
||||||
|
|
||||||
chains, err := certs[0].Verify(opts)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertBadCertificate)
|
|
||||||
return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.verifiedChains = chains
|
|
||||||
}
|
|
||||||
|
|
||||||
c.peerCertificates = certs
|
|
||||||
c.ocspResponse = certificate.OCSPStaple
|
|
||||||
c.scts = certificate.SignedCertificateTimestamps
|
|
||||||
|
|
||||||
if len(certs) > 0 {
|
|
||||||
switch certs[0].PublicKey.(type) {
|
|
||||||
case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey:
|
|
||||||
default:
|
|
||||||
c.sendAlert(alertUnsupportedCertificate)
|
|
||||||
return fmt.Errorf("tls: client certificate contains an unsupported public key of type %T", certs[0].PublicKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.config.VerifyPeerCertificate != nil {
|
|
||||||
if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
|
|
||||||
c.sendAlert(alertBadCertificate)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func clientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo {
|
|
||||||
supportedVersions := clientHello.supportedVersions
|
|
||||||
if len(clientHello.supportedVersions) == 0 {
|
|
||||||
supportedVersions = supportedVersionsFromMax(clientHello.vers)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ClientHelloInfo{
|
|
||||||
CipherSuites: clientHello.cipherSuites,
|
|
||||||
ServerName: clientHello.serverName,
|
|
||||||
SupportedCurves: clientHello.supportedCurves,
|
|
||||||
SupportedPoints: clientHello.supportedPoints,
|
|
||||||
SignatureSchemes: clientHello.supportedSignatureAlgorithms,
|
|
||||||
SupportedProtos: clientHello.alpnProtocols,
|
|
||||||
SupportedVersions: supportedVersions,
|
|
||||||
Conn: c.conn,
|
|
||||||
config: c.config,
|
|
||||||
ctx: ctx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,880 +0,0 @@
|
|||||||
// Copyright 2018 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/rsa"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// maxClientPSKIdentities is the number of client PSK identities the server will
|
|
||||||
// attempt to validate. It will ignore the rest not to let cheap ClientHello
|
|
||||||
// messages cause too much work in session ticket decryption attempts.
|
|
||||||
const maxClientPSKIdentities = 5
|
|
||||||
|
|
||||||
type serverHandshakeStateTLS13 struct {
|
|
||||||
c *Conn
|
|
||||||
ctx context.Context
|
|
||||||
clientHello *clientHelloMsg
|
|
||||||
hello *serverHelloMsg
|
|
||||||
sentDummyCCS bool
|
|
||||||
usingPSK bool
|
|
||||||
suite *cipherSuiteTLS13
|
|
||||||
cert *Certificate
|
|
||||||
sigAlg SignatureScheme
|
|
||||||
earlySecret []byte
|
|
||||||
sharedKey []byte
|
|
||||||
handshakeSecret []byte
|
|
||||||
masterSecret []byte
|
|
||||||
trafficSecret []byte // client_application_traffic_secret_0
|
|
||||||
transcript hash.Hash
|
|
||||||
clientFinished []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeStateTLS13) handshake() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
if needFIPS() {
|
|
||||||
return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
// For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2.
|
|
||||||
if err := hs.processClientHello(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.checkForResumption(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.pickCertificate(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.buffering = true
|
|
||||||
if err := hs.sendServerParameters(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.sendServerCertificate(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.sendServerFinished(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Note that at this point we could start sending application data without
|
|
||||||
// waiting for the client's second flight, but the application might not
|
|
||||||
// expect the lack of replay protection of the ClientHello parameters.
|
|
||||||
if _, err := c.flush(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.readClientCertificate(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := hs.readClientFinished(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.isHandshakeComplete.Store(true)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeStateTLS13) processClientHello() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
hs.hello = new(serverHelloMsg)
|
|
||||||
|
|
||||||
// TLS 1.3 froze the ServerHello.legacy_version field, and uses
|
|
||||||
// supported_versions instead. See RFC 8446, sections 4.1.3 and 4.2.1.
|
|
||||||
hs.hello.vers = VersionTLS12
|
|
||||||
hs.hello.supportedVersion = c.vers
|
|
||||||
|
|
||||||
if len(hs.clientHello.supportedVersions) == 0 {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: client used the legacy version field to negotiate TLS 1.3")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Abort if the client is doing a fallback and landing lower than what we
|
|
||||||
// support. See RFC 7507, which however does not specify the interaction
|
|
||||||
// with supported_versions. The only difference is that with
|
|
||||||
// supported_versions a client has a chance to attempt a [TLS 1.2, TLS 1.4]
|
|
||||||
// handshake in case TLS 1.3 is broken but 1.2 is not. Alas, in that case,
|
|
||||||
// it will have to drop the TLS_FALLBACK_SCSV protection if it falls back to
|
|
||||||
// TLS 1.2, because a TLS 1.3 server would abort here. The situation before
|
|
||||||
// supported_versions was not better because there was just no way to do a
|
|
||||||
// TLS 1.4 handshake without risking the server selecting TLS 1.3.
|
|
||||||
for _, id := range hs.clientHello.cipherSuites {
|
|
||||||
if id == TLS_FALLBACK_SCSV {
|
|
||||||
// Use c.vers instead of max(supported_versions) because an attacker
|
|
||||||
// could defeat this by adding an arbitrary high version otherwise.
|
|
||||||
if c.vers < c.config.maxSupportedVersion(roleServer) {
|
|
||||||
c.sendAlert(alertInappropriateFallback)
|
|
||||||
return errors.New("tls: client using inappropriate protocol fallback")
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(hs.clientHello.compressionMethods) != 1 ||
|
|
||||||
hs.clientHello.compressionMethods[0] != compressionNone {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: TLS 1.3 client supports illegal compression methods")
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.hello.random = make([]byte, 32)
|
|
||||||
if _, err := io.ReadFull(c.config.rand(), hs.hello.random); err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(hs.clientHello.secureRenegotiation) != 0 {
|
|
||||||
c.sendAlert(alertHandshakeFailure)
|
|
||||||
return errors.New("tls: initial handshake had non-empty renegotiation extension")
|
|
||||||
}
|
|
||||||
|
|
||||||
if hs.clientHello.earlyData {
|
|
||||||
// See RFC 8446, Section 4.2.10 for the complicated behavior required
|
|
||||||
// here. The scenario is that a different server at our address offered
|
|
||||||
// to accept early data in the past, which we can't handle. For now, all
|
|
||||||
// 0-RTT enabled session tickets need to expire before a Go server can
|
|
||||||
// replace a server or join a pool. That's the same requirement that
|
|
||||||
// applies to mixing or replacing with any TLS 1.2 server.
|
|
||||||
c.sendAlert(alertUnsupportedExtension)
|
|
||||||
return errors.New("tls: client sent unexpected early data")
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.hello.sessionId = hs.clientHello.sessionId
|
|
||||||
hs.hello.compressionMethod = compressionNone
|
|
||||||
|
|
||||||
preferenceList := defaultCipherSuitesTLS13
|
|
||||||
if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) {
|
|
||||||
preferenceList = defaultCipherSuitesTLS13NoAES
|
|
||||||
}
|
|
||||||
for _, suiteID := range preferenceList {
|
|
||||||
hs.suite = mutualCipherSuiteTLS13(hs.clientHello.cipherSuites, suiteID)
|
|
||||||
if hs.suite != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hs.suite == nil {
|
|
||||||
c.sendAlert(alertHandshakeFailure)
|
|
||||||
return errors.New("tls: no cipher suite supported by both client and server")
|
|
||||||
}
|
|
||||||
c.cipherSuite = hs.suite.id
|
|
||||||
hs.hello.cipherSuite = hs.suite.id
|
|
||||||
hs.transcript = hs.suite.hash.New()
|
|
||||||
|
|
||||||
// Pick the ECDHE group in server preference order, but give priority to
|
|
||||||
// groups with a key share, to avoid a HelloRetryRequest round-trip.
|
|
||||||
var selectedGroup CurveID
|
|
||||||
var clientKeyShare *keyShare
|
|
||||||
GroupSelection:
|
|
||||||
for _, preferredGroup := range c.config.curvePreferences() {
|
|
||||||
for _, ks := range hs.clientHello.keyShares {
|
|
||||||
if ks.group == preferredGroup {
|
|
||||||
selectedGroup = ks.group
|
|
||||||
clientKeyShare = &ks
|
|
||||||
break GroupSelection
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if selectedGroup != 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, group := range hs.clientHello.supportedCurves {
|
|
||||||
if group == preferredGroup {
|
|
||||||
selectedGroup = group
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if selectedGroup == 0 {
|
|
||||||
c.sendAlert(alertHandshakeFailure)
|
|
||||||
return errors.New("tls: no ECDHE curve supported by both client and server")
|
|
||||||
}
|
|
||||||
if clientKeyShare == nil {
|
|
||||||
if err := hs.doHelloRetryRequest(selectedGroup); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
clientKeyShare = &hs.clientHello.keyShares[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := curveForCurveID(selectedGroup); !ok {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return errors.New("tls: CurvePreferences includes unsupported curve")
|
|
||||||
}
|
|
||||||
key, err := generateECDHEKey(c.config.rand(), selectedGroup)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hs.hello.serverShare = keyShare{group: selectedGroup, data: key.PublicKey().Bytes()}
|
|
||||||
peerKey, err := key.Curve().NewPublicKey(clientKeyShare.data)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: invalid client key share")
|
|
||||||
}
|
|
||||||
hs.sharedKey, err = key.ECDH(peerKey)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: invalid client key share")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.serverName = hs.clientHello.serverName
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeStateTLS13) checkForResumption() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
if c.config.SessionTicketsDisabled {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
modeOK := false
|
|
||||||
for _, mode := range hs.clientHello.pskModes {
|
|
||||||
if mode == pskModeDHE {
|
|
||||||
modeOK = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !modeOK {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(hs.clientHello.pskIdentities) != len(hs.clientHello.pskBinders) {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: invalid or missing PSK binders")
|
|
||||||
}
|
|
||||||
if len(hs.clientHello.pskIdentities) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, identity := range hs.clientHello.pskIdentities {
|
|
||||||
if i >= maxClientPSKIdentities {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
plaintext, _ := c.decryptTicket(identity.label)
|
|
||||||
if plaintext == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sessionState := new(sessionStateTLS13)
|
|
||||||
if ok := sessionState.unmarshal(plaintext); !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
createdAt := time.Unix(int64(sessionState.createdAt), 0)
|
|
||||||
if c.config.time().Sub(createdAt) > maxSessionTicketLifetime {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't check the obfuscated ticket age because it's affected by
|
|
||||||
// clock skew and it's only a freshness signal useful for shrinking the
|
|
||||||
// window for replay attacks, which don't affect us as we don't do 0-RTT.
|
|
||||||
|
|
||||||
pskSuite := cipherSuiteTLS13ByID(sessionState.cipherSuite)
|
|
||||||
if pskSuite == nil || pskSuite.hash != hs.suite.hash {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// PSK connections don't re-establish client certificates, but carry
|
|
||||||
// them over in the session ticket. Ensure the presence of client certs
|
|
||||||
// in the ticket is consistent with the configured requirements.
|
|
||||||
sessionHasClientCerts := len(sessionState.certificate.Certificate) != 0
|
|
||||||
needClientCerts := requiresClientCert(c.config.ClientAuth)
|
|
||||||
if needClientCerts && !sessionHasClientCerts {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
psk := hs.suite.expandLabel(sessionState.resumptionSecret, "resumption",
|
|
||||||
nil, hs.suite.hash.Size())
|
|
||||||
hs.earlySecret = hs.suite.extract(psk, nil)
|
|
||||||
binderKey := hs.suite.deriveSecret(hs.earlySecret, resumptionBinderLabel, nil)
|
|
||||||
// Clone the transcript in case a HelloRetryRequest was recorded.
|
|
||||||
transcript := cloneHash(hs.transcript, hs.suite.hash)
|
|
||||||
if transcript == nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return errors.New("tls: internal error: failed to clone hash")
|
|
||||||
}
|
|
||||||
transcript.Write(hs.clientHello.marshalWithoutBinders())
|
|
||||||
pskBinder := hs.suite.finishedHash(binderKey, transcript)
|
|
||||||
if !hmac.Equal(hs.clientHello.pskBinders[i], pskBinder) {
|
|
||||||
c.sendAlert(alertDecryptError)
|
|
||||||
return errors.New("tls: invalid PSK binder")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.didResume = true
|
|
||||||
if err := c.processCertsFromClient(sessionState.certificate); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.hello.selectedIdentityPresent = true
|
|
||||||
hs.hello.selectedIdentity = uint16(i)
|
|
||||||
hs.usingPSK = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// cloneHash uses the encoding.BinaryMarshaler and encoding.BinaryUnmarshaler
|
|
||||||
// interfaces implemented by standard library hashes to clone the state of in
|
|
||||||
// to a new instance of h. It returns nil if the operation fails.
|
|
||||||
func cloneHash(in hash.Hash, h crypto.Hash) hash.Hash {
|
|
||||||
// Recreate the interface to avoid importing encoding.
|
|
||||||
type binaryMarshaler interface {
|
|
||||||
MarshalBinary() (data []byte, err error)
|
|
||||||
UnmarshalBinary(data []byte) error
|
|
||||||
}
|
|
||||||
marshaler, ok := in.(binaryMarshaler)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
state, err := marshaler.MarshalBinary()
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := h.New()
|
|
||||||
unmarshaler, ok := out.(binaryMarshaler)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := unmarshaler.UnmarshalBinary(state); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeStateTLS13) pickCertificate() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
// Only one of PSK and certificates are used at a time.
|
|
||||||
if hs.usingPSK {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// signature_algorithms is required in TLS 1.3. See RFC 8446, Section 4.2.3.
|
|
||||||
if len(hs.clientHello.supportedSignatureAlgorithms) == 0 {
|
|
||||||
return c.sendAlert(alertMissingExtension)
|
|
||||||
}
|
|
||||||
|
|
||||||
certificate, err := c.config.getCertificate(clientHelloInfo(hs.ctx, c, hs.clientHello))
|
|
||||||
if err != nil {
|
|
||||||
if err == errNoCertificates {
|
|
||||||
c.sendAlert(alertUnrecognizedName)
|
|
||||||
} else {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hs.sigAlg, err = selectSignatureScheme(c.vers, certificate, hs.clientHello.supportedSignatureAlgorithms)
|
|
||||||
if err != nil {
|
|
||||||
// getCertificate returned a certificate that is unsupported or
|
|
||||||
// incompatible with the client's signature algorithms.
|
|
||||||
c.sendAlert(alertHandshakeFailure)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hs.cert = certificate
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
|
|
||||||
// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
|
|
||||||
func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
|
|
||||||
if hs.sentDummyCCS {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
hs.sentDummyCCS = true
|
|
||||||
|
|
||||||
_, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
// The first ClientHello gets double-hashed into the transcript upon a
|
|
||||||
// HelloRetryRequest. See RFC 8446, Section 4.4.1.
|
|
||||||
hs.transcript.Write(hs.clientHello.marshal())
|
|
||||||
chHash := hs.transcript.Sum(nil)
|
|
||||||
hs.transcript.Reset()
|
|
||||||
hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
|
||||||
hs.transcript.Write(chHash)
|
|
||||||
|
|
||||||
helloRetryRequest := &serverHelloMsg{
|
|
||||||
vers: hs.hello.vers,
|
|
||||||
random: helloRetryRequestRandom,
|
|
||||||
sessionId: hs.hello.sessionId,
|
|
||||||
cipherSuite: hs.hello.cipherSuite,
|
|
||||||
compressionMethod: hs.hello.compressionMethod,
|
|
||||||
supportedVersion: hs.hello.supportedVersion,
|
|
||||||
selectedGroup: selectedGroup,
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.transcript.Write(helloRetryRequest.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
clientHello, ok := msg.(*clientHelloMsg)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return unexpectedMessageError(clientHello, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(clientHello.keyShares) != 1 || clientHello.keyShares[0].group != selectedGroup {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: client sent invalid key share in second ClientHello")
|
|
||||||
}
|
|
||||||
|
|
||||||
if clientHello.earlyData {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: client indicated early data in second ClientHello")
|
|
||||||
}
|
|
||||||
|
|
||||||
if illegalClientHelloChange(clientHello, hs.clientHello) {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: client illegally modified second ClientHello")
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.clientHello = clientHello
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// illegalClientHelloChange reports whether the two ClientHello messages are
|
|
||||||
// different, with the exception of the changes allowed before and after a
|
|
||||||
// HelloRetryRequest. See RFC 8446, Section 4.1.2.
|
|
||||||
func illegalClientHelloChange(ch, ch1 *clientHelloMsg) bool {
|
|
||||||
if len(ch.supportedVersions) != len(ch1.supportedVersions) ||
|
|
||||||
len(ch.cipherSuites) != len(ch1.cipherSuites) ||
|
|
||||||
len(ch.supportedCurves) != len(ch1.supportedCurves) ||
|
|
||||||
len(ch.supportedSignatureAlgorithms) != len(ch1.supportedSignatureAlgorithms) ||
|
|
||||||
len(ch.supportedSignatureAlgorithmsCert) != len(ch1.supportedSignatureAlgorithmsCert) ||
|
|
||||||
len(ch.alpnProtocols) != len(ch1.alpnProtocols) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for i := range ch.supportedVersions {
|
|
||||||
if ch.supportedVersions[i] != ch1.supportedVersions[i] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := range ch.cipherSuites {
|
|
||||||
if ch.cipherSuites[i] != ch1.cipherSuites[i] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := range ch.supportedCurves {
|
|
||||||
if ch.supportedCurves[i] != ch1.supportedCurves[i] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := range ch.supportedSignatureAlgorithms {
|
|
||||||
if ch.supportedSignatureAlgorithms[i] != ch1.supportedSignatureAlgorithms[i] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := range ch.supportedSignatureAlgorithmsCert {
|
|
||||||
if ch.supportedSignatureAlgorithmsCert[i] != ch1.supportedSignatureAlgorithmsCert[i] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := range ch.alpnProtocols {
|
|
||||||
if ch.alpnProtocols[i] != ch1.alpnProtocols[i] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ch.vers != ch1.vers ||
|
|
||||||
!bytes.Equal(ch.random, ch1.random) ||
|
|
||||||
!bytes.Equal(ch.sessionId, ch1.sessionId) ||
|
|
||||||
!bytes.Equal(ch.compressionMethods, ch1.compressionMethods) ||
|
|
||||||
ch.serverName != ch1.serverName ||
|
|
||||||
ch.ocspStapling != ch1.ocspStapling ||
|
|
||||||
!bytes.Equal(ch.supportedPoints, ch1.supportedPoints) ||
|
|
||||||
ch.ticketSupported != ch1.ticketSupported ||
|
|
||||||
!bytes.Equal(ch.sessionTicket, ch1.sessionTicket) ||
|
|
||||||
ch.secureRenegotiationSupported != ch1.secureRenegotiationSupported ||
|
|
||||||
!bytes.Equal(ch.secureRenegotiation, ch1.secureRenegotiation) ||
|
|
||||||
ch.scts != ch1.scts ||
|
|
||||||
!bytes.Equal(ch.cookie, ch1.cookie) ||
|
|
||||||
!bytes.Equal(ch.pskModes, ch1.pskModes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
hs.transcript.Write(hs.clientHello.marshal())
|
|
||||||
hs.transcript.Write(hs.hello.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
earlySecret := hs.earlySecret
|
|
||||||
if earlySecret == nil {
|
|
||||||
earlySecret = hs.suite.extract(nil, nil)
|
|
||||||
}
|
|
||||||
hs.handshakeSecret = hs.suite.extract(hs.sharedKey,
|
|
||||||
hs.suite.deriveSecret(earlySecret, "derived", nil))
|
|
||||||
|
|
||||||
clientSecret := hs.suite.deriveSecret(hs.handshakeSecret,
|
|
||||||
clientHandshakeTrafficLabel, hs.transcript)
|
|
||||||
c.in.setTrafficSecret(hs.suite, clientSecret)
|
|
||||||
serverSecret := hs.suite.deriveSecret(hs.handshakeSecret,
|
|
||||||
serverHandshakeTrafficLabel, hs.transcript)
|
|
||||||
c.out.setTrafficSecret(hs.suite, serverSecret)
|
|
||||||
|
|
||||||
err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.clientHello.random, clientSecret)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.clientHello.random, serverSecret)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
encryptedExtensions := new(encryptedExtensionsMsg)
|
|
||||||
|
|
||||||
selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertNoApplicationProtocol)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
encryptedExtensions.alpnProtocol = selectedProto
|
|
||||||
c.clientProtocol = selectedProto
|
|
||||||
|
|
||||||
hs.transcript.Write(encryptedExtensions.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, encryptedExtensions.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeStateTLS13) requestClientCert() bool {
|
|
||||||
return hs.c.config.ClientAuth >= RequestClientCert && !hs.usingPSK
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeStateTLS13) sendServerCertificate() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
// Only one of PSK and certificates are used at a time.
|
|
||||||
if hs.usingPSK {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if hs.requestClientCert() {
|
|
||||||
// Request a client certificate
|
|
||||||
certReq := new(certificateRequestMsgTLS13)
|
|
||||||
certReq.ocspStapling = true
|
|
||||||
certReq.scts = true
|
|
||||||
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
|
|
||||||
if c.config.ClientCAs != nil {
|
|
||||||
certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.transcript.Write(certReq.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
certMsg := new(certificateMsgTLS13)
|
|
||||||
|
|
||||||
certMsg.certificate = *hs.cert
|
|
||||||
certMsg.scts = hs.clientHello.scts && len(hs.cert.SignedCertificateTimestamps) > 0
|
|
||||||
certMsg.ocspStapling = hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0
|
|
||||||
|
|
||||||
hs.transcript.Write(certMsg.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
certVerifyMsg := new(certificateVerifyMsg)
|
|
||||||
certVerifyMsg.hasSignatureAlgorithm = true
|
|
||||||
certVerifyMsg.signatureAlgorithm = hs.sigAlg
|
|
||||||
|
|
||||||
sigType, sigHash, err := typeAndHashFromSignatureScheme(hs.sigAlg)
|
|
||||||
if err != nil {
|
|
||||||
return c.sendAlert(alertInternalError)
|
|
||||||
}
|
|
||||||
|
|
||||||
signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
|
|
||||||
signOpts := crypto.SignerOpts(sigHash)
|
|
||||||
if sigType == signatureRSAPSS {
|
|
||||||
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
|
|
||||||
}
|
|
||||||
sig, err := hs.cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
|
|
||||||
if err != nil {
|
|
||||||
public := hs.cert.PrivateKey.(crypto.Signer).Public()
|
|
||||||
if rsaKey, ok := public.(*rsa.PublicKey); ok && sigType == signatureRSAPSS &&
|
|
||||||
rsaKey.N.BitLen()/8 < sigHash.Size()*2+2 { // key too small for RSA-PSS
|
|
||||||
c.sendAlert(alertHandshakeFailure)
|
|
||||||
} else {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
}
|
|
||||||
return errors.New("tls: failed to sign handshake: " + err.Error())
|
|
||||||
}
|
|
||||||
certVerifyMsg.signature = sig
|
|
||||||
|
|
||||||
hs.transcript.Write(certVerifyMsg.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeStateTLS13) sendServerFinished() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
finished := &finishedMsg{
|
|
||||||
verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.transcript.Write(finished.marshal())
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Derive secrets that take context through the server Finished.
|
|
||||||
|
|
||||||
hs.masterSecret = hs.suite.extract(nil,
|
|
||||||
hs.suite.deriveSecret(hs.handshakeSecret, "derived", nil))
|
|
||||||
|
|
||||||
hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
|
|
||||||
clientApplicationTrafficLabel, hs.transcript)
|
|
||||||
serverSecret := hs.suite.deriveSecret(hs.masterSecret,
|
|
||||||
serverApplicationTrafficLabel, hs.transcript)
|
|
||||||
c.out.setTrafficSecret(hs.suite, serverSecret)
|
|
||||||
|
|
||||||
err := c.config.writeKeyLog(keyLogLabelClientTraffic, hs.clientHello.random, hs.trafficSecret)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.clientHello.random, serverSecret)
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
|
|
||||||
|
|
||||||
// If we did not request client certificates, at this point we can
|
|
||||||
// precompute the client finished and roll the transcript forward to send
|
|
||||||
// session tickets in our first flight.
|
|
||||||
if !hs.requestClientCert() {
|
|
||||||
if err := hs.sendSessionTickets(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeStateTLS13) shouldSendSessionTickets() bool {
|
|
||||||
if hs.c.config.SessionTicketsDisabled {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't send tickets the client wouldn't use. See RFC 8446, Section 4.2.9.
|
|
||||||
for _, pskMode := range hs.clientHello.pskModes {
|
|
||||||
if pskMode == pskModeDHE {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeStateTLS13) sendSessionTickets() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
hs.clientFinished = hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
|
|
||||||
finishedMsg := &finishedMsg{
|
|
||||||
verifyData: hs.clientFinished,
|
|
||||||
}
|
|
||||||
hs.transcript.Write(finishedMsg.marshal())
|
|
||||||
|
|
||||||
if !hs.shouldSendSessionTickets() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
resumptionSecret := hs.suite.deriveSecret(hs.masterSecret,
|
|
||||||
resumptionLabel, hs.transcript)
|
|
||||||
|
|
||||||
m := new(newSessionTicketMsgTLS13)
|
|
||||||
|
|
||||||
var certsFromClient [][]byte
|
|
||||||
for _, cert := range c.peerCertificates {
|
|
||||||
certsFromClient = append(certsFromClient, cert.Raw)
|
|
||||||
}
|
|
||||||
state := sessionStateTLS13{
|
|
||||||
cipherSuite: hs.suite.id,
|
|
||||||
createdAt: uint64(c.config.time().Unix()),
|
|
||||||
resumptionSecret: resumptionSecret,
|
|
||||||
certificate: Certificate{
|
|
||||||
Certificate: certsFromClient,
|
|
||||||
OCSPStaple: c.ocspResponse,
|
|
||||||
SignedCertificateTimestamps: c.scts,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
m.label, err = c.encryptTicket(state.marshal())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m.lifetime = uint32(maxSessionTicketLifetime / time.Second)
|
|
||||||
|
|
||||||
// ticket_age_add is a random 32-bit value. See RFC 8446, section 4.6.1
|
|
||||||
// The value is not stored anywhere; we never need to check the ticket age
|
|
||||||
// because 0-RTT is not supported.
|
|
||||||
ageAdd := make([]byte, 4)
|
|
||||||
_, err = hs.c.config.rand().Read(ageAdd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m.ageAdd = binary.LittleEndian.Uint32(ageAdd)
|
|
||||||
|
|
||||||
// ticket_nonce, which must be unique per connection, is always left at
|
|
||||||
// zero because we only ever send one ticket per connection.
|
|
||||||
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeStateTLS13) readClientCertificate() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
if !hs.requestClientCert() {
|
|
||||||
// Make sure the connection is still being verified whether or not
|
|
||||||
// the server requested a client certificate.
|
|
||||||
if c.config.VerifyConnection != nil {
|
|
||||||
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
|
|
||||||
c.sendAlert(alertBadCertificate)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we requested a client certificate, then the client must send a
|
|
||||||
// certificate message. If it's empty, no CertificateVerify is sent.
|
|
||||||
|
|
||||||
msg, err := c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
certMsg, ok := msg.(*certificateMsgTLS13)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return unexpectedMessageError(certMsg, msg)
|
|
||||||
}
|
|
||||||
hs.transcript.Write(certMsg.marshal())
|
|
||||||
|
|
||||||
if err := c.processCertsFromClient(certMsg.certificate); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.config.VerifyConnection != nil {
|
|
||||||
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
|
|
||||||
c.sendAlert(alertBadCertificate)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(certMsg.certificate.Certificate) != 0 {
|
|
||||||
msg, err = c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
certVerify, ok := msg.(*certificateVerifyMsg)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return unexpectedMessageError(certVerify, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// See RFC 8446, Section 4.4.3.
|
|
||||||
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: client certificate used with invalid signature algorithm")
|
|
||||||
}
|
|
||||||
sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
|
|
||||||
if err != nil {
|
|
||||||
return c.sendAlert(alertInternalError)
|
|
||||||
}
|
|
||||||
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
|
||||||
return errors.New("tls: client certificate used with invalid signature algorithm")
|
|
||||||
}
|
|
||||||
signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
|
|
||||||
if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
|
|
||||||
sigHash, signed, certVerify.signature); err != nil {
|
|
||||||
c.sendAlert(alertDecryptError)
|
|
||||||
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.transcript.Write(certVerify.marshal())
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we waited until the client certificates to send session tickets, we
|
|
||||||
// are ready to do it now.
|
|
||||||
if err := hs.sendSessionTickets(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs *serverHandshakeStateTLS13) readClientFinished() error {
|
|
||||||
c := hs.c
|
|
||||||
|
|
||||||
msg, err := c.readHandshake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
finished, ok := msg.(*finishedMsg)
|
|
||||||
if !ok {
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return unexpectedMessageError(finished, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hmac.Equal(hs.clientFinished, finished.verifyData) {
|
|
||||||
c.sendAlert(alertDecryptError)
|
|
||||||
return errors.New("tls: invalid client finished hash")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.in.setTrafficSecret(hs.suite, hs.trafficSecret)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,369 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/md5"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/x509"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"crypto/ecdh"
|
|
||||||
)
|
|
||||||
|
|
||||||
// a keyAgreement implements the client and server side of a TLS key agreement
|
|
||||||
// protocol by generating and processing key exchange messages.
|
|
||||||
type keyAgreement interface {
|
|
||||||
// On the server side, the first two methods are called in order.
|
|
||||||
|
|
||||||
// In the case that the key agreement protocol doesn't use a
|
|
||||||
// ServerKeyExchange message, generateServerKeyExchange can return nil,
|
|
||||||
// nil.
|
|
||||||
generateServerKeyExchange(*Config, *Certificate, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error)
|
|
||||||
processClientKeyExchange(*Config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error)
|
|
||||||
|
|
||||||
// On the client side, the next two methods are called in order.
|
|
||||||
|
|
||||||
// This method may not be called if the server doesn't send a
|
|
||||||
// ServerKeyExchange message.
|
|
||||||
processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error
|
|
||||||
generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
|
|
||||||
errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message")
|
|
||||||
)
|
|
||||||
|
|
||||||
// rsaKeyAgreement implements the standard TLS key agreement where the client
|
|
||||||
// encrypts the pre-master secret to the server's public key.
|
|
||||||
type rsaKeyAgreement struct{}
|
|
||||||
|
|
||||||
func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
|
|
||||||
if len(ckx.ciphertext) < 2 {
|
|
||||||
return nil, errClientKeyExchange
|
|
||||||
}
|
|
||||||
ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
|
|
||||||
if ciphertextLen != len(ckx.ciphertext)-2 {
|
|
||||||
return nil, errClientKeyExchange
|
|
||||||
}
|
|
||||||
ciphertext := ckx.ciphertext[2:]
|
|
||||||
|
|
||||||
priv, ok := cert.PrivateKey.(crypto.Decrypter)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("tls: certificate private key does not implement crypto.Decrypter")
|
|
||||||
}
|
|
||||||
// Perform constant time RSA PKCS #1 v1.5 decryption
|
|
||||||
preMasterSecret, err := priv.Decrypt(config.rand(), ciphertext, &rsa.PKCS1v15DecryptOptions{SessionKeyLen: 48})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// We don't check the version number in the premaster secret. For one,
|
|
||||||
// by checking it, we would leak information about the validity of the
|
|
||||||
// encrypted pre-master secret. Secondly, it provides only a small
|
|
||||||
// benefit against a downgrade attack and some implementations send the
|
|
||||||
// wrong version anyway. See the discussion at the end of section
|
|
||||||
// 7.4.7.1 of RFC 4346.
|
|
||||||
return preMasterSecret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
|
|
||||||
return errors.New("tls: unexpected ServerKeyExchange")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
|
|
||||||
preMasterSecret := make([]byte, 48)
|
|
||||||
preMasterSecret[0] = byte(clientHello.vers >> 8)
|
|
||||||
preMasterSecret[1] = byte(clientHello.vers)
|
|
||||||
_, err := io.ReadFull(config.rand(), preMasterSecret[2:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rsaKey, ok := cert.PublicKey.(*rsa.PublicKey)
|
|
||||||
if !ok {
|
|
||||||
return nil, nil, errors.New("tls: server certificate contains incorrect key type for selected ciphersuite")
|
|
||||||
}
|
|
||||||
encrypted, err := rsa.EncryptPKCS1v15(config.rand(), rsaKey, preMasterSecret)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
ckx := new(clientKeyExchangeMsg)
|
|
||||||
ckx.ciphertext = make([]byte, len(encrypted)+2)
|
|
||||||
ckx.ciphertext[0] = byte(len(encrypted) >> 8)
|
|
||||||
ckx.ciphertext[1] = byte(len(encrypted))
|
|
||||||
copy(ckx.ciphertext[2:], encrypted)
|
|
||||||
return preMasterSecret, ckx, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// sha1Hash calculates a SHA1 hash over the given byte slices.
|
|
||||||
func sha1Hash(slices [][]byte) []byte {
|
|
||||||
hsha1 := sha1.New()
|
|
||||||
for _, slice := range slices {
|
|
||||||
hsha1.Write(slice)
|
|
||||||
}
|
|
||||||
return hsha1.Sum(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the
|
|
||||||
// concatenation of an MD5 and SHA1 hash.
|
|
||||||
func md5SHA1Hash(slices [][]byte) []byte {
|
|
||||||
md5sha1 := make([]byte, md5.Size+sha1.Size)
|
|
||||||
hmd5 := md5.New()
|
|
||||||
for _, slice := range slices {
|
|
||||||
hmd5.Write(slice)
|
|
||||||
}
|
|
||||||
copy(md5sha1, hmd5.Sum(nil))
|
|
||||||
copy(md5sha1[md5.Size:], sha1Hash(slices))
|
|
||||||
return md5sha1
|
|
||||||
}
|
|
||||||
|
|
||||||
// hashForServerKeyExchange hashes the given slices and returns their digest
|
|
||||||
// using the given hash function (for >= TLS 1.2) or using a default based on
|
|
||||||
// the sigType (for earlier TLS versions). For Ed25519 signatures, which don't
|
|
||||||
// do pre-hashing, it returns the concatenation of the slices.
|
|
||||||
func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint16, slices ...[]byte) []byte {
|
|
||||||
if sigType == signatureEd25519 {
|
|
||||||
var signed []byte
|
|
||||||
for _, slice := range slices {
|
|
||||||
signed = append(signed, slice...)
|
|
||||||
}
|
|
||||||
return signed
|
|
||||||
}
|
|
||||||
if version >= VersionTLS12 {
|
|
||||||
h := hashFunc.New()
|
|
||||||
for _, slice := range slices {
|
|
||||||
h.Write(slice)
|
|
||||||
}
|
|
||||||
digest := h.Sum(nil)
|
|
||||||
return digest
|
|
||||||
}
|
|
||||||
if sigType == signatureECDSA {
|
|
||||||
return sha1Hash(slices)
|
|
||||||
}
|
|
||||||
return md5SHA1Hash(slices)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ecdheKeyAgreement implements a TLS key agreement where the server
|
|
||||||
// generates an ephemeral EC public/private key pair and signs it. The
|
|
||||||
// pre-master secret is then calculated using ECDH. The signature may
|
|
||||||
// be ECDSA, Ed25519 or RSA.
|
|
||||||
type ecdheKeyAgreement struct {
|
|
||||||
version uint16
|
|
||||||
isRSA bool
|
|
||||||
key *ecdh.PrivateKey
|
|
||||||
|
|
||||||
// ckx and preMasterSecret are generated in processServerKeyExchange
|
|
||||||
// and returned in generateClientKeyExchange.
|
|
||||||
ckx *clientKeyExchangeMsg
|
|
||||||
preMasterSecret []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
|
|
||||||
var curveID CurveID
|
|
||||||
for _, c := range clientHello.supportedCurves {
|
|
||||||
if config.supportsCurve(c) {
|
|
||||||
curveID = c
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if curveID == 0 {
|
|
||||||
return nil, errors.New("tls: no supported elliptic curves offered")
|
|
||||||
}
|
|
||||||
if _, ok := curveForCurveID(curveID); !ok {
|
|
||||||
return nil, errors.New("tls: CurvePreferences includes unsupported curve")
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := generateECDHEKey(config.rand(), curveID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ka.key = key
|
|
||||||
|
|
||||||
// See RFC 4492, Section 5.4.
|
|
||||||
ecdhePublic := key.PublicKey().Bytes()
|
|
||||||
serverECDHEParams := make([]byte, 1+2+1+len(ecdhePublic))
|
|
||||||
serverECDHEParams[0] = 3 // named curve
|
|
||||||
serverECDHEParams[1] = byte(curveID >> 8)
|
|
||||||
serverECDHEParams[2] = byte(curveID)
|
|
||||||
serverECDHEParams[3] = byte(len(ecdhePublic))
|
|
||||||
copy(serverECDHEParams[4:], ecdhePublic)
|
|
||||||
|
|
||||||
priv, ok := cert.PrivateKey.(crypto.Signer)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("tls: certificate private key of type %T does not implement crypto.Signer", cert.PrivateKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
var signatureAlgorithm SignatureScheme
|
|
||||||
var sigType uint8
|
|
||||||
var sigHash crypto.Hash
|
|
||||||
if ka.version >= VersionTLS12 {
|
|
||||||
signatureAlgorithm, err = selectSignatureScheme(ka.version, cert, clientHello.supportedSignatureAlgorithms)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(priv.Public())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
|
|
||||||
return nil, errors.New("tls: certificate cannot be used with the selected cipher suite")
|
|
||||||
}
|
|
||||||
|
|
||||||
signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, hello.random, serverECDHEParams)
|
|
||||||
|
|
||||||
signOpts := crypto.SignerOpts(sigHash)
|
|
||||||
if sigType == signatureRSAPSS {
|
|
||||||
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
|
|
||||||
}
|
|
||||||
sig, err := priv.Sign(config.rand(), signed, signOpts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
skx := new(serverKeyExchangeMsg)
|
|
||||||
sigAndHashLen := 0
|
|
||||||
if ka.version >= VersionTLS12 {
|
|
||||||
sigAndHashLen = 2
|
|
||||||
}
|
|
||||||
skx.key = make([]byte, len(serverECDHEParams)+sigAndHashLen+2+len(sig))
|
|
||||||
copy(skx.key, serverECDHEParams)
|
|
||||||
k := skx.key[len(serverECDHEParams):]
|
|
||||||
if ka.version >= VersionTLS12 {
|
|
||||||
k[0] = byte(signatureAlgorithm >> 8)
|
|
||||||
k[1] = byte(signatureAlgorithm)
|
|
||||||
k = k[2:]
|
|
||||||
}
|
|
||||||
k[0] = byte(len(sig) >> 8)
|
|
||||||
k[1] = byte(len(sig))
|
|
||||||
copy(k[2:], sig)
|
|
||||||
|
|
||||||
return skx, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
|
|
||||||
if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
|
|
||||||
return nil, errClientKeyExchange
|
|
||||||
}
|
|
||||||
|
|
||||||
peerKey, err := ka.key.Curve().NewPublicKey(ckx.ciphertext[1:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, errClientKeyExchange
|
|
||||||
}
|
|
||||||
preMasterSecret, err := ka.key.ECDH(peerKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errClientKeyExchange
|
|
||||||
}
|
|
||||||
|
|
||||||
return preMasterSecret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
|
|
||||||
if len(skx.key) < 4 {
|
|
||||||
return errServerKeyExchange
|
|
||||||
}
|
|
||||||
if skx.key[0] != 3 { // named curve
|
|
||||||
return errors.New("tls: server selected unsupported curve")
|
|
||||||
}
|
|
||||||
curveID := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
|
|
||||||
|
|
||||||
publicLen := int(skx.key[3])
|
|
||||||
if publicLen+4 > len(skx.key) {
|
|
||||||
return errServerKeyExchange
|
|
||||||
}
|
|
||||||
serverECDHEParams := skx.key[:4+publicLen]
|
|
||||||
publicKey := serverECDHEParams[4:]
|
|
||||||
|
|
||||||
sig := skx.key[4+publicLen:]
|
|
||||||
if len(sig) < 2 {
|
|
||||||
return errServerKeyExchange
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := curveForCurveID(curveID); !ok {
|
|
||||||
return errors.New("tls: server selected unsupported curve")
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := generateECDHEKey(config.rand(), curveID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ka.key = key
|
|
||||||
|
|
||||||
peerKey, err := key.Curve().NewPublicKey(publicKey)
|
|
||||||
if err != nil {
|
|
||||||
return errServerKeyExchange
|
|
||||||
}
|
|
||||||
ka.preMasterSecret, err = key.ECDH(peerKey)
|
|
||||||
if err != nil {
|
|
||||||
return errServerKeyExchange
|
|
||||||
}
|
|
||||||
|
|
||||||
ourPublicKey := key.PublicKey().Bytes()
|
|
||||||
ka.ckx = new(clientKeyExchangeMsg)
|
|
||||||
ka.ckx.ciphertext = make([]byte, 1+len(ourPublicKey))
|
|
||||||
ka.ckx.ciphertext[0] = byte(len(ourPublicKey))
|
|
||||||
copy(ka.ckx.ciphertext[1:], ourPublicKey)
|
|
||||||
|
|
||||||
var sigType uint8
|
|
||||||
var sigHash crypto.Hash
|
|
||||||
if ka.version >= VersionTLS12 {
|
|
||||||
signatureAlgorithm := SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1])
|
|
||||||
sig = sig[2:]
|
|
||||||
if len(sig) < 2 {
|
|
||||||
return errServerKeyExchange
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isSupportedSignatureAlgorithm(signatureAlgorithm, clientHello.supportedSignatureAlgorithms) {
|
|
||||||
return errors.New("tls: certificate used with invalid signature algorithm")
|
|
||||||
}
|
|
||||||
sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(cert.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
|
|
||||||
return errServerKeyExchange
|
|
||||||
}
|
|
||||||
|
|
||||||
sigLen := int(sig[0])<<8 | int(sig[1])
|
|
||||||
if sigLen+2 != len(sig) {
|
|
||||||
return errServerKeyExchange
|
|
||||||
}
|
|
||||||
sig = sig[2:]
|
|
||||||
|
|
||||||
signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, serverHello.random, serverECDHEParams)
|
|
||||||
if err := verifyHandshakeSignature(sigType, cert.PublicKey, sigHash, signed, sig); err != nil {
|
|
||||||
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
|
|
||||||
if ka.ckx == nil {
|
|
||||||
return nil, nil, errors.New("tls: missing ServerKeyExchange message")
|
|
||||||
}
|
|
||||||
|
|
||||||
return ka.preMasterSecret, ka.ckx, nil
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
// Copyright 2018 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/hmac"
|
|
||||||
"errors"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"crypto/ecdh"
|
|
||||||
"golang.org/x/crypto/cryptobyte"
|
|
||||||
"golang.org/x/crypto/hkdf"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This file contains the functions necessary to compute the TLS 1.3 key
|
|
||||||
// schedule. See RFC 8446, Section 7.
|
|
||||||
|
|
||||||
const (
|
|
||||||
resumptionBinderLabel = "res binder"
|
|
||||||
clientHandshakeTrafficLabel = "c hs traffic"
|
|
||||||
serverHandshakeTrafficLabel = "s hs traffic"
|
|
||||||
clientApplicationTrafficLabel = "c ap traffic"
|
|
||||||
serverApplicationTrafficLabel = "s ap traffic"
|
|
||||||
exporterLabel = "exp master"
|
|
||||||
resumptionLabel = "res master"
|
|
||||||
trafficUpdateLabel = "traffic upd"
|
|
||||||
)
|
|
||||||
|
|
||||||
// expandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1.
|
|
||||||
func (c *cipherSuiteTLS13) expandLabel(secret []byte, label string, context []byte, length int) []byte {
|
|
||||||
var hkdfLabel cryptobyte.Builder
|
|
||||||
hkdfLabel.AddUint16(uint16(length))
|
|
||||||
hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
b.AddBytes([]byte("tls13 "))
|
|
||||||
b.AddBytes([]byte(label))
|
|
||||||
})
|
|
||||||
hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
b.AddBytes(context)
|
|
||||||
})
|
|
||||||
out := make([]byte, length)
|
|
||||||
n, err := hkdf.Expand(c.hash.New, secret, hkdfLabel.BytesOrPanic()).Read(out)
|
|
||||||
if err != nil || n != length {
|
|
||||||
panic("tls: HKDF-Expand-Label invocation failed unexpectedly")
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// deriveSecret implements Derive-Secret from RFC 8446, Section 7.1.
|
|
||||||
func (c *cipherSuiteTLS13) deriveSecret(secret []byte, label string, transcript hash.Hash) []byte {
|
|
||||||
if transcript == nil {
|
|
||||||
transcript = c.hash.New()
|
|
||||||
}
|
|
||||||
return c.expandLabel(secret, label, transcript.Sum(nil), c.hash.Size())
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract implements HKDF-Extract with the cipher suite hash.
|
|
||||||
func (c *cipherSuiteTLS13) extract(newSecret, currentSecret []byte) []byte {
|
|
||||||
if newSecret == nil {
|
|
||||||
newSecret = make([]byte, c.hash.Size())
|
|
||||||
}
|
|
||||||
return hkdf.Extract(c.hash.New, newSecret, currentSecret)
|
|
||||||
}
|
|
||||||
|
|
||||||
// nextTrafficSecret generates the next traffic secret, given the current one,
|
|
||||||
// according to RFC 8446, Section 7.2.
|
|
||||||
func (c *cipherSuiteTLS13) nextTrafficSecret(trafficSecret []byte) []byte {
|
|
||||||
return c.expandLabel(trafficSecret, trafficUpdateLabel, nil, c.hash.Size())
|
|
||||||
}
|
|
||||||
|
|
||||||
// trafficKey generates traffic keys according to RFC 8446, Section 7.3.
|
|
||||||
func (c *cipherSuiteTLS13) trafficKey(trafficSecret []byte) (key, iv []byte) {
|
|
||||||
key = c.expandLabel(trafficSecret, "key", nil, c.keyLen)
|
|
||||||
iv = c.expandLabel(trafficSecret, "iv", nil, aeadNonceLength)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// finishedHash generates the Finished verify_data or PskBinderEntry according
|
|
||||||
// to RFC 8446, Section 4.4.4. See sections 4.4 and 4.2.11.2 for the baseKey
|
|
||||||
// selection.
|
|
||||||
func (c *cipherSuiteTLS13) finishedHash(baseKey []byte, transcript hash.Hash) []byte {
|
|
||||||
finishedKey := c.expandLabel(baseKey, "finished", nil, c.hash.Size())
|
|
||||||
verifyData := hmac.New(c.hash.New, finishedKey)
|
|
||||||
verifyData.Write(transcript.Sum(nil))
|
|
||||||
return verifyData.Sum(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// exportKeyingMaterial implements RFC5705 exporters for TLS 1.3 according to
|
|
||||||
// RFC 8446, Section 7.5.
|
|
||||||
func (c *cipherSuiteTLS13) exportKeyingMaterial(masterSecret []byte, transcript hash.Hash) func(string, []byte, int) ([]byte, error) {
|
|
||||||
expMasterSecret := c.deriveSecret(masterSecret, exporterLabel, transcript)
|
|
||||||
return func(label string, context []byte, length int) ([]byte, error) {
|
|
||||||
secret := c.deriveSecret(expMasterSecret, label, nil)
|
|
||||||
h := c.hash.New()
|
|
||||||
h.Write(context)
|
|
||||||
return c.expandLabel(secret, "exporter", h.Sum(nil), length), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// generateECDHEKey returns a PrivateKey that implements Diffie-Hellman
|
|
||||||
// according to RFC 8446, Section 4.2.8.2.
|
|
||||||
func generateECDHEKey(rand io.Reader, curveID CurveID) (*ecdh.PrivateKey, error) {
|
|
||||||
curve, ok := curveForCurveID(curveID)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("tls: internal error: unsupported curve")
|
|
||||||
}
|
|
||||||
|
|
||||||
return curve.GenerateKey(rand)
|
|
||||||
}
|
|
||||||
|
|
||||||
func curveForCurveID(id CurveID) (ecdh.Curve, bool) {
|
|
||||||
switch id {
|
|
||||||
case X25519:
|
|
||||||
return ecdh.X25519(), true
|
|
||||||
case CurveP256:
|
|
||||||
return ecdh.P256(), true
|
|
||||||
case CurveP384:
|
|
||||||
return ecdh.P384(), true
|
|
||||||
case CurveP521:
|
|
||||||
return ecdh.P521(), true
|
|
||||||
default:
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func curveIDForCurve(curve ecdh.Curve) (CurveID, bool) {
|
|
||||||
switch curve {
|
|
||||||
case ecdh.X25519():
|
|
||||||
return X25519, true
|
|
||||||
case ecdh.P256():
|
|
||||||
return CurveP256, true
|
|
||||||
case ecdh.P384():
|
|
||||||
return CurveP384, true
|
|
||||||
case ecdh.P521():
|
|
||||||
return CurveP521, true
|
|
||||||
default:
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
// Copyright 2022 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build !boringcrypto
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
func needFIPS() bool { return false }
|
|
||||||
|
|
||||||
func supportedSignatureAlgorithms() []SignatureScheme {
|
|
||||||
return defaultSupportedSignatureAlgorithms
|
|
||||||
}
|
|
||||||
|
|
||||||
func fipsMinVersion(c *Config) uint16 { panic("fipsMinVersion") }
|
|
||||||
func fipsMaxVersion(c *Config) uint16 { panic("fipsMaxVersion") }
|
|
||||||
func fipsCurvePreferences(c *Config) []CurveID { panic("fipsCurvePreferences") }
|
|
||||||
func fipsCipherSuites(c *Config) []uint16 { panic("fipsCipherSuites") }
|
|
||||||
|
|
||||||
var fipsSupportedSignatureAlgorithms []SignatureScheme
|
|
||||||
@@ -1,285 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/md5"
|
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/sha512"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"hash"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Split a premaster secret in two as specified in RFC 4346, Section 5.
|
|
||||||
func splitPreMasterSecret(secret []byte) (s1, s2 []byte) {
|
|
||||||
s1 = secret[0 : (len(secret)+1)/2]
|
|
||||||
s2 = secret[len(secret)/2:]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// pHash implements the P_hash function, as defined in RFC 4346, Section 5.
|
|
||||||
func pHash(result, secret, seed []byte, hash func() hash.Hash) {
|
|
||||||
h := hmac.New(hash, secret)
|
|
||||||
h.Write(seed)
|
|
||||||
a := h.Sum(nil)
|
|
||||||
|
|
||||||
j := 0
|
|
||||||
for j < len(result) {
|
|
||||||
h.Reset()
|
|
||||||
h.Write(a)
|
|
||||||
h.Write(seed)
|
|
||||||
b := h.Sum(nil)
|
|
||||||
copy(result[j:], b)
|
|
||||||
j += len(b)
|
|
||||||
|
|
||||||
h.Reset()
|
|
||||||
h.Write(a)
|
|
||||||
a = h.Sum(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, Section 5.
|
|
||||||
func prf10(result, secret, label, seed []byte) {
|
|
||||||
hashSHA1 := sha1.New
|
|
||||||
hashMD5 := md5.New
|
|
||||||
|
|
||||||
labelAndSeed := make([]byte, len(label)+len(seed))
|
|
||||||
copy(labelAndSeed, label)
|
|
||||||
copy(labelAndSeed[len(label):], seed)
|
|
||||||
|
|
||||||
s1, s2 := splitPreMasterSecret(secret)
|
|
||||||
pHash(result, s1, labelAndSeed, hashMD5)
|
|
||||||
result2 := make([]byte, len(result))
|
|
||||||
pHash(result2, s2, labelAndSeed, hashSHA1)
|
|
||||||
|
|
||||||
for i, b := range result2 {
|
|
||||||
result[i] ^= b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5.
|
|
||||||
func prf12(hashFunc func() hash.Hash) func(result, secret, label, seed []byte) {
|
|
||||||
return func(result, secret, label, seed []byte) {
|
|
||||||
labelAndSeed := make([]byte, len(label)+len(seed))
|
|
||||||
copy(labelAndSeed, label)
|
|
||||||
copy(labelAndSeed[len(label):], seed)
|
|
||||||
|
|
||||||
pHash(result, secret, labelAndSeed, hashFunc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
masterSecretLength = 48 // Length of a master secret in TLS 1.1.
|
|
||||||
finishedVerifyLength = 12 // Length of verify_data in a Finished message.
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
masterSecretLabel = []byte("master secret")
|
|
||||||
keyExpansionLabel = []byte("key expansion")
|
|
||||||
clientFinishedLabel = []byte("client finished")
|
|
||||||
serverFinishedLabel = []byte("server finished")
|
|
||||||
)
|
|
||||||
|
|
||||||
func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secret, label, seed []byte), crypto.Hash) {
|
|
||||||
switch version {
|
|
||||||
case VersionTLS10, VersionTLS11:
|
|
||||||
return prf10, crypto.Hash(0)
|
|
||||||
case VersionTLS12:
|
|
||||||
if suite.flags&suiteSHA384 != 0 {
|
|
||||||
return prf12(sha512.New384), crypto.SHA384
|
|
||||||
}
|
|
||||||
return prf12(sha256.New), crypto.SHA256
|
|
||||||
default:
|
|
||||||
panic("unknown version")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func prfForVersion(version uint16, suite *cipherSuite) func(result, secret, label, seed []byte) {
|
|
||||||
prf, _ := prfAndHashForVersion(version, suite)
|
|
||||||
return prf
|
|
||||||
}
|
|
||||||
|
|
||||||
// masterFromPreMasterSecret generates the master secret from the pre-master
|
|
||||||
// secret. See RFC 5246, Section 8.1.
|
|
||||||
func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, clientRandom, serverRandom []byte) []byte {
|
|
||||||
seed := make([]byte, 0, len(clientRandom)+len(serverRandom))
|
|
||||||
seed = append(seed, clientRandom...)
|
|
||||||
seed = append(seed, serverRandom...)
|
|
||||||
|
|
||||||
masterSecret := make([]byte, masterSecretLength)
|
|
||||||
prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed)
|
|
||||||
return masterSecret
|
|
||||||
}
|
|
||||||
|
|
||||||
// keysFromMasterSecret generates the connection keys from the master
|
|
||||||
// secret, given the lengths of the MAC key, cipher key and IV, as defined in
|
|
||||||
// RFC 2246, Section 6.3.
|
|
||||||
func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
|
|
||||||
seed := make([]byte, 0, len(serverRandom)+len(clientRandom))
|
|
||||||
seed = append(seed, serverRandom...)
|
|
||||||
seed = append(seed, clientRandom...)
|
|
||||||
|
|
||||||
n := 2*macLen + 2*keyLen + 2*ivLen
|
|
||||||
keyMaterial := make([]byte, n)
|
|
||||||
prfForVersion(version, suite)(keyMaterial, masterSecret, keyExpansionLabel, seed)
|
|
||||||
clientMAC = keyMaterial[:macLen]
|
|
||||||
keyMaterial = keyMaterial[macLen:]
|
|
||||||
serverMAC = keyMaterial[:macLen]
|
|
||||||
keyMaterial = keyMaterial[macLen:]
|
|
||||||
clientKey = keyMaterial[:keyLen]
|
|
||||||
keyMaterial = keyMaterial[keyLen:]
|
|
||||||
serverKey = keyMaterial[:keyLen]
|
|
||||||
keyMaterial = keyMaterial[keyLen:]
|
|
||||||
clientIV = keyMaterial[:ivLen]
|
|
||||||
keyMaterial = keyMaterial[ivLen:]
|
|
||||||
serverIV = keyMaterial[:ivLen]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash {
|
|
||||||
var buffer []byte
|
|
||||||
if version >= VersionTLS12 {
|
|
||||||
buffer = []byte{}
|
|
||||||
}
|
|
||||||
|
|
||||||
prf, hash := prfAndHashForVersion(version, cipherSuite)
|
|
||||||
if hash != 0 {
|
|
||||||
return finishedHash{hash.New(), hash.New(), nil, nil, buffer, version, prf}
|
|
||||||
}
|
|
||||||
|
|
||||||
return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), buffer, version, prf}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A finishedHash calculates the hash of a set of handshake messages suitable
|
|
||||||
// for including in a Finished message.
|
|
||||||
type finishedHash struct {
|
|
||||||
client hash.Hash
|
|
||||||
server hash.Hash
|
|
||||||
|
|
||||||
// Prior to TLS 1.2, an additional MD5 hash is required.
|
|
||||||
clientMD5 hash.Hash
|
|
||||||
serverMD5 hash.Hash
|
|
||||||
|
|
||||||
// In TLS 1.2, a full buffer is sadly required.
|
|
||||||
buffer []byte
|
|
||||||
|
|
||||||
version uint16
|
|
||||||
prf func(result, secret, label, seed []byte)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *finishedHash) Write(msg []byte) (n int, err error) {
|
|
||||||
h.client.Write(msg)
|
|
||||||
h.server.Write(msg)
|
|
||||||
|
|
||||||
if h.version < VersionTLS12 {
|
|
||||||
h.clientMD5.Write(msg)
|
|
||||||
h.serverMD5.Write(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
if h.buffer != nil {
|
|
||||||
h.buffer = append(h.buffer, msg...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(msg), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h finishedHash) Sum() []byte {
|
|
||||||
if h.version >= VersionTLS12 {
|
|
||||||
return h.client.Sum(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
out := make([]byte, 0, md5.Size+sha1.Size)
|
|
||||||
out = h.clientMD5.Sum(out)
|
|
||||||
return h.client.Sum(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// clientSum returns the contents of the verify_data member of a client's
|
|
||||||
// Finished message.
|
|
||||||
func (h finishedHash) clientSum(masterSecret []byte) []byte {
|
|
||||||
out := make([]byte, finishedVerifyLength)
|
|
||||||
h.prf(out, masterSecret, clientFinishedLabel, h.Sum())
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// serverSum returns the contents of the verify_data member of a server's
|
|
||||||
// Finished message.
|
|
||||||
func (h finishedHash) serverSum(masterSecret []byte) []byte {
|
|
||||||
out := make([]byte, finishedVerifyLength)
|
|
||||||
h.prf(out, masterSecret, serverFinishedLabel, h.Sum())
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// hashForClientCertificate returns the handshake messages so far, pre-hashed if
|
|
||||||
// necessary, suitable for signing by a TLS client certificate.
|
|
||||||
func (h finishedHash) hashForClientCertificate(sigType uint8, hashAlg crypto.Hash) []byte {
|
|
||||||
if (h.version >= VersionTLS12 || sigType == signatureEd25519) && h.buffer == nil {
|
|
||||||
panic("tls: handshake hash for a client certificate requested after discarding the handshake buffer")
|
|
||||||
}
|
|
||||||
|
|
||||||
if sigType == signatureEd25519 {
|
|
||||||
return h.buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
if h.version >= VersionTLS12 {
|
|
||||||
hash := hashAlg.New()
|
|
||||||
hash.Write(h.buffer)
|
|
||||||
return hash.Sum(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if sigType == signatureECDSA {
|
|
||||||
return h.server.Sum(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
return h.Sum()
|
|
||||||
}
|
|
||||||
|
|
||||||
// discardHandshakeBuffer is called when there is no more need to
|
|
||||||
// buffer the entirety of the handshake messages.
|
|
||||||
func (h *finishedHash) discardHandshakeBuffer() {
|
|
||||||
h.buffer = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// noExportedKeyingMaterial is used as a value of
|
|
||||||
// ConnectionState.ekm when renegotiation is enabled and thus
|
|
||||||
// we wish to fail all key-material export requests.
|
|
||||||
func noExportedKeyingMaterial(label string, context []byte, length int) ([]byte, error) {
|
|
||||||
return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ekmFromMasterSecret generates exported keying material as defined in RFC 5705.
|
|
||||||
func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte) func(string, []byte, int) ([]byte, error) {
|
|
||||||
return func(label string, context []byte, length int) ([]byte, error) {
|
|
||||||
switch label {
|
|
||||||
case "client finished", "server finished", "master secret", "key expansion":
|
|
||||||
// These values are reserved and may not be used.
|
|
||||||
return nil, fmt.Errorf("crypto/tls: reserved ExportKeyingMaterial label: %s", label)
|
|
||||||
}
|
|
||||||
|
|
||||||
seedLen := len(serverRandom) + len(clientRandom)
|
|
||||||
if context != nil {
|
|
||||||
seedLen += 2 + len(context)
|
|
||||||
}
|
|
||||||
seed := make([]byte, 0, seedLen)
|
|
||||||
|
|
||||||
seed = append(seed, clientRandom...)
|
|
||||||
seed = append(seed, serverRandom...)
|
|
||||||
|
|
||||||
if context != nil {
|
|
||||||
if len(context) >= 1<<16 {
|
|
||||||
return nil, fmt.Errorf("crypto/tls: ExportKeyingMaterial context too long")
|
|
||||||
}
|
|
||||||
seed = append(seed, byte(len(context)>>8), byte(len(context)))
|
|
||||||
seed = append(seed, context...)
|
|
||||||
}
|
|
||||||
|
|
||||||
keyMaterial := make([]byte, length)
|
|
||||||
prfForVersion(version, suite)(keyMaterial, masterSecret, []byte(label), seed)
|
|
||||||
return keyMaterial, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/subtle"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/cryptobyte"
|
|
||||||
)
|
|
||||||
|
|
||||||
// sessionState contains the information that is serialized into a session
|
|
||||||
// ticket in order to later resume a connection.
|
|
||||||
type sessionState struct {
|
|
||||||
vers uint16
|
|
||||||
cipherSuite uint16
|
|
||||||
createdAt uint64
|
|
||||||
masterSecret []byte // opaque master_secret<1..2^16-1>;
|
|
||||||
// struct { opaque certificate<1..2^24-1> } Certificate;
|
|
||||||
certificates [][]byte // Certificate certificate_list<0..2^24-1>;
|
|
||||||
|
|
||||||
// usedOldKey is true if the ticket from which this session came from
|
|
||||||
// was encrypted with an older key and thus should be refreshed.
|
|
||||||
usedOldKey bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *sessionState) marshal() []byte {
|
|
||||||
var b cryptobyte.Builder
|
|
||||||
b.AddUint16(m.vers)
|
|
||||||
b.AddUint16(m.cipherSuite)
|
|
||||||
addUint64(&b, m.createdAt)
|
|
||||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
b.AddBytes(m.masterSecret)
|
|
||||||
})
|
|
||||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
for _, cert := range m.certificates {
|
|
||||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
b.AddBytes(cert)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return b.BytesOrPanic()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *sessionState) unmarshal(data []byte) bool {
|
|
||||||
*m = sessionState{usedOldKey: m.usedOldKey}
|
|
||||||
s := cryptobyte.String(data)
|
|
||||||
if ok := s.ReadUint16(&m.vers) &&
|
|
||||||
s.ReadUint16(&m.cipherSuite) &&
|
|
||||||
readUint64(&s, &m.createdAt) &&
|
|
||||||
readUint16LengthPrefixed(&s, &m.masterSecret) &&
|
|
||||||
len(m.masterSecret) != 0; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
var certList cryptobyte.String
|
|
||||||
if !s.ReadUint24LengthPrefixed(&certList) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for !certList.Empty() {
|
|
||||||
var cert []byte
|
|
||||||
if !readUint24LengthPrefixed(&certList, &cert) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
m.certificates = append(m.certificates, cert)
|
|
||||||
}
|
|
||||||
return s.Empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
// sessionStateTLS13 is the content of a TLS 1.3 session ticket. Its first
|
|
||||||
// version (revision = 0) doesn't carry any of the information needed for 0-RTT
|
|
||||||
// validation and the nonce is always empty.
|
|
||||||
type sessionStateTLS13 struct {
|
|
||||||
// uint8 version = 0x0304;
|
|
||||||
// uint8 revision = 0;
|
|
||||||
cipherSuite uint16
|
|
||||||
createdAt uint64
|
|
||||||
resumptionSecret []byte // opaque resumption_master_secret<1..2^8-1>;
|
|
||||||
certificate Certificate // CertificateEntry certificate_list<0..2^24-1>;
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *sessionStateTLS13) marshal() []byte {
|
|
||||||
var b cryptobyte.Builder
|
|
||||||
b.AddUint16(VersionTLS13)
|
|
||||||
b.AddUint8(0) // revision
|
|
||||||
b.AddUint16(m.cipherSuite)
|
|
||||||
addUint64(&b, m.createdAt)
|
|
||||||
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
b.AddBytes(m.resumptionSecret)
|
|
||||||
})
|
|
||||||
marshalCertificate(&b, m.certificate)
|
|
||||||
return b.BytesOrPanic()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *sessionStateTLS13) unmarshal(data []byte) bool {
|
|
||||||
*m = sessionStateTLS13{}
|
|
||||||
s := cryptobyte.String(data)
|
|
||||||
var version uint16
|
|
||||||
var revision uint8
|
|
||||||
return s.ReadUint16(&version) &&
|
|
||||||
version == VersionTLS13 &&
|
|
||||||
s.ReadUint8(&revision) &&
|
|
||||||
revision == 0 &&
|
|
||||||
s.ReadUint16(&m.cipherSuite) &&
|
|
||||||
readUint64(&s, &m.createdAt) &&
|
|
||||||
readUint8LengthPrefixed(&s, &m.resumptionSecret) &&
|
|
||||||
len(m.resumptionSecret) != 0 &&
|
|
||||||
unmarshalCertificate(&s, &m.certificate) &&
|
|
||||||
s.Empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) encryptTicket(state []byte) ([]byte, error) {
|
|
||||||
if len(c.ticketKeys) == 0 {
|
|
||||||
return nil, errors.New("tls: internal error: session ticket keys unavailable")
|
|
||||||
}
|
|
||||||
|
|
||||||
encrypted := make([]byte, ticketKeyNameLen+aes.BlockSize+len(state)+sha256.Size)
|
|
||||||
keyName := encrypted[:ticketKeyNameLen]
|
|
||||||
iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
|
|
||||||
macBytes := encrypted[len(encrypted)-sha256.Size:]
|
|
||||||
|
|
||||||
if _, err := io.ReadFull(c.config.rand(), iv); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
key := c.ticketKeys[0]
|
|
||||||
copy(keyName, key.keyName[:])
|
|
||||||
block, err := aes.NewCipher(key.aesKey[:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
|
|
||||||
}
|
|
||||||
cipher.NewCTR(block, iv).XORKeyStream(encrypted[ticketKeyNameLen+aes.BlockSize:], state)
|
|
||||||
|
|
||||||
mac := hmac.New(sha256.New, key.hmacKey[:])
|
|
||||||
mac.Write(encrypted[:len(encrypted)-sha256.Size])
|
|
||||||
mac.Sum(macBytes[:0])
|
|
||||||
|
|
||||||
return encrypted, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) decryptTicket(encrypted []byte) (plaintext []byte, usedOldKey bool) {
|
|
||||||
if len(encrypted) < ticketKeyNameLen+aes.BlockSize+sha256.Size {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
keyName := encrypted[:ticketKeyNameLen]
|
|
||||||
iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
|
|
||||||
macBytes := encrypted[len(encrypted)-sha256.Size:]
|
|
||||||
ciphertext := encrypted[ticketKeyNameLen+aes.BlockSize : len(encrypted)-sha256.Size]
|
|
||||||
|
|
||||||
keyIndex := -1
|
|
||||||
for i, candidateKey := range c.ticketKeys {
|
|
||||||
if bytes.Equal(keyName, candidateKey.keyName[:]) {
|
|
||||||
keyIndex = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if keyIndex == -1 {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
key := &c.ticketKeys[keyIndex]
|
|
||||||
|
|
||||||
mac := hmac.New(sha256.New, key.hmacKey[:])
|
|
||||||
mac.Write(encrypted[:len(encrypted)-sha256.Size])
|
|
||||||
expected := mac.Sum(nil)
|
|
||||||
|
|
||||||
if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
block, err := aes.NewCipher(key.aesKey[:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
plaintext = make([]byte, len(ciphertext))
|
|
||||||
cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
|
|
||||||
|
|
||||||
return plaintext, keyIndex > 0
|
|
||||||
}
|
|
||||||
@@ -1,356 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package tls partially implements TLS 1.2, as specified in RFC 5246,
|
|
||||||
// and TLS 1.3, as specified in RFC 8446.
|
|
||||||
package tls
|
|
||||||
|
|
||||||
// BUG(agl): The crypto/tls package only implements some countermeasures
|
|
||||||
// against Lucky13 attacks on CBC-mode encryption, and only on SHA1
|
|
||||||
// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
|
|
||||||
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/ed25519"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/pem"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Server returns a new TLS server side connection
|
|
||||||
// using conn as the underlying transport.
|
|
||||||
// The configuration config must be non-nil and must include
|
|
||||||
// at least one certificate or else set GetCertificate.
|
|
||||||
func Server(conn net.Conn, config *Config) *Conn {
|
|
||||||
c := &Conn{
|
|
||||||
conn: conn,
|
|
||||||
config: config,
|
|
||||||
}
|
|
||||||
c.handshakeFn = c.serverHandshake
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client returns a new TLS client side connection
|
|
||||||
// using conn as the underlying transport.
|
|
||||||
// The config cannot be nil: users must set either ServerName or
|
|
||||||
// InsecureSkipVerify in the config.
|
|
||||||
func Client(conn net.Conn, config *Config) *Conn {
|
|
||||||
c := &Conn{
|
|
||||||
conn: conn,
|
|
||||||
config: config,
|
|
||||||
isClient: true,
|
|
||||||
}
|
|
||||||
c.handshakeFn = c.clientHandshake
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// A listener implements a network listener (net.Listener) for TLS connections.
|
|
||||||
type listener struct {
|
|
||||||
net.Listener
|
|
||||||
config *Config
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept waits for and returns the next incoming TLS connection.
|
|
||||||
// The returned connection is of type *Conn.
|
|
||||||
func (l *listener) Accept() (net.Conn, error) {
|
|
||||||
c, err := l.Listener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return Server(c, l.config), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewListener creates a Listener which accepts connections from an inner
|
|
||||||
// Listener and wraps each connection with Server.
|
|
||||||
// The configuration config must be non-nil and must include
|
|
||||||
// at least one certificate or else set GetCertificate.
|
|
||||||
func NewListener(inner net.Listener, config *Config) net.Listener {
|
|
||||||
l := new(listener)
|
|
||||||
l.Listener = inner
|
|
||||||
l.config = config
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen creates a TLS listener accepting connections on the
|
|
||||||
// given network address using net.Listen.
|
|
||||||
// The configuration config must be non-nil and must include
|
|
||||||
// at least one certificate or else set GetCertificate.
|
|
||||||
func Listen(network, laddr string, config *Config) (net.Listener, error) {
|
|
||||||
if config == nil || len(config.Certificates) == 0 &&
|
|
||||||
config.GetCertificate == nil && config.GetConfigForClient == nil {
|
|
||||||
return nil, errors.New("tls: neither Certificates, GetCertificate, nor GetConfigForClient set in Config")
|
|
||||||
}
|
|
||||||
l, err := net.Listen(network, laddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return NewListener(l, config), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type timeoutError struct{}
|
|
||||||
|
|
||||||
func (timeoutError) Error() string { return "tls: DialWithDialer timed out" }
|
|
||||||
func (timeoutError) Timeout() bool { return true }
|
|
||||||
func (timeoutError) Temporary() bool { return true }
|
|
||||||
|
|
||||||
// DialWithDialer connects to the given network address using dialer.Dial and
|
|
||||||
// then initiates a TLS handshake, returning the resulting TLS connection. Any
|
|
||||||
// timeout or deadline given in the dialer apply to connection and TLS
|
|
||||||
// handshake as a whole.
|
|
||||||
//
|
|
||||||
// DialWithDialer interprets a nil configuration as equivalent to the zero
|
|
||||||
// configuration; see the documentation of Config for the defaults.
|
|
||||||
//
|
|
||||||
// DialWithDialer uses context.Background internally; to specify the context,
|
|
||||||
// use Dialer.DialContext with NetDialer set to the desired dialer.
|
|
||||||
func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error) {
|
|
||||||
return dial(context.Background(), dialer, network, addr, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
func dial(ctx context.Context, netDialer *net.Dialer, network, addr string, config *Config) (*Conn, error) {
|
|
||||||
if netDialer.Timeout != 0 {
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
ctx, cancel = context.WithTimeout(ctx, netDialer.Timeout)
|
|
||||||
defer cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !netDialer.Deadline.IsZero() {
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
ctx, cancel = context.WithDeadline(ctx, netDialer.Deadline)
|
|
||||||
defer cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
rawConn, err := netDialer.DialContext(ctx, network, addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
colonPos := strings.LastIndex(addr, ":")
|
|
||||||
if colonPos == -1 {
|
|
||||||
colonPos = len(addr)
|
|
||||||
}
|
|
||||||
hostname := addr[:colonPos]
|
|
||||||
|
|
||||||
if config == nil {
|
|
||||||
config = defaultConfig()
|
|
||||||
}
|
|
||||||
// If no ServerName is set, infer the ServerName
|
|
||||||
// from the hostname we're connecting to.
|
|
||||||
if config.ServerName == "" {
|
|
||||||
// Make a copy to avoid polluting argument or default.
|
|
||||||
c := config.Clone()
|
|
||||||
c.ServerName = hostname
|
|
||||||
config = c
|
|
||||||
}
|
|
||||||
|
|
||||||
conn := Client(rawConn, config)
|
|
||||||
if err := conn.HandshakeContext(ctx); err != nil {
|
|
||||||
rawConn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial connects to the given network address using net.Dial
|
|
||||||
// and then initiates a TLS handshake, returning the resulting
|
|
||||||
// TLS connection.
|
|
||||||
// Dial interprets a nil configuration as equivalent to
|
|
||||||
// the zero configuration; see the documentation of Config
|
|
||||||
// for the defaults.
|
|
||||||
func Dial(network, addr string, config *Config) (*Conn, error) {
|
|
||||||
return DialWithDialer(new(net.Dialer), network, addr, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dialer dials TLS connections given a configuration and a Dialer for the
|
|
||||||
// underlying connection.
|
|
||||||
type Dialer struct {
|
|
||||||
// NetDialer is the optional dialer to use for the TLS connections'
|
|
||||||
// underlying TCP connections.
|
|
||||||
// A nil NetDialer is equivalent to the net.Dialer zero value.
|
|
||||||
NetDialer *net.Dialer
|
|
||||||
|
|
||||||
// Config is the TLS configuration to use for new connections.
|
|
||||||
// A nil configuration is equivalent to the zero
|
|
||||||
// configuration; see the documentation of Config for the
|
|
||||||
// defaults.
|
|
||||||
Config *Config
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial connects to the given network address and initiates a TLS
|
|
||||||
// handshake, returning the resulting TLS connection.
|
|
||||||
//
|
|
||||||
// The returned Conn, if any, will always be of type *Conn.
|
|
||||||
//
|
|
||||||
// Dial uses context.Background internally; to specify the context,
|
|
||||||
// use DialContext.
|
|
||||||
func (d *Dialer) Dial(network, addr string) (net.Conn, error) {
|
|
||||||
return d.DialContext(context.Background(), network, addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Dialer) netDialer() *net.Dialer {
|
|
||||||
if d.NetDialer != nil {
|
|
||||||
return d.NetDialer
|
|
||||||
}
|
|
||||||
return new(net.Dialer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialContext connects to the given network address and initiates a TLS
|
|
||||||
// handshake, returning the resulting TLS connection.
|
|
||||||
//
|
|
||||||
// The provided Context must be non-nil. If the context expires before
|
|
||||||
// the connection is complete, an error is returned. Once successfully
|
|
||||||
// connected, any expiration of the context will not affect the
|
|
||||||
// connection.
|
|
||||||
//
|
|
||||||
// The returned Conn, if any, will always be of type *Conn.
|
|
||||||
func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
||||||
c, err := dial(ctx, d.netDialer(), network, addr, d.Config)
|
|
||||||
if err != nil {
|
|
||||||
// Don't return c (a typed nil) in an interface.
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadX509KeyPair reads and parses a public/private key pair from a pair
|
|
||||||
// of files. The files must contain PEM encoded data. The certificate file
|
|
||||||
// may contain intermediate certificates following the leaf certificate to
|
|
||||||
// form a certificate chain. On successful return, Certificate.Leaf will
|
|
||||||
// be nil because the parsed form of the certificate is not retained.
|
|
||||||
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
|
|
||||||
certPEMBlock, err := os.ReadFile(certFile)
|
|
||||||
if err != nil {
|
|
||||||
return Certificate{}, err
|
|
||||||
}
|
|
||||||
keyPEMBlock, err := os.ReadFile(keyFile)
|
|
||||||
if err != nil {
|
|
||||||
return Certificate{}, err
|
|
||||||
}
|
|
||||||
return X509KeyPair(certPEMBlock, keyPEMBlock)
|
|
||||||
}
|
|
||||||
|
|
||||||
// X509KeyPair parses a public/private key pair from a pair of
|
|
||||||
// PEM encoded data. On successful return, Certificate.Leaf will be nil because
|
|
||||||
// the parsed form of the certificate is not retained.
|
|
||||||
func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
|
|
||||||
fail := func(err error) (Certificate, error) { return Certificate{}, err }
|
|
||||||
|
|
||||||
var cert Certificate
|
|
||||||
var skippedBlockTypes []string
|
|
||||||
for {
|
|
||||||
var certDERBlock *pem.Block
|
|
||||||
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
|
|
||||||
if certDERBlock == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if certDERBlock.Type == "CERTIFICATE" {
|
|
||||||
cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
|
|
||||||
} else {
|
|
||||||
skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(cert.Certificate) == 0 {
|
|
||||||
if len(skippedBlockTypes) == 0 {
|
|
||||||
return fail(errors.New("tls: failed to find any PEM data in certificate input"))
|
|
||||||
}
|
|
||||||
if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") {
|
|
||||||
return fail(errors.New("tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched"))
|
|
||||||
}
|
|
||||||
return fail(fmt.Errorf("tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
|
|
||||||
}
|
|
||||||
|
|
||||||
skippedBlockTypes = skippedBlockTypes[:0]
|
|
||||||
var keyDERBlock *pem.Block
|
|
||||||
for {
|
|
||||||
keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
|
|
||||||
if keyDERBlock == nil {
|
|
||||||
if len(skippedBlockTypes) == 0 {
|
|
||||||
return fail(errors.New("tls: failed to find any PEM data in key input"))
|
|
||||||
}
|
|
||||||
if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" {
|
|
||||||
return fail(errors.New("tls: found a certificate rather than a key in the PEM for the private key"))
|
|
||||||
}
|
|
||||||
return fail(fmt.Errorf("tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
|
|
||||||
}
|
|
||||||
if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't need to parse the public key for TLS, but we so do anyway
|
|
||||||
// to check that it looks sane and matches the private key.
|
|
||||||
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
|
|
||||||
if err != nil {
|
|
||||||
return fail(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return fail(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch pub := x509Cert.PublicKey.(type) {
|
|
||||||
case *rsa.PublicKey:
|
|
||||||
priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
|
|
||||||
if !ok {
|
|
||||||
return fail(errors.New("tls: private key type does not match public key type"))
|
|
||||||
}
|
|
||||||
if pub.N.Cmp(priv.N) != 0 {
|
|
||||||
return fail(errors.New("tls: private key does not match public key"))
|
|
||||||
}
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
|
|
||||||
if !ok {
|
|
||||||
return fail(errors.New("tls: private key type does not match public key type"))
|
|
||||||
}
|
|
||||||
if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
|
|
||||||
return fail(errors.New("tls: private key does not match public key"))
|
|
||||||
}
|
|
||||||
case ed25519.PublicKey:
|
|
||||||
priv, ok := cert.PrivateKey.(ed25519.PrivateKey)
|
|
||||||
if !ok {
|
|
||||||
return fail(errors.New("tls: private key type does not match public key type"))
|
|
||||||
}
|
|
||||||
if !bytes.Equal(priv.Public().(ed25519.PublicKey), pub) {
|
|
||||||
return fail(errors.New("tls: private key does not match public key"))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return fail(errors.New("tls: unknown public key algorithm"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return cert, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
|
|
||||||
// PKCS #1 private keys by default, while OpenSSL 1.0.0 generates PKCS #8 keys.
|
|
||||||
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
|
|
||||||
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
|
|
||||||
if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
|
|
||||||
return key, nil
|
|
||||||
}
|
|
||||||
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
|
|
||||||
switch key := key.(type) {
|
|
||||||
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
|
|
||||||
return key, nil
|
|
||||||
default:
|
|
||||||
return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if key, err := x509.ParseECPrivateKey(der); err == nil {
|
|
||||||
return key, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New("tls: failed to parse private key")
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# tls
|
|
||||||
|
|
||||||
crypto/tls fork for shadowtls v3
|
|
||||||
|
|
||||||
version: go1.19.5
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user