TUN-6637: Upgrade go version and quic-go
This commit is contained in:
parent
7a9207a6e1
commit
046a30e3c7
|
@ -1,7 +1,7 @@
|
||||||
# use a builder image for building cloudflare
|
# use a builder image for building cloudflare
|
||||||
ARG TARGET_GOOS
|
ARG TARGET_GOOS
|
||||||
ARG TARGET_GOARCH
|
ARG TARGET_GOARCH
|
||||||
FROM golang:1.17.1 as builder
|
FROM golang:1.18.1 as builder
|
||||||
ENV GO111MODULE=on \
|
ENV GO111MODULE=on \
|
||||||
CGO_ENABLED=0 \
|
CGO_ENABLED=0 \
|
||||||
TARGET_GOOS=${TARGET_GOOS} \
|
TARGET_GOOS=${TARGET_GOOS} \
|
||||||
|
|
10
cfsetup.yaml
10
cfsetup.yaml
|
@ -1,5 +1,5 @@
|
||||||
pinned_go: &pinned_go go=1.17.10-1
|
pinned_go: &pinned_go go=1.18-1
|
||||||
pinned_go_fips: &pinned_go_fips go-boring=1.17.9-1
|
pinned_go_fips: &pinned_go_fips go-boring=1.18-1
|
||||||
|
|
||||||
build_dir: &build_dir /cfsetup_build
|
build_dir: &build_dir /cfsetup_build
|
||||||
default-flavor: bullseye
|
default-flavor: bullseye
|
||||||
|
@ -152,7 +152,7 @@ stretch: &stretch
|
||||||
- build-essential
|
- build-essential
|
||||||
- gotest-to-teamcity
|
- gotest-to-teamcity
|
||||||
pre-cache: &test_pre_cache
|
pre-cache: &test_pre_cache
|
||||||
- go get golang.org/x/tools/cmd/goimports
|
- go install golang.org/x/tools/cmd/goimports@latest
|
||||||
post-cache:
|
post-cache:
|
||||||
- export GOOS=linux
|
- export GOOS=linux
|
||||||
- export GOARCH=amd64
|
- export GOARCH=amd64
|
||||||
|
@ -255,8 +255,8 @@ centos-7:
|
||||||
pre-cache:
|
pre-cache:
|
||||||
- yum install -y fakeroot
|
- yum install -y fakeroot
|
||||||
- yum upgrade -y binutils-2.27-44.base.el7.x86_64
|
- yum upgrade -y binutils-2.27-44.base.el7.x86_64
|
||||||
- wget https://go.dev/dl/go1.17.10.linux-amd64.tar.gz -P /tmp/
|
- wget https://go.dev/dl/go1.18.1.linux-amd64.tar.gz -P /tmp/
|
||||||
- tar -C /usr/local -xzf /tmp/go1.17.10.linux-amd64.tar.gz
|
- tar -C /usr/local -xzf /tmp/go1.18.1.linux-amd64.tar.gz
|
||||||
post-cache:
|
post-cache:
|
||||||
- export PATH=$PATH:/usr/local/go/bin
|
- export PATH=$PATH:/usr/local/go/bin
|
||||||
- export GOOS=linux
|
- export GOOS=linux
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.17.10 as builder
|
FROM golang:1.18.1 as builder
|
||||||
ENV GO111MODULE=on \
|
ENV GO111MODULE=on \
|
||||||
CGO_ENABLED=0
|
CGO_ENABLED=0
|
||||||
WORKDIR /go/src/github.com/cloudflare/cloudflared/
|
WORKDIR /go/src/github.com/cloudflare/cloudflared/
|
||||||
|
|
17
go.mod
17
go.mod
|
@ -1,6 +1,6 @@
|
||||||
module github.com/cloudflare/cloudflared
|
module github.com/cloudflare/cloudflared
|
||||||
|
|
||||||
go 1.17
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93
|
github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93
|
||||||
|
@ -16,7 +16,7 @@ require (
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/lucas-clemente/quic-go v0.27.1
|
github.com/lucas-clemente/quic-go v0.28.1
|
||||||
github.com/mattn/go-colorable v0.1.8
|
github.com/mattn/go-colorable v0.1.8
|
||||||
github.com/miekg/dns v1.1.45
|
github.com/miekg/dns v1.1.45
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
|
@ -34,10 +34,10 @@ require (
|
||||||
go.opentelemetry.io/proto/otlp v0.15.0
|
go.opentelemetry.io/proto/otlp v0.15.0
|
||||||
go.uber.org/automaxprocs v1.4.0
|
go.uber.org/automaxprocs v1.4.0
|
||||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f
|
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f
|
||||||
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d
|
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
|
||||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||||
google.golang.org/protobuf v1.28.0
|
google.golang.org/protobuf v1.28.0
|
||||||
gopkg.in/coreos/go-oidc.v2 v2.2.1
|
gopkg.in/coreos/go-oidc.v2 v2.2.1
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
|
@ -71,8 +71,9 @@ require (
|
||||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
|
github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
|
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
|
||||||
|
github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
@ -99,7 +100,7 @@ require (
|
||||||
|
|
||||||
replace github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d
|
replace github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d
|
||||||
|
|
||||||
replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da
|
replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220808144024-f036dcbe387e
|
||||||
|
|
||||||
// Avoid 'CVE-2022-21698'
|
// Avoid 'CVE-2022-21698'
|
||||||
replace github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1
|
replace github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1
|
||||||
|
|
28
go.sum
28
go.sum
|
@ -103,15 +103,14 @@ github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 h1:JLaf/iINcLyjwbtTsCJjc6rtlASgHeIJPrB6QmwURnA=
|
github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 h1:JLaf/iINcLyjwbtTsCJjc6rtlASgHeIJPrB6QmwURnA=
|
||||||
github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||||
github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da h1:FmuwbQ8RU/ftTKnfz5diawqvQFH1KDB9wN2Q8S2wqds=
|
github.com/chungthuang/quic-go v0.27.1-0.20220808144024-f036dcbe387e h1:HanU8Gx2eTN9X0miD0HNdl/doTs08ZLQzlQMIrGVHgk=
|
||||||
github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI=
|
github.com/chungthuang/quic-go v0.27.1-0.20220808144024-f036dcbe387e/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
@ -338,7 +337,6 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
|
||||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
|
||||||
|
@ -399,10 +397,12 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
|
||||||
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ=
|
github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ=
|
||||||
github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||||
github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWChPSI4+hgW7jc=
|
github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ=
|
||||||
github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
|
github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.1 h1:qp7p7XXUFL7fpBvSS1sWD+uSqPvzNQK43DH+/qEkj0Y=
|
github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM=
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
|
github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
|
||||||
|
github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 h1:7m/WlWcSROrcK5NxuXaxYD32BZqe/LEEnBrWcH/cOqQ=
|
||||||
|
github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
|
||||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
|
@ -507,7 +507,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
||||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
|
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
|
||||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
@ -707,11 +706,10 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d h1:1n1fc535VhN8SYtD4cDUyNlfpAF2ROMM9+11equK3hs=
|
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
|
||||||
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
@ -815,11 +813,13 @@ golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
|
|
||||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
[![PkgGoDev](https://pkg.go.dev/badge/github.com/lucas-clemente/quic-go)](https://pkg.go.dev/github.com/lucas-clemente/quic-go)
|
[![PkgGoDev](https://pkg.go.dev/badge/github.com/lucas-clemente/quic-go)](https://pkg.go.dev/github.com/lucas-clemente/quic-go)
|
||||||
[![Code Coverage](https://img.shields.io/codecov/c/github/lucas-clemente/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/lucas-clemente/quic-go/)
|
[![Code Coverage](https://img.shields.io/codecov/c/github/lucas-clemente/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/lucas-clemente/quic-go/)
|
||||||
|
|
||||||
quic-go is an implementation of the [QUIC protocol, RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000) protocol in Go.
|
quic-go is an implementation of the QUIC protocol ([RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000), [RFC 9001](https://datatracker.ietf.org/doc/html/rfc9001), [RFC 9002](https://datatracker.ietf.org/doc/html/rfc9002)) in Go, including the [Unreliable Datagram Extension, RFC 9221](https://datatracker.ietf.org/doc/html/rfc9221). It has support for HTTP/3 [RFC 9114](https://datatracker.ietf.org/doc/html/rfc9114).
|
||||||
In addition to RFC 9000, it currently implements the [IETF QUIC draft-29](https://tools.ietf.org/html/draft-ietf-quic-transport-29). Support for draft-29 will eventually be dropped, as it is phased out of the ecosystem.
|
|
||||||
|
In addition the RFCs listed above, it currently implements the [IETF QUIC draft-29](https://tools.ietf.org/html/draft-ietf-quic-transport-29). Support for draft-29 will eventually be dropped, as it is phased out of the ecosystem.
|
||||||
|
|
||||||
## Guides
|
## Guides
|
||||||
|
|
||||||
|
|
|
@ -231,6 +231,8 @@ func newClient(
|
||||||
) (*client, error) {
|
) (*client, error) {
|
||||||
if tlsConf == nil {
|
if tlsConf == nil {
|
||||||
tlsConf = &tls.Config{}
|
tlsConf = &tls.Config{}
|
||||||
|
} else {
|
||||||
|
tlsConf = tlsConf.Clone()
|
||||||
}
|
}
|
||||||
if tlsConf.ServerName == "" {
|
if tlsConf.ServerName == "" {
|
||||||
sni := host
|
sni := host
|
||||||
|
|
|
@ -190,6 +190,7 @@ type connection struct {
|
||||||
clientHelloWritten <-chan *wire.TransportParameters
|
clientHelloWritten <-chan *wire.TransportParameters
|
||||||
earlyConnReadyChan chan struct{}
|
earlyConnReadyChan chan struct{}
|
||||||
handshakeCompleteChan chan struct{} // is closed when the handshake completes
|
handshakeCompleteChan chan struct{} // is closed when the handshake completes
|
||||||
|
sentFirstPacket bool
|
||||||
handshakeComplete bool
|
handshakeComplete bool
|
||||||
handshakeConfirmed bool
|
handshakeConfirmed bool
|
||||||
|
|
||||||
|
@ -1522,6 +1523,12 @@ func (s *connection) handleCloseError(closeErr *closeError) {
|
||||||
s.connIDGenerator.RemoveAll()
|
s.connIDGenerator.RemoveAll()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Don't send out any CONNECTION_CLOSE if this is an error that occurred
|
||||||
|
// before we even sent out the first packet.
|
||||||
|
if s.perspective == protocol.PerspectiveClient && !s.sentFirstPacket {
|
||||||
|
s.connIDGenerator.RemoveAll()
|
||||||
|
return
|
||||||
|
}
|
||||||
connClosePacket, err := s.sendConnectionClose(e)
|
connClosePacket, err := s.sendConnectionClose(e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err)
|
s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err)
|
||||||
|
@ -1763,6 +1770,7 @@ func (s *connection) sendPacket() (bool, error) {
|
||||||
if err != nil || packet == nil {
|
if err != nil || packet == nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
s.sentFirstPacket = true
|
||||||
s.logCoalescedPacket(packet)
|
s.logCoalescedPacket(packet)
|
||||||
for _, p := range packet.packets {
|
for _, p := range packet.packets {
|
||||||
if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() {
|
if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() {
|
||||||
|
|
|
@ -23,6 +23,7 @@ const (
|
||||||
VersionDraft29 = protocol.VersionDraft29
|
VersionDraft29 = protocol.VersionDraft29
|
||||||
// Version1 is RFC 9000
|
// Version1 is RFC 9000
|
||||||
Version1 = protocol.Version1
|
Version1 = protocol.Version1
|
||||||
|
Version2 = protocol.Version2
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Token can be used to verify the ownership of the client address.
|
// A Token can be used to verify the ownership of the client address.
|
||||||
|
@ -127,7 +128,6 @@ type SendStream interface {
|
||||||
// The Context is canceled as soon as the write-side of the stream is closed.
|
// The Context is canceled as soon as the write-side of the stream is closed.
|
||||||
// This happens when Close() or CancelWrite() is called, or when the peer
|
// This happens when Close() or CancelWrite() is called, or when the peer
|
||||||
// cancels the read-side of their stream.
|
// cancels the read-side of their stream.
|
||||||
// Warning: This API should not be considered stable and might change soon.
|
|
||||||
Context() context.Context
|
Context() context.Context
|
||||||
// SetWriteDeadline sets the deadline for future Write calls
|
// SetWriteDeadline sets the deadline for future Write calls
|
||||||
// and any currently-blocked Write call.
|
// and any currently-blocked Write call.
|
||||||
|
@ -184,18 +184,15 @@ type Connection interface {
|
||||||
// The error string will be sent to the peer.
|
// The error string will be sent to the peer.
|
||||||
CloseWithError(ApplicationErrorCode, string) error
|
CloseWithError(ApplicationErrorCode, string) error
|
||||||
// The context is cancelled when the connection is closed.
|
// The context is cancelled when the connection is closed.
|
||||||
// Warning: This API should not be considered stable and might change soon.
|
|
||||||
Context() context.Context
|
Context() context.Context
|
||||||
// ConnectionState returns basic details about the QUIC connection.
|
// ConnectionState returns basic details about the QUIC connection.
|
||||||
// It blocks until the handshake completes.
|
// It blocks until the handshake completes.
|
||||||
// Warning: This API should not be considered stable and might change soon.
|
// Warning: This API should not be considered stable and might change soon.
|
||||||
ConnectionState() ConnectionState
|
ConnectionState() ConnectionState
|
||||||
|
|
||||||
// SendMessage sends a message as a datagram.
|
// SendMessage sends a message as a datagram, as specified in RFC 9221.
|
||||||
// See https://datatracker.ietf.org/doc/draft-pauly-quic-datagram/.
|
|
||||||
SendMessage([]byte) error
|
SendMessage([]byte) error
|
||||||
// ReceiveMessage gets a message received in a datagram.
|
// ReceiveMessage gets a message received in a datagram, as specified in RFC 9221.
|
||||||
// See https://datatracker.ietf.org/doc/draft-pauly-quic-datagram/.
|
|
||||||
ReceiveMessage() ([]byte, error)
|
ReceiveMessage() ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,7 +215,6 @@ type EarlyConnection interface {
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// The QUIC versions that can be negotiated.
|
// The QUIC versions that can be negotiated.
|
||||||
// If not set, it uses all versions available.
|
// If not set, it uses all versions available.
|
||||||
// Warning: This API should not be considered stable and will change soon.
|
|
||||||
Versions []VersionNumber
|
Versions []VersionNumber
|
||||||
// The length of the connection ID in bytes.
|
// The length of the connection ID in bytes.
|
||||||
// It can be 0, or any value between 4 and 18.
|
// It can be 0, or any value between 4 and 18.
|
||||||
|
|
|
@ -23,6 +23,10 @@ type Packet struct {
|
||||||
skippedPacket bool
|
skippedPacket bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Packet) outstanding() bool {
|
||||||
|
return !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket
|
||||||
|
}
|
||||||
|
|
||||||
// SentPacketHandler handles ACKs received for outgoing packets
|
// SentPacketHandler handles ACKs received for outgoing packets
|
||||||
type SentPacketHandler interface {
|
type SentPacketHandler interface {
|
||||||
// SentPacket may modify the packet
|
// SentPacket may modify the packet
|
||||||
|
|
4
vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_handler.go
generated
vendored
4
vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_handler.go
generated
vendored
|
@ -598,7 +598,7 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E
|
||||||
pnSpace.lossTime = lossTime
|
pnSpace.lossTime = lossTime
|
||||||
}
|
}
|
||||||
if packetLost {
|
if packetLost {
|
||||||
p.declaredLost = true
|
p = pnSpace.history.DeclareLost(p)
|
||||||
// the bytes in flight need to be reduced no matter if the frames in this packet will be retransmitted
|
// the bytes in flight need to be reduced no matter if the frames in this packet will be retransmitted
|
||||||
h.removeFromBytesInFlight(p)
|
h.removeFromBytesInFlight(p)
|
||||||
h.queueFramesForRetransmission(p)
|
h.queueFramesForRetransmission(p)
|
||||||
|
@ -767,7 +767,7 @@ func (h *sentPacketHandler) QueueProbePacket(encLevel protocol.EncryptionLevel)
|
||||||
// TODO: don't declare the packet lost here.
|
// TODO: don't declare the packet lost here.
|
||||||
// Keep track of acknowledged frames instead.
|
// Keep track of acknowledged frames instead.
|
||||||
h.removeFromBytesInFlight(p)
|
h.removeFromBytesInFlight(p)
|
||||||
p.declaredLost = true
|
pnSpace.history.DeclareLost(p)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
87
vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_history.go
generated
vendored
87
vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_history.go
generated
vendored
|
@ -10,7 +10,8 @@ import (
|
||||||
|
|
||||||
type sentPacketHistory struct {
|
type sentPacketHistory struct {
|
||||||
rttStats *utils.RTTStats
|
rttStats *utils.RTTStats
|
||||||
packetList *PacketList
|
outstandingPacketList *PacketList
|
||||||
|
etcPacketList *PacketList
|
||||||
packetMap map[protocol.PacketNumber]*PacketElement
|
packetMap map[protocol.PacketNumber]*PacketElement
|
||||||
highestSent protocol.PacketNumber
|
highestSent protocol.PacketNumber
|
||||||
}
|
}
|
||||||
|
@ -18,7 +19,8 @@ type sentPacketHistory struct {
|
||||||
func newSentPacketHistory(rttStats *utils.RTTStats) *sentPacketHistory {
|
func newSentPacketHistory(rttStats *utils.RTTStats) *sentPacketHistory {
|
||||||
return &sentPacketHistory{
|
return &sentPacketHistory{
|
||||||
rttStats: rttStats,
|
rttStats: rttStats,
|
||||||
packetList: NewPacketList(),
|
outstandingPacketList: NewPacketList(),
|
||||||
|
etcPacketList: NewPacketList(),
|
||||||
packetMap: make(map[protocol.PacketNumber]*PacketElement),
|
packetMap: make(map[protocol.PacketNumber]*PacketElement),
|
||||||
highestSent: protocol.InvalidPacketNumber,
|
highestSent: protocol.InvalidPacketNumber,
|
||||||
}
|
}
|
||||||
|
@ -30,7 +32,7 @@ func (h *sentPacketHistory) SentPacket(p *Packet, isAckEliciting bool) {
|
||||||
}
|
}
|
||||||
// Skipped packet numbers.
|
// Skipped packet numbers.
|
||||||
for pn := h.highestSent + 1; pn < p.PacketNumber; pn++ {
|
for pn := h.highestSent + 1; pn < p.PacketNumber; pn++ {
|
||||||
el := h.packetList.PushBack(Packet{
|
el := h.etcPacketList.PushBack(Packet{
|
||||||
PacketNumber: pn,
|
PacketNumber: pn,
|
||||||
EncryptionLevel: p.EncryptionLevel,
|
EncryptionLevel: p.EncryptionLevel,
|
||||||
SendTime: p.SendTime,
|
SendTime: p.SendTime,
|
||||||
|
@ -41,7 +43,12 @@ func (h *sentPacketHistory) SentPacket(p *Packet, isAckEliciting bool) {
|
||||||
h.highestSent = p.PacketNumber
|
h.highestSent = p.PacketNumber
|
||||||
|
|
||||||
if isAckEliciting {
|
if isAckEliciting {
|
||||||
el := h.packetList.PushBack(*p)
|
var el *PacketElement
|
||||||
|
if p.outstanding() {
|
||||||
|
el = h.outstandingPacketList.PushBack(*p)
|
||||||
|
} else {
|
||||||
|
el = h.etcPacketList.PushBack(*p)
|
||||||
|
}
|
||||||
h.packetMap[p.PacketNumber] = el
|
h.packetMap[p.PacketNumber] = el
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,10 +56,25 @@ func (h *sentPacketHistory) SentPacket(p *Packet, isAckEliciting bool) {
|
||||||
// Iterate iterates through all packets.
|
// Iterate iterates through all packets.
|
||||||
func (h *sentPacketHistory) Iterate(cb func(*Packet) (cont bool, err error)) error {
|
func (h *sentPacketHistory) Iterate(cb func(*Packet) (cont bool, err error)) error {
|
||||||
cont := true
|
cont := true
|
||||||
var next *PacketElement
|
outstandingEl := h.outstandingPacketList.Front()
|
||||||
for el := h.packetList.Front(); cont && el != nil; el = next {
|
etcEl := h.etcPacketList.Front()
|
||||||
|
var el *PacketElement
|
||||||
|
// whichever has the next packet number is returned first
|
||||||
|
for cont {
|
||||||
|
if outstandingEl == nil || (etcEl != nil && etcEl.Value.PacketNumber < outstandingEl.Value.PacketNumber) {
|
||||||
|
el = etcEl
|
||||||
|
} else {
|
||||||
|
el = outstandingEl
|
||||||
|
}
|
||||||
|
if el == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if el == outstandingEl {
|
||||||
|
outstandingEl = outstandingEl.Next()
|
||||||
|
} else {
|
||||||
|
etcEl = etcEl.Next()
|
||||||
|
}
|
||||||
var err error
|
var err error
|
||||||
next = el.Next()
|
|
||||||
cont, err = cb(&el.Value)
|
cont, err = cb(&el.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -61,16 +83,14 @@ func (h *sentPacketHistory) Iterate(cb func(*Packet) (cont bool, err error)) err
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FirstOutStanding returns the first outstanding packet.
|
// FirstOutstanding returns the first outstanding packet.
|
||||||
func (h *sentPacketHistory) FirstOutstanding() *Packet {
|
func (h *sentPacketHistory) FirstOutstanding() *Packet {
|
||||||
for el := h.packetList.Front(); el != nil; el = el.Next() {
|
el := h.outstandingPacketList.Front()
|
||||||
p := &el.Value
|
if el == nil {
|
||||||
if !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return &el.Value
|
||||||
|
}
|
||||||
|
|
||||||
func (h *sentPacketHistory) Len() int {
|
func (h *sentPacketHistory) Len() int {
|
||||||
return len(h.packetMap)
|
return len(h.packetMap)
|
||||||
|
@ -81,28 +101,53 @@ func (h *sentPacketHistory) Remove(p protocol.PacketNumber) error {
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("packet %d not found in sent packet history", p)
|
return fmt.Errorf("packet %d not found in sent packet history", p)
|
||||||
}
|
}
|
||||||
h.packetList.Remove(el)
|
h.outstandingPacketList.Remove(el)
|
||||||
|
h.etcPacketList.Remove(el)
|
||||||
delete(h.packetMap, p)
|
delete(h.packetMap, p)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *sentPacketHistory) HasOutstandingPackets() bool {
|
func (h *sentPacketHistory) HasOutstandingPackets() bool {
|
||||||
return h.FirstOutstanding() != nil
|
return h.outstandingPacketList.Len() > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *sentPacketHistory) DeleteOldPackets(now time.Time) {
|
func (h *sentPacketHistory) DeleteOldPackets(now time.Time) {
|
||||||
maxAge := 3 * h.rttStats.PTO(false)
|
maxAge := 3 * h.rttStats.PTO(false)
|
||||||
var nextEl *PacketElement
|
var nextEl *PacketElement
|
||||||
for el := h.packetList.Front(); el != nil; el = nextEl {
|
// we don't iterate outstandingPacketList, as we should not delete outstanding packets.
|
||||||
|
// being outstanding for more than 3*PTO should only happen in the case of drastic RTT changes.
|
||||||
|
for el := h.etcPacketList.Front(); el != nil; el = nextEl {
|
||||||
nextEl = el.Next()
|
nextEl = el.Next()
|
||||||
p := el.Value
|
p := el.Value
|
||||||
if p.SendTime.After(now.Add(-maxAge)) {
|
if p.SendTime.After(now.Add(-maxAge)) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if !p.skippedPacket && !p.declaredLost { // should only happen in the case of drastic RTT changes
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
delete(h.packetMap, p.PacketNumber)
|
delete(h.packetMap, p.PacketNumber)
|
||||||
h.packetList.Remove(el)
|
h.etcPacketList.Remove(el)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *sentPacketHistory) DeclareLost(p *Packet) *Packet {
|
||||||
|
el, ok := h.packetMap[p.PacketNumber]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// try to remove it from both lists, as we don't know which one it currently belongs to.
|
||||||
|
// Remove is a no-op for elements that are not in the list.
|
||||||
|
h.outstandingPacketList.Remove(el)
|
||||||
|
h.etcPacketList.Remove(el)
|
||||||
|
p.declaredLost = true
|
||||||
|
// move it to the correct position in the etc list (based on the packet number)
|
||||||
|
for el = h.etcPacketList.Back(); el != nil; el = el.Prev() {
|
||||||
|
if el.Value.PacketNumber < p.PacketNumber {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if el == nil {
|
||||||
|
el = h.etcPacketList.PushFront(*p)
|
||||||
|
} else {
|
||||||
|
el = h.etcPacketList.InsertAfter(*p, el)
|
||||||
|
}
|
||||||
|
h.packetMap[p.PacketNumber] = el
|
||||||
|
return &el.Value
|
||||||
|
}
|
||||||
|
|
|
@ -9,9 +9,15 @@ import (
|
||||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createAEAD(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) cipher.AEAD {
|
func createAEAD(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, v protocol.VersionNumber) cipher.AEAD {
|
||||||
key := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, "quic key", suite.KeyLen)
|
keyLabel := hkdfLabelKeyV1
|
||||||
iv := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, "quic iv", suite.IVLen())
|
ivLabel := hkdfLabelIVV1
|
||||||
|
if v == protocol.Version2 {
|
||||||
|
keyLabel = hkdfLabelKeyV2
|
||||||
|
ivLabel = hkdfLabelIVV2
|
||||||
|
}
|
||||||
|
key := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, keyLabel, suite.KeyLen)
|
||||||
|
iv := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, ivLabel, suite.IVLen())
|
||||||
return suite.AEAD(key, iv)
|
return suite.AEAD(key, iv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,8 @@ type cryptoSetup struct {
|
||||||
|
|
||||||
zeroRTTParameters *wire.TransportParameters
|
zeroRTTParameters *wire.TransportParameters
|
||||||
clientHelloWritten bool
|
clientHelloWritten bool
|
||||||
clientHelloWrittenChan chan *wire.TransportParameters
|
clientHelloWrittenChan chan struct{} // is closed as soon as the ClientHello is written
|
||||||
|
zeroRTTParametersChan chan<- *wire.TransportParameters
|
||||||
|
|
||||||
rttStats *utils.RTTStats
|
rttStats *utils.RTTStats
|
||||||
|
|
||||||
|
@ -238,13 +239,14 @@ func newCryptoSetup(
|
||||||
tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer)
|
tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer)
|
||||||
}
|
}
|
||||||
extHandler := newExtensionHandler(tp.Marshal(perspective), perspective, version)
|
extHandler := newExtensionHandler(tp.Marshal(perspective), perspective, version)
|
||||||
|
zeroRTTParametersChan := make(chan *wire.TransportParameters, 1)
|
||||||
cs := &cryptoSetup{
|
cs := &cryptoSetup{
|
||||||
tlsConf: tlsConf,
|
tlsConf: tlsConf,
|
||||||
initialStream: initialStream,
|
initialStream: initialStream,
|
||||||
initialSealer: initialSealer,
|
initialSealer: initialSealer,
|
||||||
initialOpener: initialOpener,
|
initialOpener: initialOpener,
|
||||||
handshakeStream: handshakeStream,
|
handshakeStream: handshakeStream,
|
||||||
aead: newUpdatableAEAD(rttStats, tracer, logger),
|
aead: newUpdatableAEAD(rttStats, tracer, logger, version),
|
||||||
readEncLevel: protocol.EncryptionInitial,
|
readEncLevel: protocol.EncryptionInitial,
|
||||||
writeEncLevel: protocol.EncryptionInitial,
|
writeEncLevel: protocol.EncryptionInitial,
|
||||||
runner: runner,
|
runner: runner,
|
||||||
|
@ -256,7 +258,8 @@ func newCryptoSetup(
|
||||||
perspective: perspective,
|
perspective: perspective,
|
||||||
handshakeDone: make(chan struct{}),
|
handshakeDone: make(chan struct{}),
|
||||||
alertChan: make(chan uint8),
|
alertChan: make(chan uint8),
|
||||||
clientHelloWrittenChan: make(chan *wire.TransportParameters, 1),
|
clientHelloWrittenChan: make(chan struct{}),
|
||||||
|
zeroRTTParametersChan: zeroRTTParametersChan,
|
||||||
messageChan: make(chan []byte, 100),
|
messageChan: make(chan []byte, 100),
|
||||||
isReadingHandshakeMessage: make(chan struct{}),
|
isReadingHandshakeMessage: make(chan struct{}),
|
||||||
closeChan: make(chan struct{}),
|
closeChan: make(chan struct{}),
|
||||||
|
@ -278,7 +281,7 @@ func newCryptoSetup(
|
||||||
GetAppDataForSessionState: cs.marshalDataForSessionState,
|
GetAppDataForSessionState: cs.marshalDataForSessionState,
|
||||||
SetAppDataFromSessionState: cs.handleDataFromSessionState,
|
SetAppDataFromSessionState: cs.handleDataFromSessionState,
|
||||||
}
|
}
|
||||||
return cs, cs.clientHelloWrittenChan
|
return cs, zeroRTTParametersChan
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *cryptoSetup) ChangeConnectionID(id protocol.ConnectionID) {
|
func (h *cryptoSetup) ChangeConnectionID(id protocol.ConnectionID) {
|
||||||
|
@ -308,6 +311,15 @@ func (h *cryptoSetup) RunHandshake() {
|
||||||
close(handshakeComplete)
|
close(handshakeComplete)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if h.perspective == protocol.PerspectiveClient {
|
||||||
|
select {
|
||||||
|
case err := <-handshakeErrChan:
|
||||||
|
h.onError(0, err.Error())
|
||||||
|
return
|
||||||
|
case <-h.clientHelloWrittenChan:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-handshakeComplete: // return when the handshake is done
|
case <-handshakeComplete: // return when the handshake is done
|
||||||
h.mutex.Lock()
|
h.mutex.Lock()
|
||||||
|
@ -324,7 +336,13 @@ func (h *cryptoSetup) RunHandshake() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *cryptoSetup) onError(alert uint8, message string) {
|
func (h *cryptoSetup) onError(alert uint8, message string) {
|
||||||
h.runner.OnError(qerr.NewCryptoError(alert, message))
|
var err error
|
||||||
|
if alert == 0 {
|
||||||
|
err = &qerr.TransportError{ErrorCode: qerr.InternalError, ErrorMessage: message}
|
||||||
|
} else {
|
||||||
|
err = qerr.NewCryptoError(alert, message)
|
||||||
|
}
|
||||||
|
h.runner.OnError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the crypto setup.
|
// Close closes the crypto setup.
|
||||||
|
@ -554,8 +572,8 @@ func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.Ciph
|
||||||
panic("Received 0-RTT read key for the client")
|
panic("Received 0-RTT read key for the client")
|
||||||
}
|
}
|
||||||
h.zeroRTTOpener = newLongHeaderOpener(
|
h.zeroRTTOpener = newLongHeaderOpener(
|
||||||
createAEAD(suite, trafficSecret),
|
createAEAD(suite, trafficSecret, h.version),
|
||||||
newHeaderProtector(suite, trafficSecret, true),
|
newHeaderProtector(suite, trafficSecret, true, h.version),
|
||||||
)
|
)
|
||||||
h.mutex.Unlock()
|
h.mutex.Unlock()
|
||||||
h.logger.Debugf("Installed 0-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID))
|
h.logger.Debugf("Installed 0-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID))
|
||||||
|
@ -566,8 +584,8 @@ func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.Ciph
|
||||||
case qtls.EncryptionHandshake:
|
case qtls.EncryptionHandshake:
|
||||||
h.readEncLevel = protocol.EncryptionHandshake
|
h.readEncLevel = protocol.EncryptionHandshake
|
||||||
h.handshakeOpener = newHandshakeOpener(
|
h.handshakeOpener = newHandshakeOpener(
|
||||||
createAEAD(suite, trafficSecret),
|
createAEAD(suite, trafficSecret, h.version),
|
||||||
newHeaderProtector(suite, trafficSecret, true),
|
newHeaderProtector(suite, trafficSecret, true, h.version),
|
||||||
h.dropInitialKeys,
|
h.dropInitialKeys,
|
||||||
h.perspective,
|
h.perspective,
|
||||||
)
|
)
|
||||||
|
@ -594,8 +612,8 @@ func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.Cip
|
||||||
panic("Received 0-RTT write key for the server")
|
panic("Received 0-RTT write key for the server")
|
||||||
}
|
}
|
||||||
h.zeroRTTSealer = newLongHeaderSealer(
|
h.zeroRTTSealer = newLongHeaderSealer(
|
||||||
createAEAD(suite, trafficSecret),
|
createAEAD(suite, trafficSecret, h.version),
|
||||||
newHeaderProtector(suite, trafficSecret, true),
|
newHeaderProtector(suite, trafficSecret, true, h.version),
|
||||||
)
|
)
|
||||||
h.mutex.Unlock()
|
h.mutex.Unlock()
|
||||||
h.logger.Debugf("Installed 0-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID))
|
h.logger.Debugf("Installed 0-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID))
|
||||||
|
@ -606,8 +624,8 @@ func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.Cip
|
||||||
case qtls.EncryptionHandshake:
|
case qtls.EncryptionHandshake:
|
||||||
h.writeEncLevel = protocol.EncryptionHandshake
|
h.writeEncLevel = protocol.EncryptionHandshake
|
||||||
h.handshakeSealer = newHandshakeSealer(
|
h.handshakeSealer = newHandshakeSealer(
|
||||||
createAEAD(suite, trafficSecret),
|
createAEAD(suite, trafficSecret, h.version),
|
||||||
newHeaderProtector(suite, trafficSecret, true),
|
newHeaderProtector(suite, trafficSecret, true, h.version),
|
||||||
h.dropInitialKeys,
|
h.dropInitialKeys,
|
||||||
h.perspective,
|
h.perspective,
|
||||||
)
|
)
|
||||||
|
@ -645,12 +663,13 @@ func (h *cryptoSetup) WriteRecord(p []byte) (int, error) {
|
||||||
n, err := h.initialStream.Write(p)
|
n, err := h.initialStream.Write(p)
|
||||||
if !h.clientHelloWritten && h.perspective == protocol.PerspectiveClient {
|
if !h.clientHelloWritten && h.perspective == protocol.PerspectiveClient {
|
||||||
h.clientHelloWritten = true
|
h.clientHelloWritten = true
|
||||||
|
close(h.clientHelloWrittenChan)
|
||||||
if h.zeroRTTSealer != nil && h.zeroRTTParameters != nil {
|
if h.zeroRTTSealer != nil && h.zeroRTTParameters != nil {
|
||||||
h.logger.Debugf("Doing 0-RTT.")
|
h.logger.Debugf("Doing 0-RTT.")
|
||||||
h.clientHelloWrittenChan <- h.zeroRTTParameters
|
h.zeroRTTParametersChan <- h.zeroRTTParameters
|
||||||
} else {
|
} else {
|
||||||
h.logger.Debugf("Not doing 0-RTT.")
|
h.logger.Debugf("Not doing 0-RTT.")
|
||||||
h.clientHelloWrittenChan <- nil
|
h.zeroRTTParametersChan <- nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return n, err
|
return n, err
|
||||||
|
|
23
vendor/github.com/lucas-clemente/quic-go/internal/handshake/header_protector.go
generated
vendored
23
vendor/github.com/lucas-clemente/quic-go/internal/handshake/header_protector.go
generated
vendored
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"golang.org/x/crypto/chacha20"
|
"golang.org/x/crypto/chacha20"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||||
"github.com/lucas-clemente/quic-go/internal/qtls"
|
"github.com/lucas-clemente/quic-go/internal/qtls"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,12 +18,20 @@ type headerProtector interface {
|
||||||
DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte)
|
DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool) headerProtector {
|
func hkdfHeaderProtectionLabel(v protocol.VersionNumber) string {
|
||||||
|
if v == protocol.Version2 {
|
||||||
|
return "quicv2 hp"
|
||||||
|
}
|
||||||
|
return "quic hp"
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool, v protocol.VersionNumber) headerProtector {
|
||||||
|
hkdfLabel := hkdfHeaderProtectionLabel(v)
|
||||||
switch suite.ID {
|
switch suite.ID {
|
||||||
case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384:
|
case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384:
|
||||||
return newAESHeaderProtector(suite, trafficSecret, isLongHeader)
|
return newAESHeaderProtector(suite, trafficSecret, isLongHeader, hkdfLabel)
|
||||||
case tls.TLS_CHACHA20_POLY1305_SHA256:
|
case tls.TLS_CHACHA20_POLY1305_SHA256:
|
||||||
return newChaChaHeaderProtector(suite, trafficSecret, isLongHeader)
|
return newChaChaHeaderProtector(suite, trafficSecret, isLongHeader, hkdfLabel)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("Invalid cipher suite id: %d", suite.ID))
|
panic(fmt.Sprintf("Invalid cipher suite id: %d", suite.ID))
|
||||||
}
|
}
|
||||||
|
@ -36,8 +45,8 @@ type aesHeaderProtector struct {
|
||||||
|
|
||||||
var _ headerProtector = &aesHeaderProtector{}
|
var _ headerProtector = &aesHeaderProtector{}
|
||||||
|
|
||||||
func newAESHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool) headerProtector {
|
func newAESHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector {
|
||||||
hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, "quic hp", suite.KeyLen)
|
hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen)
|
||||||
block, err := aes.NewCipher(hpKey)
|
block, err := aes.NewCipher(hpKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("error creating new AES cipher: %s", err))
|
panic(fmt.Sprintf("error creating new AES cipher: %s", err))
|
||||||
|
@ -81,8 +90,8 @@ type chachaHeaderProtector struct {
|
||||||
|
|
||||||
var _ headerProtector = &chachaHeaderProtector{}
|
var _ headerProtector = &chachaHeaderProtector{}
|
||||||
|
|
||||||
func newChaChaHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool) headerProtector {
|
func newChaChaHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector {
|
||||||
hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, "quic hp", suite.KeyLen)
|
hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen)
|
||||||
|
|
||||||
p := &chachaHeaderProtector{
|
p := &chachaHeaderProtector{
|
||||||
isLongHeader: isLongHeader,
|
isLongHeader: isLongHeader,
|
||||||
|
|
|
@ -12,12 +12,23 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
quicSaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99}
|
quicSaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99}
|
||||||
quicSalt = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a}
|
quicSaltV1 = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a}
|
||||||
|
quicSaltV2 = []byte{0xa7, 0x07, 0xc2, 0x03, 0xa5, 0x9b, 0x47, 0x18, 0x4a, 0x1d, 0x62, 0xca, 0x57, 0x04, 0x06, 0xea, 0x7a, 0xe3, 0xe5, 0xd3}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
hkdfLabelKeyV1 = "quic key"
|
||||||
|
hkdfLabelKeyV2 = "quicv2 key"
|
||||||
|
hkdfLabelIVV1 = "quic iv"
|
||||||
|
hkdfLabelIVV2 = "quicv2 iv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getSalt(v protocol.VersionNumber) []byte {
|
func getSalt(v protocol.VersionNumber) []byte {
|
||||||
|
if v == protocol.Version2 {
|
||||||
|
return quicSaltV2
|
||||||
|
}
|
||||||
if v == protocol.Version1 {
|
if v == protocol.Version1 {
|
||||||
return quicSalt
|
return quicSaltV1
|
||||||
}
|
}
|
||||||
return quicSaltOld
|
return quicSaltOld
|
||||||
}
|
}
|
||||||
|
@ -40,14 +51,14 @@ func NewInitialAEAD(connID protocol.ConnectionID, pers protocol.Perspective, v p
|
||||||
mySecret = serverSecret
|
mySecret = serverSecret
|
||||||
otherSecret = clientSecret
|
otherSecret = clientSecret
|
||||||
}
|
}
|
||||||
myKey, myIV := computeInitialKeyAndIV(mySecret)
|
myKey, myIV := computeInitialKeyAndIV(mySecret, v)
|
||||||
otherKey, otherIV := computeInitialKeyAndIV(otherSecret)
|
otherKey, otherIV := computeInitialKeyAndIV(otherSecret, v)
|
||||||
|
|
||||||
encrypter := qtls.AEADAESGCMTLS13(myKey, myIV)
|
encrypter := qtls.AEADAESGCMTLS13(myKey, myIV)
|
||||||
decrypter := qtls.AEADAESGCMTLS13(otherKey, otherIV)
|
decrypter := qtls.AEADAESGCMTLS13(otherKey, otherIV)
|
||||||
|
|
||||||
return newLongHeaderSealer(encrypter, newHeaderProtector(initialSuite, mySecret, true)),
|
return newLongHeaderSealer(encrypter, newHeaderProtector(initialSuite, mySecret, true, v)),
|
||||||
newLongHeaderOpener(decrypter, newAESHeaderProtector(initialSuite, otherSecret, true))
|
newLongHeaderOpener(decrypter, newAESHeaderProtector(initialSuite, otherSecret, true, hkdfHeaderProtectionLabel(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func computeSecrets(connID protocol.ConnectionID, v protocol.VersionNumber) (clientSecret, serverSecret []byte) {
|
func computeSecrets(connID protocol.ConnectionID, v protocol.VersionNumber) (clientSecret, serverSecret []byte) {
|
||||||
|
@ -57,8 +68,14 @@ func computeSecrets(connID protocol.ConnectionID, v protocol.VersionNumber) (cli
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func computeInitialKeyAndIV(secret []byte) (key, iv []byte) {
|
func computeInitialKeyAndIV(secret []byte, v protocol.VersionNumber) (key, iv []byte) {
|
||||||
key = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic key", 16)
|
keyLabel := hkdfLabelKeyV1
|
||||||
iv = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic iv", 12)
|
ivLabel := hkdfLabelIVV1
|
||||||
|
if v == protocol.Version2 {
|
||||||
|
keyLabel = hkdfLabelKeyV2
|
||||||
|
ivLabel = hkdfLabelIVV2
|
||||||
|
}
|
||||||
|
key = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, keyLabel, 16)
|
||||||
|
iv = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, ivLabel, 12)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ type updatableAEAD struct {
|
||||||
|
|
||||||
tracer logging.ConnectionTracer
|
tracer logging.ConnectionTracer
|
||||||
logger utils.Logger
|
logger utils.Logger
|
||||||
|
version protocol.VersionNumber
|
||||||
|
|
||||||
// use a single slice to avoid allocations
|
// use a single slice to avoid allocations
|
||||||
nonceBuf []byte
|
nonceBuf []byte
|
||||||
|
@ -67,7 +68,7 @@ var (
|
||||||
_ ShortHeaderSealer = &updatableAEAD{}
|
_ ShortHeaderSealer = &updatableAEAD{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func newUpdatableAEAD(rttStats *utils.RTTStats, tracer logging.ConnectionTracer, logger utils.Logger) *updatableAEAD {
|
func newUpdatableAEAD(rttStats *utils.RTTStats, tracer logging.ConnectionTracer, logger utils.Logger, version protocol.VersionNumber) *updatableAEAD {
|
||||||
return &updatableAEAD{
|
return &updatableAEAD{
|
||||||
firstPacketNumber: protocol.InvalidPacketNumber,
|
firstPacketNumber: protocol.InvalidPacketNumber,
|
||||||
largestAcked: protocol.InvalidPacketNumber,
|
largestAcked: protocol.InvalidPacketNumber,
|
||||||
|
@ -77,6 +78,7 @@ func newUpdatableAEAD(rttStats *utils.RTTStats, tracer logging.ConnectionTracer,
|
||||||
rttStats: rttStats,
|
rttStats: rttStats,
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
version: version,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,8 +102,8 @@ func (a *updatableAEAD) rollKeys() {
|
||||||
|
|
||||||
a.nextRcvTrafficSecret = a.getNextTrafficSecret(a.suite.Hash, a.nextRcvTrafficSecret)
|
a.nextRcvTrafficSecret = a.getNextTrafficSecret(a.suite.Hash, a.nextRcvTrafficSecret)
|
||||||
a.nextSendTrafficSecret = a.getNextTrafficSecret(a.suite.Hash, a.nextSendTrafficSecret)
|
a.nextSendTrafficSecret = a.getNextTrafficSecret(a.suite.Hash, a.nextSendTrafficSecret)
|
||||||
a.nextRcvAEAD = createAEAD(a.suite, a.nextRcvTrafficSecret)
|
a.nextRcvAEAD = createAEAD(a.suite, a.nextRcvTrafficSecret, a.version)
|
||||||
a.nextSendAEAD = createAEAD(a.suite, a.nextSendTrafficSecret)
|
a.nextSendAEAD = createAEAD(a.suite, a.nextSendTrafficSecret, a.version)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *updatableAEAD) startKeyDropTimer(now time.Time) {
|
func (a *updatableAEAD) startKeyDropTimer(now time.Time) {
|
||||||
|
@ -117,27 +119,27 @@ func (a *updatableAEAD) getNextTrafficSecret(hash crypto.Hash, ts []byte) []byte
|
||||||
// For the client, this function is called before SetWriteKey.
|
// For the client, this function is called before SetWriteKey.
|
||||||
// For the server, this function is called after SetWriteKey.
|
// For the server, this function is called after SetWriteKey.
|
||||||
func (a *updatableAEAD) SetReadKey(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) {
|
func (a *updatableAEAD) SetReadKey(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) {
|
||||||
a.rcvAEAD = createAEAD(suite, trafficSecret)
|
a.rcvAEAD = createAEAD(suite, trafficSecret, a.version)
|
||||||
a.headerDecrypter = newHeaderProtector(suite, trafficSecret, false)
|
a.headerDecrypter = newHeaderProtector(suite, trafficSecret, false, a.version)
|
||||||
if a.suite == nil {
|
if a.suite == nil {
|
||||||
a.setAEADParameters(a.rcvAEAD, suite)
|
a.setAEADParameters(a.rcvAEAD, suite)
|
||||||
}
|
}
|
||||||
|
|
||||||
a.nextRcvTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret)
|
a.nextRcvTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret)
|
||||||
a.nextRcvAEAD = createAEAD(suite, a.nextRcvTrafficSecret)
|
a.nextRcvAEAD = createAEAD(suite, a.nextRcvTrafficSecret, a.version)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For the client, this function is called after SetReadKey.
|
// For the client, this function is called after SetReadKey.
|
||||||
// For the server, this function is called before SetWriteKey.
|
// For the server, this function is called before SetWriteKey.
|
||||||
func (a *updatableAEAD) SetWriteKey(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) {
|
func (a *updatableAEAD) SetWriteKey(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) {
|
||||||
a.sendAEAD = createAEAD(suite, trafficSecret)
|
a.sendAEAD = createAEAD(suite, trafficSecret, a.version)
|
||||||
a.headerEncrypter = newHeaderProtector(suite, trafficSecret, false)
|
a.headerEncrypter = newHeaderProtector(suite, trafficSecret, false, a.version)
|
||||||
if a.suite == nil {
|
if a.suite == nil {
|
||||||
a.setAEADParameters(a.sendAEAD, suite)
|
a.setAEADParameters(a.sendAEAD, suite)
|
||||||
}
|
}
|
||||||
|
|
||||||
a.nextSendTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret)
|
a.nextSendTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret)
|
||||||
a.nextSendAEAD = createAEAD(suite, a.nextSendTrafficSecret)
|
a.nextSendAEAD = createAEAD(suite, a.nextSendTrafficSecret, a.version)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *updatableAEAD) setAEADParameters(aead cipher.AEAD, suite *qtls.CipherSuiteTLS13) {
|
func (a *updatableAEAD) setAEADParameters(aead cipher.AEAD, suite *qtls.CipherSuiteTLS13) {
|
||||||
|
|
|
@ -137,8 +137,7 @@ const MaxAckFrameSize ByteCount = 1000
|
||||||
// The size is chosen such that a DATAGRAM frame fits into a QUIC packet.
|
// The size is chosen such that a DATAGRAM frame fits into a QUIC packet.
|
||||||
const DefaultMaxDatagramFrameSize ByteCount = 1220
|
const DefaultMaxDatagramFrameSize ByteCount = 1220
|
||||||
|
|
||||||
// DatagramRcvQueueLen is the length of the receive queue for DATAGRAM frames.
|
// DatagramRcvQueueLen is the length of the receive queue for DATAGRAM frames (RFC 9221)
|
||||||
// See https://datatracker.ietf.org/doc/draft-pauly-quic-datagram/.
|
|
||||||
const DatagramRcvQueueLen = 128
|
const DatagramRcvQueueLen = 128
|
||||||
|
|
||||||
// MaxNumAckRanges is the maximum number of ACK ranges that we send in an ACK frame.
|
// MaxNumAckRanges is the maximum number of ACK ranges that we send in an ACK frame.
|
||||||
|
|
|
@ -23,11 +23,12 @@ const (
|
||||||
VersionUnknown VersionNumber = math.MaxUint32
|
VersionUnknown VersionNumber = math.MaxUint32
|
||||||
VersionDraft29 VersionNumber = 0xff00001d
|
VersionDraft29 VersionNumber = 0xff00001d
|
||||||
Version1 VersionNumber = 0x1
|
Version1 VersionNumber = 0x1
|
||||||
|
Version2 VersionNumber = 0x709a50c4
|
||||||
)
|
)
|
||||||
|
|
||||||
// SupportedVersions lists the versions that the server supports
|
// SupportedVersions lists the versions that the server supports
|
||||||
// must be in sorted descending order
|
// must be in sorted descending order
|
||||||
var SupportedVersions = []VersionNumber{Version1, VersionDraft29}
|
var SupportedVersions = []VersionNumber{Version1, Version2, VersionDraft29}
|
||||||
|
|
||||||
// IsValidVersion says if the version is known to quic-go
|
// IsValidVersion says if the version is known to quic-go
|
||||||
func IsValidVersion(v VersionNumber) bool {
|
func IsValidVersion(v VersionNumber) bool {
|
||||||
|
@ -50,6 +51,8 @@ func (vn VersionNumber) String() string {
|
||||||
return "draft-29"
|
return "draft-29"
|
||||||
case Version1:
|
case Version1:
|
||||||
return "v1"
|
return "v1"
|
||||||
|
case Version2:
|
||||||
|
return "v2"
|
||||||
default:
|
default:
|
||||||
if vn.isGQUIC() {
|
if vn.isGQUIC() {
|
||||||
return fmt.Sprintf("gQUIC %d", vn.toGQUICVersion())
|
return fmt.Sprintf("gQUIC %d", vn.toGQUICVersion())
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//go:build go1.18
|
//go:build go1.18 && !go1.19
|
||||||
// +build go1.18
|
// +build go1.18,!go1.19
|
||||||
|
|
||||||
package qtls
|
package qtls
|
||||||
|
|
||||||
|
|
|
@ -3,4 +3,98 @@
|
||||||
|
|
||||||
package qtls
|
package qtls
|
||||||
|
|
||||||
var _ int = "The version of quic-go you're using can't be built on Go 1.19 yet. For more details, please see https://github.com/lucas-clemente/quic-go/wiki/quic-go-and-Go-versions."
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/marten-seemann/qtls-go1-19"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Alert is a TLS alert
|
||||||
|
Alert = qtls.Alert
|
||||||
|
// A Certificate is qtls.Certificate.
|
||||||
|
Certificate = qtls.Certificate
|
||||||
|
// CertificateRequestInfo contains information about a certificate request.
|
||||||
|
CertificateRequestInfo = qtls.CertificateRequestInfo
|
||||||
|
// A CipherSuiteTLS13 is a cipher suite for TLS 1.3
|
||||||
|
CipherSuiteTLS13 = qtls.CipherSuiteTLS13
|
||||||
|
// ClientHelloInfo contains information about a ClientHello.
|
||||||
|
ClientHelloInfo = qtls.ClientHelloInfo
|
||||||
|
// ClientSessionCache is a cache used for session resumption.
|
||||||
|
ClientSessionCache = qtls.ClientSessionCache
|
||||||
|
// ClientSessionState is a state needed for session resumption.
|
||||||
|
ClientSessionState = qtls.ClientSessionState
|
||||||
|
// A Config is a qtls.Config.
|
||||||
|
Config = qtls.Config
|
||||||
|
// A Conn is a qtls.Conn.
|
||||||
|
Conn = qtls.Conn
|
||||||
|
// ConnectionState contains information about the state of the connection.
|
||||||
|
ConnectionState = qtls.ConnectionStateWith0RTT
|
||||||
|
// EncryptionLevel is the encryption level of a message.
|
||||||
|
EncryptionLevel = qtls.EncryptionLevel
|
||||||
|
// Extension is a TLS extension
|
||||||
|
Extension = qtls.Extension
|
||||||
|
// ExtraConfig is the qtls.ExtraConfig
|
||||||
|
ExtraConfig = qtls.ExtraConfig
|
||||||
|
// RecordLayer is a qtls RecordLayer.
|
||||||
|
RecordLayer = qtls.RecordLayer
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// EncryptionHandshake is the Handshake encryption level
|
||||||
|
EncryptionHandshake = qtls.EncryptionHandshake
|
||||||
|
// Encryption0RTT is the 0-RTT encryption level
|
||||||
|
Encryption0RTT = qtls.Encryption0RTT
|
||||||
|
// EncryptionApplication is the application data encryption level
|
||||||
|
EncryptionApplication = qtls.EncryptionApplication
|
||||||
|
)
|
||||||
|
|
||||||
|
// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
|
||||||
|
func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
|
||||||
|
return qtls.AEADAESGCMTLS13(key, fixedNonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client returns a new TLS client side connection.
|
||||||
|
func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
|
||||||
|
return qtls.Client(conn, config, extraConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server returns a new TLS server side connection.
|
||||||
|
func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
|
||||||
|
return qtls.Server(conn, config, extraConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetConnectionState(conn *Conn) ConnectionState {
|
||||||
|
return conn.ConnectionStateWith0RTT()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToTLSConnectionState extracts the tls.ConnectionState
|
||||||
|
func ToTLSConnectionState(cs ConnectionState) tls.ConnectionState {
|
||||||
|
return cs.ConnectionState
|
||||||
|
}
|
||||||
|
|
||||||
|
type cipherSuiteTLS13 struct {
|
||||||
|
ID uint16
|
||||||
|
KeyLen int
|
||||||
|
AEAD func(key, fixedNonce []byte) cipher.AEAD
|
||||||
|
Hash crypto.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname cipherSuiteTLS13ByID github.com/marten-seemann/qtls-go1-19.cipherSuiteTLS13ByID
|
||||||
|
func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13
|
||||||
|
|
||||||
|
// CipherSuiteTLS13ByID gets a TLS 1.3 cipher suite.
|
||||||
|
func CipherSuiteTLS13ByID(id uint16) *CipherSuiteTLS13 {
|
||||||
|
val := cipherSuiteTLS13ByID(id)
|
||||||
|
cs := (*cipherSuiteTLS13)(unsafe.Pointer(val))
|
||||||
|
return &qtls.CipherSuiteTLS13{
|
||||||
|
ID: cs.ID,
|
||||||
|
KeyLen: cs.KeyLen,
|
||||||
|
AEAD: cs.AEAD,
|
||||||
|
Hash: cs.Hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
//go:build go1.20
|
||||||
|
// +build go1.20
|
||||||
|
|
||||||
|
package qtls
|
||||||
|
|
||||||
|
var _ int = "The version of quic-go you're using can't be built on Go 1.20 yet. For more details, please see https://github.com/lucas-clemente/quic-go/wiki/quic-go-and-Go-versions."
|
|
@ -127,18 +127,32 @@ func (h *ExtendedHeader) Write(b *bytes.Buffer, ver protocol.VersionNumber) erro
|
||||||
return h.writeShortHeader(b, ver)
|
return h.writeShortHeader(b, ver)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ExtendedHeader) writeLongHeader(b *bytes.Buffer, _ protocol.VersionNumber) error {
|
func (h *ExtendedHeader) writeLongHeader(b *bytes.Buffer, version protocol.VersionNumber) error {
|
||||||
var packetType uint8
|
var packetType uint8
|
||||||
|
if version == protocol.Version2 {
|
||||||
//nolint:exhaustive
|
//nolint:exhaustive
|
||||||
switch h.Type {
|
switch h.Type {
|
||||||
case protocol.PacketTypeInitial:
|
case protocol.PacketTypeInitial:
|
||||||
packetType = 0x0
|
packetType = 0b01
|
||||||
case protocol.PacketType0RTT:
|
case protocol.PacketType0RTT:
|
||||||
packetType = 0x1
|
packetType = 0b10
|
||||||
case protocol.PacketTypeHandshake:
|
case protocol.PacketTypeHandshake:
|
||||||
packetType = 0x2
|
packetType = 0b11
|
||||||
case protocol.PacketTypeRetry:
|
case protocol.PacketTypeRetry:
|
||||||
packetType = 0x3
|
packetType = 0b00
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//nolint:exhaustive
|
||||||
|
switch h.Type {
|
||||||
|
case protocol.PacketTypeInitial:
|
||||||
|
packetType = 0b00
|
||||||
|
case protocol.PacketType0RTT:
|
||||||
|
packetType = 0b01
|
||||||
|
case protocol.PacketTypeHandshake:
|
||||||
|
packetType = 0b10
|
||||||
|
case protocol.PacketTypeRetry:
|
||||||
|
packetType = 0b11
|
||||||
|
}
|
||||||
}
|
}
|
||||||
firstByte := 0xc0 | packetType<<4
|
firstByte := 0xc0 | packetType<<4
|
||||||
if h.Type != protocol.PacketTypeRetry {
|
if h.Type != protocol.PacketTypeRetry {
|
||||||
|
|
|
@ -53,10 +53,14 @@ func Is0RTTPacket(b []byte) bool {
|
||||||
if b[0]&0x80 == 0 {
|
if b[0]&0x80 == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !protocol.IsSupportedVersion(protocol.SupportedVersions, protocol.VersionNumber(binary.BigEndian.Uint32(b[1:5]))) {
|
version := protocol.VersionNumber(binary.BigEndian.Uint32(b[1:5]))
|
||||||
|
if !protocol.IsSupportedVersion(protocol.SupportedVersions, version) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return b[0]&0x30>>4 == 0x1
|
if version == protocol.Version2 {
|
||||||
|
return b[0]>>4&0b11 == 0b10
|
||||||
|
}
|
||||||
|
return b[0]>>4&0b11 == 0b01
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrUnsupportedVersion = errors.New("unsupported version")
|
var ErrUnsupportedVersion = errors.New("unsupported version")
|
||||||
|
@ -179,15 +183,28 @@ func (h *Header) parseLongHeader(b *bytes.Reader) error {
|
||||||
return ErrUnsupportedVersion
|
return ErrUnsupportedVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (h.typeByte & 0x30) >> 4 {
|
if h.Version == protocol.Version2 {
|
||||||
case 0x0:
|
switch h.typeByte >> 4 & 0b11 {
|
||||||
h.Type = protocol.PacketTypeInitial
|
case 0b00:
|
||||||
case 0x1:
|
|
||||||
h.Type = protocol.PacketType0RTT
|
|
||||||
case 0x2:
|
|
||||||
h.Type = protocol.PacketTypeHandshake
|
|
||||||
case 0x3:
|
|
||||||
h.Type = protocol.PacketTypeRetry
|
h.Type = protocol.PacketTypeRetry
|
||||||
|
case 0b01:
|
||||||
|
h.Type = protocol.PacketTypeInitial
|
||||||
|
case 0b10:
|
||||||
|
h.Type = protocol.PacketType0RTT
|
||||||
|
case 0b11:
|
||||||
|
h.Type = protocol.PacketTypeHandshake
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch h.typeByte >> 4 & 0b11 {
|
||||||
|
case 0b00:
|
||||||
|
h.Type = protocol.PacketTypeInitial
|
||||||
|
case 0b01:
|
||||||
|
h.Type = protocol.PacketType0RTT
|
||||||
|
case 0b10:
|
||||||
|
h.Type = protocol.PacketTypeHandshake
|
||||||
|
case 0b11:
|
||||||
|
h.Type = protocol.PacketTypeRetry
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.Type == protocol.PacketTypeRetry {
|
if h.Type == protocol.PacketTypeRetry {
|
||||||
|
|
|
@ -42,7 +42,7 @@ const (
|
||||||
activeConnectionIDLimitParameterID transportParameterID = 0xe
|
activeConnectionIDLimitParameterID transportParameterID = 0xe
|
||||||
initialSourceConnectionIDParameterID transportParameterID = 0xf
|
initialSourceConnectionIDParameterID transportParameterID = 0xf
|
||||||
retrySourceConnectionIDParameterID transportParameterID = 0x10
|
retrySourceConnectionIDParameterID transportParameterID = 0x10
|
||||||
// https://datatracker.ietf.org/doc/draft-ietf-quic-datagram/
|
// RFC 9221
|
||||||
maxDatagramFrameSizeParameterID transportParameterID = 0x20
|
maxDatagramFrameSizeParameterID transportParameterID = 0x20
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ func ParseVersionNegotiationPacket(b *bytes.Reader) (*Header, []protocol.Version
|
||||||
}
|
}
|
||||||
|
|
||||||
// ComposeVersionNegotiation composes a Version Negotiation
|
// ComposeVersionNegotiation composes a Version Negotiation
|
||||||
func ComposeVersionNegotiation(destConnID, srcConnID protocol.ConnectionID, versions []protocol.VersionNumber) ([]byte, error) {
|
func ComposeVersionNegotiation(destConnID, srcConnID protocol.ConnectionID, versions []protocol.VersionNumber) []byte {
|
||||||
greasedVersions := protocol.GetGreasedVersions(versions)
|
greasedVersions := protocol.GetGreasedVersions(versions)
|
||||||
expectedLen := 1 /* type byte */ + 4 /* version field */ + 1 /* dest connection ID length field */ + destConnID.Len() + 1 /* src connection ID length field */ + srcConnID.Len() + len(greasedVersions)*4
|
expectedLen := 1 /* type byte */ + 4 /* version field */ + 1 /* dest connection ID length field */ + destConnID.Len() + 1 /* src connection ID length field */ + srcConnID.Len() + len(greasedVersions)*4
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, expectedLen))
|
buf := bytes.NewBuffer(make([]byte, 0, expectedLen))
|
||||||
|
@ -50,5 +50,5 @@ func ComposeVersionNegotiation(destConnID, srcConnID protocol.ConnectionID, vers
|
||||||
for _, v := range greasedVersions {
|
for _, v := range greasedVersions {
|
||||||
utils.BigEndian.WriteUint32(buf, uint32(v))
|
utils.BigEndian.WriteUint32(buf, uint32(v))
|
||||||
}
|
}
|
||||||
return buf.Bytes(), nil
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,7 @@ func setReceiveBuffer(c net.PacketConn, logger utils.Logger) error {
|
||||||
}
|
}
|
||||||
if size >= protocol.DesiredReceiveBufferSize {
|
if size >= protocol.DesiredReceiveBufferSize {
|
||||||
logger.Debugf("Conn has receive buffer of %d kiB (wanted: at least %d kiB)", size/1024, protocol.DesiredReceiveBufferSize/1024)
|
logger.Debugf("Conn has receive buffer of %d kiB (wanted: at least %d kiB)", size/1024, protocol.DesiredReceiveBufferSize/1024)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if err := conn.SetReadBuffer(protocol.DesiredReceiveBufferSize); err != nil {
|
if err := conn.SetReadBuffer(protocol.DesiredReceiveBufferSize); err != nil {
|
||||||
return fmt.Errorf("failed to increase receive buffer size: %w", err)
|
return fmt.Errorf("failed to increase receive buffer size: %w", err)
|
||||||
|
|
|
@ -31,7 +31,10 @@ func NewReader(r io.Reader) Reader {
|
||||||
|
|
||||||
func (r *byteReader) ReadByte() (byte, error) {
|
func (r *byteReader) ReadByte() (byte, error) {
|
||||||
var b [1]byte
|
var b [1]byte
|
||||||
_, err := r.Reader.Read(b[:])
|
n, err := r.Reader.Read(b[:])
|
||||||
|
if n == 1 && err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
return b[0], err
|
return b[0], err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ type receiveStream struct {
|
||||||
resetRemotely bool // set when HandleResetStreamFrame() is called
|
resetRemotely bool // set when HandleResetStreamFrame() is called
|
||||||
|
|
||||||
readChan chan struct{}
|
readChan chan struct{}
|
||||||
|
readOnce chan struct{} // cap: 1, to protect against concurrent use of Read
|
||||||
deadline time.Time
|
deadline time.Time
|
||||||
|
|
||||||
flowController flowcontrol.StreamFlowController
|
flowController flowcontrol.StreamFlowController
|
||||||
|
@ -70,6 +71,7 @@ func newReceiveStream(
|
||||||
flowController: flowController,
|
flowController: flowController,
|
||||||
frameQueue: newFrameSorter(),
|
frameQueue: newFrameSorter(),
|
||||||
readChan: make(chan struct{}, 1),
|
readChan: make(chan struct{}, 1),
|
||||||
|
readOnce: make(chan struct{}, 1),
|
||||||
finalOffset: protocol.MaxByteCount,
|
finalOffset: protocol.MaxByteCount,
|
||||||
version: version,
|
version: version,
|
||||||
}
|
}
|
||||||
|
@ -81,6 +83,12 @@ func (s *receiveStream) StreamID() protocol.StreamID {
|
||||||
|
|
||||||
// Read implements io.Reader. It is not thread safe!
|
// Read implements io.Reader. It is not thread safe!
|
||||||
func (s *receiveStream) Read(p []byte) (int, error) {
|
func (s *receiveStream) Read(p []byte) (int, error) {
|
||||||
|
// Concurrent use of Read is not permitted (and doesn't make any sense),
|
||||||
|
// but sometimes people do it anyway.
|
||||||
|
// Make sure that we only execute one call at any given time to avoid hard to debug failures.
|
||||||
|
s.readOnce <- struct{}{}
|
||||||
|
defer func() { <-s.readOnce }()
|
||||||
|
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
completed, n, err := s.readImpl(p)
|
completed, n, err := s.readImpl(p)
|
||||||
s.mutex.Unlock()
|
s.mutex.Unlock()
|
||||||
|
@ -105,7 +113,7 @@ func (s *receiveStream) readImpl(p []byte) (bool /*stream completed */, int, err
|
||||||
return false, 0, s.closeForShutdownErr
|
return false, 0, s.closeForShutdownErr
|
||||||
}
|
}
|
||||||
|
|
||||||
bytesRead := 0
|
var bytesRead int
|
||||||
var deadlineTimer *utils.Timer
|
var deadlineTimer *utils.Timer
|
||||||
for bytesRead < len(p) {
|
for bytesRead < len(p) {
|
||||||
if s.currentFrame == nil || s.readPosInFrame >= len(s.currentFrame) {
|
if s.currentFrame == nil || s.readPosInFrame >= len(s.currentFrame) {
|
||||||
|
|
|
@ -50,6 +50,7 @@ type sendStream struct {
|
||||||
nextFrame *wire.StreamFrame
|
nextFrame *wire.StreamFrame
|
||||||
|
|
||||||
writeChan chan struct{}
|
writeChan chan struct{}
|
||||||
|
writeOnce chan struct{}
|
||||||
deadline time.Time
|
deadline time.Time
|
||||||
|
|
||||||
flowController flowcontrol.StreamFlowController
|
flowController flowcontrol.StreamFlowController
|
||||||
|
@ -73,6 +74,7 @@ func newSendStream(
|
||||||
sender: sender,
|
sender: sender,
|
||||||
flowController: flowController,
|
flowController: flowController,
|
||||||
writeChan: make(chan struct{}, 1),
|
writeChan: make(chan struct{}, 1),
|
||||||
|
writeOnce: make(chan struct{}, 1), // cap: 1, to protect against concurrent use of Write
|
||||||
version: version,
|
version: version,
|
||||||
}
|
}
|
||||||
s.ctx, s.ctxCancel = context.WithCancel(context.Background())
|
s.ctx, s.ctxCancel = context.WithCancel(context.Background())
|
||||||
|
@ -84,6 +86,12 @@ func (s *sendStream) StreamID() protocol.StreamID {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sendStream) Write(p []byte) (int, error) {
|
func (s *sendStream) Write(p []byte) (int, error) {
|
||||||
|
// Concurrent use of Write is not permitted (and doesn't make any sense),
|
||||||
|
// but sometimes people do it anyway.
|
||||||
|
// Make sure that we only execute one call at any given time to avoid hard to debug failures.
|
||||||
|
s.writeOnce <- struct{}{}
|
||||||
|
defer func() { <-s.writeOnce }()
|
||||||
|
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
defer s.mutex.Unlock()
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,9 @@ import (
|
||||||
"github.com/lucas-clemente/quic-go/logging"
|
"github.com/lucas-clemente/quic-go/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrServerClosed is returned by the Listener or EarlyListener's Accept method after a call to Close.
|
||||||
|
var ErrServerClosed = errors.New("quic: Server closed")
|
||||||
|
|
||||||
// packetHandler handles packets
|
// packetHandler handles packets
|
||||||
type packetHandler interface {
|
type packetHandler interface {
|
||||||
handlePacket(*receivedPacket)
|
handlePacket(*receivedPacket)
|
||||||
|
@ -284,7 +287,7 @@ func (s *baseServer) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if s.serverError == nil {
|
if s.serverError == nil {
|
||||||
s.serverError = errors.New("server closed")
|
s.serverError = ErrServerClosed
|
||||||
}
|
}
|
||||||
// If the server was started with ListenAddr, we created the packet conn.
|
// If the server was started with ListenAddr, we created the packet conn.
|
||||||
// We need to close it in order to make the go routine reading from that conn return.
|
// We need to close it in order to make the go routine reading from that conn return.
|
||||||
|
@ -648,11 +651,7 @@ func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer han
|
||||||
|
|
||||||
func (s *baseServer) sendVersionNegotiationPacket(p *receivedPacket, hdr *wire.Header) {
|
func (s *baseServer) sendVersionNegotiationPacket(p *receivedPacket, hdr *wire.Header) {
|
||||||
s.logger.Debugf("Client offered version %s, sending Version Negotiation", hdr.Version)
|
s.logger.Debugf("Client offered version %s, sending Version Negotiation", hdr.Version)
|
||||||
data, err := wire.ComposeVersionNegotiation(hdr.SrcConnectionID, hdr.DestConnectionID, s.config.Versions)
|
data := wire.ComposeVersionNegotiation(hdr.SrcConnectionID, hdr.DestConnectionID, s.config.Versions)
|
||||||
if err != nil {
|
|
||||||
s.logger.Debugf("Error composing Version Negotiation: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if s.config.Tracer != nil {
|
if s.config.Tracer != nil {
|
||||||
s.config.Tracer.SentPacket(
|
s.config.Tracer.SentPacket(
|
||||||
p.remoteAddr,
|
p.remoteAddr,
|
||||||
|
|
|
@ -30,11 +30,15 @@ func wrapConn(pc net.PacketConn) (rawConn, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok := pc.LocalAddr().(*net.UDPAddr); ok {
|
||||||
|
// Only set DF on sockets that we expect to be able to handle that configuration.
|
||||||
err = setDF(rawConn)
|
err = setDF(rawConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
c, ok := pc.(OOBCapablePacketConn)
|
c, ok := pc.(OOBCapablePacketConn)
|
||||||
if !ok {
|
if !ok {
|
||||||
utils.DefaultLogger.Infof("PacketConn is not a net.UDPConn. Disabling optimizations possible on UDP connections.")
|
utils.DefaultLogger.Infof("PacketConn is not a net.UDPConn. Disabling optimizations possible on UDP connections.")
|
||||||
|
|
|
@ -7,9 +7,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setDF(rawConn syscall.RawConn) error {
|
func setDF(rawConn syscall.RawConn) error {
|
||||||
|
@ -30,7 +29,7 @@ func setDF(rawConn syscall.RawConn) error {
|
||||||
case errDFIPv4 != nil && errDFIPv6 == nil:
|
case errDFIPv4 != nil && errDFIPv6 == nil:
|
||||||
utils.DefaultLogger.Debugf("Setting DF for IPv6.")
|
utils.DefaultLogger.Debugf("Setting DF for IPv6.")
|
||||||
case errDFIPv4 != nil && errDFIPv6 != nil:
|
case errDFIPv4 != nil && errDFIPv6 != nil:
|
||||||
utils.DefaultLogger.Errorf("setting DF failed for both IPv4 and IPv6")
|
return errors.New("setting DF failed for both IPv4 and IPv6")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ type Conn struct {
|
||||||
|
|
||||||
// handshakeStatus is 1 if the connection is currently transferring
|
// handshakeStatus is 1 if the connection is currently transferring
|
||||||
// application data (i.e. is not currently processing a handshake).
|
// application data (i.e. is not currently processing a handshake).
|
||||||
|
// handshakeStatus == 1 implies handshakeErr == nil.
|
||||||
// This field is only to be accessed with sync/atomic.
|
// This field is only to be accessed with sync/atomic.
|
||||||
handshakeStatus uint32
|
handshakeStatus uint32
|
||||||
// constant after handshake; protected by handshakeMutex
|
// constant after handshake; protected by handshakeMutex
|
||||||
|
@ -1451,6 +1452,13 @@ func (c *Conn) HandshakeContext(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
|
func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
|
||||||
|
// Fast sync/atomic-based exit if there is no handshake in flight and the
|
||||||
|
// last one succeeded without an error. Avoids the expensive context setup
|
||||||
|
// and mutex for most Read and Write calls.
|
||||||
|
if c.handshakeComplete() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
handshakeCtx, cancel := context.WithCancel(ctx)
|
handshakeCtx, cancel := context.WithCancel(ctx)
|
||||||
// Note: defer this before starting the "interrupter" goroutine
|
// Note: defer this before starting the "interrupter" goroutine
|
||||||
// so that we can tell the difference between the input being canceled and
|
// so that we can tell the difference between the input being canceled and
|
||||||
|
@ -1509,6 +1517,9 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
|
||||||
if c.handshakeErr == nil && !c.handshakeComplete() {
|
if c.handshakeErr == nil && !c.handshakeComplete() {
|
||||||
c.handshakeErr = errors.New("tls: internal error: handshake should have had a result")
|
c.handshakeErr = errors.New("tls: internal error: handshake should have had a result")
|
||||||
}
|
}
|
||||||
|
if c.handshakeErr != nil && c.handshakeComplete() {
|
||||||
|
panic("tls: internal error: handshake returned an error but is marked successful")
|
||||||
|
}
|
||||||
|
|
||||||
return c.handshakeErr
|
return c.handshakeErr
|
||||||
}
|
}
|
||||||
|
|
|
@ -777,6 +777,7 @@ func (hs *serverHandshakeStateTLS13) sendSessionTickets() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
|
if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
@ -232,6 +233,20 @@ func (c *Conn) getSessionTicketMsg(appData []byte) (*newSessionTicketMsgTLS13, e
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.lifetime = uint32(maxSessionTicketLifetime / time.Second)
|
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 = c.config.rand().Read(ageAdd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 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 c.extraConfig != nil {
|
if c.extraConfig != nil {
|
||||||
m.maxEarlyData = c.extraConfig.MaxEarlyData
|
m.maxEarlyData = c.extraConfig.MaxEarlyData
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ type Conn struct {
|
||||||
|
|
||||||
// handshakeStatus is 1 if the connection is currently transferring
|
// handshakeStatus is 1 if the connection is currently transferring
|
||||||
// application data (i.e. is not currently processing a handshake).
|
// application data (i.e. is not currently processing a handshake).
|
||||||
|
// handshakeStatus == 1 implies handshakeErr == nil.
|
||||||
// This field is only to be accessed with sync/atomic.
|
// This field is only to be accessed with sync/atomic.
|
||||||
handshakeStatus uint32
|
handshakeStatus uint32
|
||||||
// constant after handshake; protected by handshakeMutex
|
// constant after handshake; protected by handshakeMutex
|
||||||
|
@ -1458,6 +1459,13 @@ func (c *Conn) HandshakeContext(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
|
func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
|
||||||
|
// Fast sync/atomic-based exit if there is no handshake in flight and the
|
||||||
|
// last one succeeded without an error. Avoids the expensive context setup
|
||||||
|
// and mutex for most Read and Write calls.
|
||||||
|
if c.handshakeComplete() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
handshakeCtx, cancel := context.WithCancel(ctx)
|
handshakeCtx, cancel := context.WithCancel(ctx)
|
||||||
// Note: defer this before starting the "interrupter" goroutine
|
// Note: defer this before starting the "interrupter" goroutine
|
||||||
// so that we can tell the difference between the input being canceled and
|
// so that we can tell the difference between the input being canceled and
|
||||||
|
@ -1516,6 +1524,9 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
|
||||||
if c.handshakeErr == nil && !c.handshakeComplete() {
|
if c.handshakeErr == nil && !c.handshakeComplete() {
|
||||||
c.handshakeErr = errors.New("tls: internal error: handshake should have had a result")
|
c.handshakeErr = errors.New("tls: internal error: handshake should have had a result")
|
||||||
}
|
}
|
||||||
|
if c.handshakeErr != nil && c.handshakeComplete() {
|
||||||
|
panic("tls: internal error: handshake returned an error but is marked successful")
|
||||||
|
}
|
||||||
|
|
||||||
return c.handshakeErr
|
return c.handshakeErr
|
||||||
}
|
}
|
||||||
|
|
|
@ -777,6 +777,7 @@ func (hs *serverHandshakeStateTLS13) sendSessionTickets() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
|
if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
@ -232,6 +233,20 @@ func (c *Conn) getSessionTicketMsg(appData []byte) (*newSessionTicketMsgTLS13, e
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.lifetime = uint32(maxSessionTicketLifetime / time.Second)
|
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 = c.config.rand().Read(ageAdd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 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 c.extraConfig != nil {
|
if c.extraConfig != nil {
|
||||||
m.maxEarlyData = c.extraConfig.MaxEarlyData
|
m.maxEarlyData = c.extraConfig.MaxEarlyData
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,6 @@
|
||||||
|
# qtls
|
||||||
|
|
||||||
|
[![Go Reference](https://pkg.go.dev/badge/github.com/marten-seemann/qtls-go1-17.svg)](https://pkg.go.dev/github.com/marten-seemann/qtls-go1-17)
|
||||||
|
[![.github/workflows/go-test.yml](https://github.com/marten-seemann/qtls-go1-17/actions/workflows/go-test.yml/badge.svg)](https://github.com/marten-seemann/qtls-go1-17/actions/workflows/go-test.yml)
|
||||||
|
|
||||||
|
This repository contains a modified version of the standard library's TLS implementation, modified for the QUIC protocol. It is used by [quic-go](https://github.com/lucas-clemente/quic-go).
|
|
@ -0,0 +1,102 @@
|
||||||
|
// 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 qtls
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
type alert uint8
|
||||||
|
|
||||||
|
// Alert is a TLS alert
|
||||||
|
type Alert = alert
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
|
@ -0,0 +1,293 @@
|
||||||
|
// 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 qtls
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
|
@ -0,0 +1,693 @@
|
||||||
|
// 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 qtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/des"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/rc4"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
type CipherSuiteTLS13 struct {
|
||||||
|
ID uint16
|
||||||
|
KeyLen int
|
||||||
|
Hash crypto.Hash
|
||||||
|
AEAD func(key, fixedNonce []byte) cipher.AEAD
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CipherSuiteTLS13) IVLen() int {
|
||||||
|
return aeadNonceLength
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// xoredNonceAEAD 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
|
||||||
|
aead, err = cipher.NewGCM(aes)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := &prefixNonceAEAD{aead: aead}
|
||||||
|
copy(ret.nonce[:], noncePrefix)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
|
||||||
|
func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
|
||||||
|
return aeadAESGCMTLS13(key, fixedNonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,22 @@
|
||||||
|
//go:build !js
|
||||||
|
// +build !js
|
||||||
|
|
||||||
|
package qtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"golang.org/x/sys/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
|
@ -0,0 +1,12 @@
|
||||||
|
//go:build js
|
||||||
|
// +build js
|
||||||
|
|
||||||
|
package qtls
|
||||||
|
|
||||||
|
var (
|
||||||
|
hasGCMAsmAMD64 = false
|
||||||
|
hasGCMAsmARM64 = false
|
||||||
|
hasGCMAsmS390X = false
|
||||||
|
|
||||||
|
hasAESGCMHardwareSupport = false
|
||||||
|
)
|
1117
vendor/github.com/marten-seemann/qtls-go1-19/handshake_client.go
generated
vendored
Normal file
1117
vendor/github.com/marten-seemann/qtls-go1-19/handshake_client.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
736
vendor/github.com/marten-seemann/qtls-go1-19/handshake_client_tls13.go
generated
vendored
Normal file
736
vendor/github.com/marten-seemann/qtls-go1-19/handshake_client_tls13.go
generated
vendored
Normal file
|
@ -0,0 +1,736 @@
|
||||||
|
// 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 qtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/rsa"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"hash"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/cryptobyte"
|
||||||
|
)
|
||||||
|
|
||||||
|
type clientHandshakeStateTLS13 struct {
|
||||||
|
c *Conn
|
||||||
|
ctx context.Context
|
||||||
|
serverHello *serverHelloMsg
|
||||||
|
hello *clientHelloMsg
|
||||||
|
ecdheParams ecdheParameters
|
||||||
|
|
||||||
|
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.ecdheParams, 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.ecdheParams == 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
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StoreUint32(&c.handshakeStatus, 1)
|
||||||
|
|
||||||
|
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 hs.ecdheParams.CurveID() == curveID {
|
||||||
|
c.sendAlert(alertIllegalParameter)
|
||||||
|
return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
|
||||||
|
}
|
||||||
|
if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
|
||||||
|
c.sendAlert(alertInternalError)
|
||||||
|
return errors.New("tls: CurvePreferences includes unsupported curve")
|
||||||
|
}
|
||||||
|
params, err := generateECDHEParameters(c.config.rand(), curveID)
|
||||||
|
if err != nil {
|
||||||
|
c.sendAlert(alertInternalError)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hs.ecdheParams = params
|
||||||
|
hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil {
|
||||||
|
c.extraConfig.Rejected0RTT()
|
||||||
|
}
|
||||||
|
hs.hello.earlyData = false // disable 0-RTT
|
||||||
|
|
||||||
|
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 hs.serverHello.serverShare.group != hs.ecdheParams.CurveID() {
|
||||||
|
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
|
||||||
|
|
||||||
|
sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data)
|
||||||
|
if sharedKey == 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.exportKey(EncryptionHandshake, hs.suite, clientSecret)
|
||||||
|
c.out.setTrafficSecret(hs.suite, clientSecret)
|
||||||
|
serverSecret := hs.suite.deriveSecret(handshakeSecret,
|
||||||
|
serverHandshakeTrafficLabel, hs.transcript)
|
||||||
|
c.in.exportKey(EncryptionHandshake, hs.suite, serverSecret)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
// Notify the caller if 0-RTT was rejected.
|
||||||
|
if !encryptedExtensions.earlyData && hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil {
|
||||||
|
c.extraConfig.Rejected0RTT()
|
||||||
|
}
|
||||||
|
c.used0RTT = encryptedExtensions.earlyData
|
||||||
|
if hs.c.extraConfig != nil && hs.c.extraConfig.ReceivedExtensions != nil {
|
||||||
|
hs.c.extraConfig.ReceivedExtensions(typeEncryptedExtensions, encryptedExtensions.additionalExtensions)
|
||||||
|
}
|
||||||
|
hs.transcript.Write(encryptedExtensions.marshal())
|
||||||
|
|
||||||
|
if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol); err != nil {
|
||||||
|
c.sendAlert(alertUnsupportedExtension)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.clientProtocol = encryptedExtensions.alpnProtocol
|
||||||
|
|
||||||
|
if c.extraConfig != nil && c.extraConfig.EnforceNextProtoSelection {
|
||||||
|
if len(encryptedExtensions.alpnProtocol) == 0 {
|
||||||
|
// the server didn't select an ALPN
|
||||||
|
c.sendAlert(alertNoApplicationProtocol)
|
||||||
|
return errors.New("ALPN negotiation failed. Server didn't offer any protocols")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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.exportKey(EncryptionApplication, hs.suite, serverSecret)
|
||||||
|
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(toCertificateRequestInfo(&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.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to save the max_early_data_size that the server sent us, in order
|
||||||
|
// to decide if we're going to try 0-RTT with this ticket.
|
||||||
|
// However, at the same time, the qtls.ClientSessionTicket needs to be equal to
|
||||||
|
// the tls.ClientSessionTicket, so we can't just add a new field to the struct.
|
||||||
|
// We therefore abuse the nonce field (which is a byte slice)
|
||||||
|
nonceWithEarlyData := make([]byte, len(msg.nonce)+4)
|
||||||
|
binary.BigEndian.PutUint32(nonceWithEarlyData, msg.maxEarlyData)
|
||||||
|
copy(nonceWithEarlyData[4:], msg.nonce)
|
||||||
|
|
||||||
|
var appData []byte
|
||||||
|
if c.extraConfig != nil && c.extraConfig.GetAppDataForSessionState != nil {
|
||||||
|
appData = c.extraConfig.GetAppDataForSessionState()
|
||||||
|
}
|
||||||
|
var b cryptobyte.Builder
|
||||||
|
b.AddUint16(clientSessionStateVersion) // revision
|
||||||
|
b.AddUint32(msg.maxEarlyData)
|
||||||
|
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||||
|
b.AddBytes(appData)
|
||||||
|
})
|
||||||
|
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||||
|
b.AddBytes(msg.nonce)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 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: b.BytesOrPanic(),
|
||||||
|
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, toClientSessionState(session))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
1843
vendor/github.com/marten-seemann/qtls-go1-19/handshake_messages.go
generated
vendored
Normal file
1843
vendor/github.com/marten-seemann/qtls-go1-19/handshake_messages.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,905 @@
|
||||||
|
// 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 qtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/subtle"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"io"
|
||||||
|
"sync/atomic"
|
||||||
|
"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 {
|
||||||
|
c.setAlternativeRecordLayer()
|
||||||
|
|
||||||
|
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()
|
||||||
|
} else if c.extraConfig.usesAlternativeRecordLayer() {
|
||||||
|
// This should already have been caught by the check that the ClientHello doesn't
|
||||||
|
// offer any (supported) versions older than TLS 1.3.
|
||||||
|
// Check again to make sure we can't be tricked into using an older version.
|
||||||
|
c.sendAlert(alertProtocolVersion)
|
||||||
|
return errors.New("tls: negotiated TLS < 1.3 when using QUIC")
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
atomic.StoreUint32(&c.handshakeStatus, 1)
|
||||||
|
|
||||||
|
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 := newClientHelloInfo(ctx, c, clientHello)
|
||||||
|
if cfc, err := c.config.GetConfigForClient(chi); err != nil {
|
||||||
|
c.sendAlert(alertInternalError)
|
||||||
|
return nil, err
|
||||||
|
} else if cfc != nil {
|
||||||
|
configForClient = fromConfig(cfc)
|
||||||
|
c.config = configForClient
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.ticketKeys = originalConfig.ticketKeys(configForClient)
|
||||||
|
|
||||||
|
clientVersions := clientHello.supportedVersions
|
||||||
|
if len(clientHello.supportedVersions) == 0 {
|
||||||
|
clientVersions = supportedVersionsFromMax(clientHello.vers)
|
||||||
|
}
|
||||||
|
if c.extraConfig.usesAlternativeRecordLayer() {
|
||||||
|
// In QUIC, the client MUST NOT offer any old TLS versions.
|
||||||
|
// Here, we can only check that none of the other supported versions of this library
|
||||||
|
// (TLS 1.0 - TLS 1.2) is offered. We don't check for any SSL versions here.
|
||||||
|
for _, ver := range clientVersions {
|
||||||
|
if ver == VersionTLS13 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, v := range supportedVersions {
|
||||||
|
if ver == v {
|
||||||
|
c.sendAlert(alertProtocolVersion)
|
||||||
|
return nil, fmt.Errorf("tls: client offered old TLS version %#x", ver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Make the config we're using allows us to use TLS 1.3.
|
||||||
|
if c.config.maxSupportedVersion(roleServer) < VersionTLS13 {
|
||||||
|
c.sendAlert(alertInternalError)
|
||||||
|
return nil, errors.New("tls: MaxVersion prevents QUIC from using TLS 1.3")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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(newClientHelloInfo(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 {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, hs.masterSecret)
|
||||||
|
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 errors.New("tls: failed to verify client certificate: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
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 newClientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo {
|
||||||
|
supportedVersions := clientHello.supportedVersions
|
||||||
|
if len(clientHello.supportedVersions) == 0 {
|
||||||
|
supportedVersions = supportedVersionsFromMax(clientHello.vers)
|
||||||
|
}
|
||||||
|
|
||||||
|
return toClientHelloInfo(&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: toConfig(c.config),
|
||||||
|
ctx: ctx,
|
||||||
|
})
|
||||||
|
}
|
900
vendor/github.com/marten-seemann/qtls-go1-19/handshake_server_tls13.go
generated
vendored
Normal file
900
vendor/github.com/marten-seemann/qtls-go1-19/handshake_server_tls13.go
generated
vendored
Normal file
|
@ -0,0 +1,900 @@
|
||||||
|
// 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 qtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/rsa"
|
||||||
|
"errors"
|
||||||
|
"hash"
|
||||||
|
"io"
|
||||||
|
"sync/atomic"
|
||||||
|
"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
|
||||||
|
alpnNegotiationErr error
|
||||||
|
encryptedExtensions *encryptedExtensionsMsg
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StoreUint32(&c.handshakeStatus, 1)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs *serverHandshakeStateTLS13) processClientHello() error {
|
||||||
|
c := hs.c
|
||||||
|
|
||||||
|
hs.hello = new(serverHelloMsg)
|
||||||
|
hs.encryptedExtensions = new(encryptedExtensionsMsg)
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
hs.hello.sessionId = hs.clientHello.sessionId
|
||||||
|
hs.hello.compressionMethod = compressionNone
|
||||||
|
|
||||||
|
if hs.suite == nil {
|
||||||
|
var preferenceList []uint16
|
||||||
|
for _, suiteID := range c.config.CipherSuites {
|
||||||
|
for _, suite := range cipherSuitesTLS13 {
|
||||||
|
if suite.id == suiteID {
|
||||||
|
preferenceList = append(preferenceList, suiteID)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(preferenceList) == 0 {
|
||||||
|
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); selectedGroup != X25519 && !ok {
|
||||||
|
c.sendAlert(alertInternalError)
|
||||||
|
return errors.New("tls: CurvePreferences includes unsupported curve")
|
||||||
|
}
|
||||||
|
params, err := generateECDHEParameters(c.config.rand(), selectedGroup)
|
||||||
|
if err != nil {
|
||||||
|
c.sendAlert(alertInternalError)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hs.hello.serverShare = keyShare{group: selectedGroup, data: params.PublicKey()}
|
||||||
|
hs.sharedKey = params.SharedKey(clientKeyShare.data)
|
||||||
|
if hs.sharedKey == nil {
|
||||||
|
c.sendAlert(alertIllegalParameter)
|
||||||
|
return errors.New("tls: invalid client key share")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.serverName = hs.clientHello.serverName
|
||||||
|
|
||||||
|
if c.extraConfig != nil && c.extraConfig.ReceivedExtensions != nil {
|
||||||
|
c.extraConfig.ReceivedExtensions(typeClientHello, hs.clientHello.additionalExtensions)
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols)
|
||||||
|
if err != nil {
|
||||||
|
hs.alpnNegotiationErr = err
|
||||||
|
}
|
||||||
|
hs.encryptedExtensions.alpnProtocol = selectedProto
|
||||||
|
c.clientProtocol = selectedProto
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if hs.clientHello.earlyData {
|
||||||
|
if sessionState.maxEarlyData == 0 {
|
||||||
|
c.sendAlert(alertUnsupportedExtension)
|
||||||
|
return errors.New("tls: client sent unexpected early data")
|
||||||
|
}
|
||||||
|
|
||||||
|
if hs.alpnNegotiationErr == nil && sessionState.alpn == c.clientProtocol &&
|
||||||
|
c.extraConfig != nil && c.extraConfig.MaxEarlyData > 0 &&
|
||||||
|
c.extraConfig.Accept0RTT != nil && c.extraConfig.Accept0RTT(sessionState.appData) {
|
||||||
|
hs.encryptedExtensions.earlyData = true
|
||||||
|
c.used0RTT = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
h := cloneHash(hs.transcript, hs.suite.hash)
|
||||||
|
h.Write(hs.clientHello.marshal())
|
||||||
|
if hs.encryptedExtensions.earlyData {
|
||||||
|
clientEarlySecret := hs.suite.deriveSecret(hs.earlySecret, "c e traffic", h)
|
||||||
|
c.in.exportKey(Encryption0RTT, hs.suite, clientEarlySecret)
|
||||||
|
if err := c.config.writeKeyLog(keyLogLabelEarlyTraffic, hs.clientHello.random, clientEarlySecret); err != nil {
|
||||||
|
c.sendAlert(alertInternalError)
|
||||||
|
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(newClientHelloInfo(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")
|
||||||
|
}
|
||||||
|
|
||||||
|
if clientHello.earlyData {
|
||||||
|
c.sendAlert(alertIllegalParameter)
|
||||||
|
return errors.New("tls: client offered 0-RTT data in 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.exportKey(EncryptionHandshake, hs.suite, clientSecret)
|
||||||
|
c.in.setTrafficSecret(hs.suite, clientSecret)
|
||||||
|
serverSecret := hs.suite.deriveSecret(hs.handshakeSecret,
|
||||||
|
serverHandshakeTrafficLabel, hs.transcript)
|
||||||
|
c.out.exportKey(EncryptionHandshake, hs.suite, serverSecret)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if hs.alpnNegotiationErr != nil {
|
||||||
|
c.sendAlert(alertNoApplicationProtocol)
|
||||||
|
return hs.alpnNegotiationErr
|
||||||
|
}
|
||||||
|
if hs.c.extraConfig != nil && hs.c.extraConfig.GetExtensions != nil {
|
||||||
|
hs.encryptedExtensions.additionalExtensions = hs.c.extraConfig.GetExtensions(typeEncryptedExtensions)
|
||||||
|
}
|
||||||
|
|
||||||
|
hs.transcript.Write(hs.encryptedExtensions.marshal())
|
||||||
|
if _, err := c.writeRecord(recordTypeHandshake, hs.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.exportKey(EncryptionApplication, hs.suite, serverSecret)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
|
||||||
|
resumptionLabel, hs.transcript)
|
||||||
|
|
||||||
|
// Don't send session tickets when the alternative record layer is set.
|
||||||
|
// Instead, save the resumption secret on the Conn.
|
||||||
|
// Session tickets can then be generated by calling Conn.GetSessionTicket().
|
||||||
|
if hs.c.extraConfig != nil && hs.c.extraConfig.AlternativeRecordLayer != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := hs.c.getSessionTicketMsg(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
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.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret)
|
||||||
|
c.in.setTrafficSecret(hs.suite, hs.trafficSecret)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,357 @@
|
||||||
|
// 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 qtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
var 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
|
||||||
|
params ecdheParameters
|
||||||
|
|
||||||
|
// 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); curveID != X25519 && !ok {
|
||||||
|
return nil, errors.New("tls: CurvePreferences includes unsupported curve")
|
||||||
|
}
|
||||||
|
|
||||||
|
params, err := generateECDHEParameters(config.rand(), curveID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ka.params = params
|
||||||
|
|
||||||
|
// See RFC 4492, Section 5.4.
|
||||||
|
ecdhePublic := params.PublicKey()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
preMasterSecret := ka.params.SharedKey(ckx.ciphertext[1:])
|
||||||
|
if preMasterSecret == 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); curveID != X25519 && !ok {
|
||||||
|
return errors.New("tls: server selected unsupported curve")
|
||||||
|
}
|
||||||
|
|
||||||
|
params, err := generateECDHEParameters(config.rand(), curveID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ka.params = params
|
||||||
|
|
||||||
|
ka.preMasterSecret = params.SharedKey(publicKey)
|
||||||
|
if ka.preMasterSecret == nil {
|
||||||
|
return errServerKeyExchange
|
||||||
|
}
|
||||||
|
|
||||||
|
ourPublicKey := params.PublicKey()
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
// 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 qtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/hmac"
|
||||||
|
"errors"
|
||||||
|
"hash"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/cryptobyte"
|
||||||
|
"golang.org/x/crypto/curve25519"
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ecdheParameters implements Diffie-Hellman with either NIST curves or X25519,
|
||||||
|
// according to RFC 8446, Section 4.2.8.2.
|
||||||
|
type ecdheParameters interface {
|
||||||
|
CurveID() CurveID
|
||||||
|
PublicKey() []byte
|
||||||
|
SharedKey(peerPublicKey []byte) []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateECDHEParameters(rand io.Reader, curveID CurveID) (ecdheParameters, error) {
|
||||||
|
if curveID == X25519 {
|
||||||
|
privateKey := make([]byte, curve25519.ScalarSize)
|
||||||
|
if _, err := io.ReadFull(rand, privateKey); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
publicKey, err := curve25519.X25519(privateKey, curve25519.Basepoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &x25519Parameters{privateKey: privateKey, publicKey: publicKey}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
curve, ok := curveForCurveID(curveID)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("tls: internal error: unsupported curve")
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &nistParameters{curveID: curveID}
|
||||||
|
var err error
|
||||||
|
p.privateKey, p.x, p.y, err = elliptic.GenerateKey(curve, rand)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func curveForCurveID(id CurveID) (elliptic.Curve, bool) {
|
||||||
|
switch id {
|
||||||
|
case CurveP256:
|
||||||
|
return elliptic.P256(), true
|
||||||
|
case CurveP384:
|
||||||
|
return elliptic.P384(), true
|
||||||
|
case CurveP521:
|
||||||
|
return elliptic.P521(), true
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type nistParameters struct {
|
||||||
|
privateKey []byte
|
||||||
|
x, y *big.Int // public key
|
||||||
|
curveID CurveID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *nistParameters) CurveID() CurveID {
|
||||||
|
return p.curveID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *nistParameters) PublicKey() []byte {
|
||||||
|
curve, _ := curveForCurveID(p.curveID)
|
||||||
|
return elliptic.Marshal(curve, p.x, p.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *nistParameters) SharedKey(peerPublicKey []byte) []byte {
|
||||||
|
curve, _ := curveForCurveID(p.curveID)
|
||||||
|
// Unmarshal also checks whether the given point is on the curve.
|
||||||
|
x, y := elliptic.Unmarshal(curve, peerPublicKey)
|
||||||
|
if x == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
xShared, _ := curve.ScalarMult(x, y, p.privateKey)
|
||||||
|
sharedKey := make([]byte, (curve.Params().BitSize+7)/8)
|
||||||
|
return xShared.FillBytes(sharedKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
type x25519Parameters struct {
|
||||||
|
privateKey []byte
|
||||||
|
publicKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *x25519Parameters) CurveID() CurveID {
|
||||||
|
return X25519
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *x25519Parameters) PublicKey() []byte {
|
||||||
|
return p.publicKey[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *x25519Parameters) SharedKey(peerPublicKey []byte) []byte {
|
||||||
|
sharedKey, err := curve25519.X25519(p.privateKey, peerPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return sharedKey
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
// 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 qtls
|
||||||
|
|
||||||
|
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
|
|
@ -0,0 +1,283 @@
|
||||||
|
// 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 qtls
|
||||||
|
|
||||||
|
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")
|
||||||
|
var keyExpansionLabel = []byte("key expansion")
|
||||||
|
var clientFinishedLabel = []byte("client finished")
|
||||||
|
var 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, masterSecret []byte) []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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,274 @@
|
||||||
|
// 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 qtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/subtle"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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.
|
||||||
|
// version (revision = 1) carries the max_early_data_size sent in the ticket.
|
||||||
|
// version (revision = 2) carries the ALPN sent in the ticket.
|
||||||
|
type sessionStateTLS13 struct {
|
||||||
|
// uint8 version = 0x0304;
|
||||||
|
// uint8 revision = 2;
|
||||||
|
cipherSuite uint16
|
||||||
|
createdAt uint64
|
||||||
|
resumptionSecret []byte // opaque resumption_master_secret<1..2^8-1>;
|
||||||
|
certificate Certificate // CertificateEntry certificate_list<0..2^24-1>;
|
||||||
|
maxEarlyData uint32
|
||||||
|
alpn string
|
||||||
|
|
||||||
|
appData []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *sessionStateTLS13) marshal() []byte {
|
||||||
|
var b cryptobyte.Builder
|
||||||
|
b.AddUint16(VersionTLS13)
|
||||||
|
b.AddUint8(2) // revision
|
||||||
|
b.AddUint16(m.cipherSuite)
|
||||||
|
addUint64(&b, m.createdAt)
|
||||||
|
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||||
|
b.AddBytes(m.resumptionSecret)
|
||||||
|
})
|
||||||
|
marshalCertificate(&b, m.certificate)
|
||||||
|
b.AddUint32(m.maxEarlyData)
|
||||||
|
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||||
|
b.AddBytes([]byte(m.alpn))
|
||||||
|
})
|
||||||
|
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||||
|
b.AddBytes(m.appData)
|
||||||
|
})
|
||||||
|
return b.BytesOrPanic()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *sessionStateTLS13) unmarshal(data []byte) bool {
|
||||||
|
*m = sessionStateTLS13{}
|
||||||
|
s := cryptobyte.String(data)
|
||||||
|
var version uint16
|
||||||
|
var revision uint8
|
||||||
|
var alpn []byte
|
||||||
|
ret := s.ReadUint16(&version) &&
|
||||||
|
version == VersionTLS13 &&
|
||||||
|
s.ReadUint8(&revision) &&
|
||||||
|
revision == 2 &&
|
||||||
|
s.ReadUint16(&m.cipherSuite) &&
|
||||||
|
readUint64(&s, &m.createdAt) &&
|
||||||
|
readUint8LengthPrefixed(&s, &m.resumptionSecret) &&
|
||||||
|
len(m.resumptionSecret) != 0 &&
|
||||||
|
unmarshalCertificate(&s, &m.certificate) &&
|
||||||
|
s.ReadUint32(&m.maxEarlyData) &&
|
||||||
|
readUint8LengthPrefixed(&s, &alpn) &&
|
||||||
|
readUint16LengthPrefixed(&s, &m.appData) &&
|
||||||
|
s.Empty()
|
||||||
|
m.alpn = string(alpn)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) getSessionTicketMsg(appData []byte) (*newSessionTicketMsgTLS13, error) {
|
||||||
|
m := new(newSessionTicketMsgTLS13)
|
||||||
|
|
||||||
|
var certsFromClient [][]byte
|
||||||
|
for _, cert := range c.peerCertificates {
|
||||||
|
certsFromClient = append(certsFromClient, cert.Raw)
|
||||||
|
}
|
||||||
|
state := sessionStateTLS13{
|
||||||
|
cipherSuite: c.cipherSuite,
|
||||||
|
createdAt: uint64(c.config.time().Unix()),
|
||||||
|
resumptionSecret: c.resumptionSecret,
|
||||||
|
certificate: Certificate{
|
||||||
|
Certificate: certsFromClient,
|
||||||
|
OCSPStaple: c.ocspResponse,
|
||||||
|
SignedCertificateTimestamps: c.scts,
|
||||||
|
},
|
||||||
|
appData: appData,
|
||||||
|
alpn: c.clientProtocol,
|
||||||
|
}
|
||||||
|
if c.extraConfig != nil {
|
||||||
|
state.maxEarlyData = c.extraConfig.MaxEarlyData
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
m.label, err = c.encryptTicket(state.marshal())
|
||||||
|
if err != nil {
|
||||||
|
return nil, 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 = c.config.rand().Read(ageAdd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 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 c.extraConfig != nil {
|
||||||
|
m.maxEarlyData = c.extraConfig.MaxEarlyData
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSessionTicket generates a new session ticket.
|
||||||
|
// It should only be called after the handshake completes.
|
||||||
|
// It can only be used for servers, and only if the alternative record layer is set.
|
||||||
|
// The ticket may be nil if config.SessionTicketsDisabled is set,
|
||||||
|
// or if the client isn't able to receive session tickets.
|
||||||
|
func (c *Conn) GetSessionTicket(appData []byte) ([]byte, error) {
|
||||||
|
if c.isClient || !c.handshakeComplete() || c.extraConfig == nil || c.extraConfig.AlternativeRecordLayer == nil {
|
||||||
|
return nil, errors.New("GetSessionTicket is only valid for servers after completion of the handshake, and if an alternative record layer is set.")
|
||||||
|
}
|
||||||
|
if c.config.SessionTicketsDisabled {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := c.getSessionTicketMsg(appData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m.marshal(), nil
|
||||||
|
}
|
|
@ -0,0 +1,362 @@
|
||||||
|
// 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 qtls partially implements TLS 1.2, as specified in RFC 5246,
|
||||||
|
// and TLS 1.3, as specified in RFC 8446.
|
||||||
|
package qtls
|
||||||
|
|
||||||
|
// 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, extraConfig *ExtraConfig) *Conn {
|
||||||
|
c := &Conn{
|
||||||
|
conn: conn,
|
||||||
|
config: fromConfig(config),
|
||||||
|
extraConfig: extraConfig,
|
||||||
|
}
|
||||||
|
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, extraConfig *ExtraConfig) *Conn {
|
||||||
|
c := &Conn{
|
||||||
|
conn: conn,
|
||||||
|
config: fromConfig(config),
|
||||||
|
extraConfig: extraConfig,
|
||||||
|
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
|
||||||
|
extraConfig *ExtraConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, l.extraConfig), 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, extraConfig *ExtraConfig) net.Listener {
|
||||||
|
l := new(listener)
|
||||||
|
l.Listener = inner
|
||||||
|
l.config = config
|
||||||
|
l.extraConfig = extraConfig
|
||||||
|
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, extraConfig *ExtraConfig) (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, extraConfig), 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, extraConfig *ExtraConfig) (*Conn, error) {
|
||||||
|
return dial(context.Background(), dialer, network, addr, config, extraConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dial(ctx context.Context, netDialer *net.Dialer, network, addr string, config *Config, extraConfig *ExtraConfig) (*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, extraConfig)
|
||||||
|
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, extraConfig *ExtraConfig) (*Conn, error) {
|
||||||
|
return DialWithDialer(new(net.Dialer), network, addr, config, extraConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
ExtraConfig *ExtraConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, d.ExtraConfig)
|
||||||
|
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")
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package qtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if !structsEqual(&tls.ConnectionState{}, &connectionState{}) {
|
||||||
|
panic("qtls.ConnectionState doesn't match")
|
||||||
|
}
|
||||||
|
if !structsEqual(&tls.ClientSessionState{}, &clientSessionState{}) {
|
||||||
|
panic("qtls.ClientSessionState doesn't match")
|
||||||
|
}
|
||||||
|
if !structsEqual(&tls.CertificateRequestInfo{}, &certificateRequestInfo{}) {
|
||||||
|
panic("qtls.CertificateRequestInfo doesn't match")
|
||||||
|
}
|
||||||
|
if !structsEqual(&tls.Config{}, &config{}) {
|
||||||
|
panic("qtls.Config doesn't match")
|
||||||
|
}
|
||||||
|
if !structsEqual(&tls.ClientHelloInfo{}, &clientHelloInfo{}) {
|
||||||
|
panic("qtls.ClientHelloInfo doesn't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toConnectionState(c connectionState) ConnectionState {
|
||||||
|
return *(*ConnectionState)(unsafe.Pointer(&c))
|
||||||
|
}
|
||||||
|
|
||||||
|
func toClientSessionState(s *clientSessionState) *ClientSessionState {
|
||||||
|
return (*ClientSessionState)(unsafe.Pointer(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromClientSessionState(s *ClientSessionState) *clientSessionState {
|
||||||
|
return (*clientSessionState)(unsafe.Pointer(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func toCertificateRequestInfo(i *certificateRequestInfo) *CertificateRequestInfo {
|
||||||
|
return (*CertificateRequestInfo)(unsafe.Pointer(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
func toConfig(c *config) *Config {
|
||||||
|
return (*Config)(unsafe.Pointer(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromConfig(c *Config) *config {
|
||||||
|
return (*config)(unsafe.Pointer(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
func toClientHelloInfo(chi *clientHelloInfo) *ClientHelloInfo {
|
||||||
|
return (*ClientHelloInfo)(unsafe.Pointer(chi))
|
||||||
|
}
|
||||||
|
|
||||||
|
func structsEqual(a, b interface{}) bool {
|
||||||
|
return compare(reflect.ValueOf(a), reflect.ValueOf(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func compare(a, b reflect.Value) bool {
|
||||||
|
sa := a.Elem()
|
||||||
|
sb := b.Elem()
|
||||||
|
if sa.NumField() != sb.NumField() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < sa.NumField(); i++ {
|
||||||
|
fa := sa.Type().Field(i)
|
||||||
|
fb := sb.Type().Field(i)
|
||||||
|
if !reflect.DeepEqual(fa.Index, fb.Index) || fa.Name != fb.Name || fa.Anonymous != fb.Anonymous || fa.Offset != fb.Offset || !reflect.DeepEqual(fa.Type, fb.Type) {
|
||||||
|
if fa.Type.Kind() != fb.Type.Kind() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if fa.Type.Kind() == reflect.Slice {
|
||||||
|
if !compareStruct(fa.Type.Elem(), fb.Type.Elem()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareStruct(a, b reflect.Type) bool {
|
||||||
|
if a.NumField() != b.NumField() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < a.NumField(); i++ {
|
||||||
|
fa := a.Field(i)
|
||||||
|
fb := b.Field(i)
|
||||||
|
if !reflect.DeepEqual(fa.Index, fb.Index) || fa.Name != fb.Name || fa.Anonymous != fb.Anonymous || fa.Offset != fb.Offset || !reflect.DeepEqual(fa.Type, fb.Type) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
|
@ -3,7 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Package bpf implements marshaling and unmarshaling of programs for the
|
Package bpf implements marshaling and unmarshaling of programs for the
|
||||||
Berkeley Packet Filter virtual machine, and provides a Go implementation
|
Berkeley Packet Filter virtual machine, and provides a Go implementation
|
||||||
of the virtual machine.
|
of the virtual machine.
|
||||||
|
@ -21,7 +20,7 @@ access to kernel functions, and while conditional branches are
|
||||||
allowed, they can only jump forwards, to guarantee that there are no
|
allowed, they can only jump forwards, to guarantee that there are no
|
||||||
infinite loops.
|
infinite loops.
|
||||||
|
|
||||||
The virtual machine
|
# The virtual machine
|
||||||
|
|
||||||
The BPF VM is an accumulator machine. Its main register, called
|
The BPF VM is an accumulator machine. Its main register, called
|
||||||
register A, is an implicit source and destination in all arithmetic
|
register A, is an implicit source and destination in all arithmetic
|
||||||
|
@ -50,7 +49,7 @@ to extensions, which are essentially calls to kernel utility
|
||||||
functions. Currently, the only extensions supported by this package
|
functions. Currently, the only extensions supported by this package
|
||||||
are the Linux packet filter extensions.
|
are the Linux packet filter extensions.
|
||||||
|
|
||||||
Examples
|
# Examples
|
||||||
|
|
||||||
This packet filter selects all ARP packets.
|
This packet filter selects all ARP packets.
|
||||||
|
|
||||||
|
@ -77,6 +76,5 @@ This packet filter captures a random 1% sample of traffic.
|
||||||
// Ignore.
|
// Ignore.
|
||||||
bpf.RetConstant{Val: 0},
|
bpf.RetConstant{Val: 0},
|
||||||
})
|
})
|
||||||
|
|
||||||
*/
|
*/
|
||||||
package bpf // import "golang.org/x/net/bpf"
|
package bpf // import "golang.org/x/net/bpf"
|
||||||
|
|
|
@ -173,11 +173,13 @@ func tokenEqual(t1, t2 string) bool {
|
||||||
|
|
||||||
// isLWS reports whether b is linear white space, according
|
// isLWS reports whether b is linear white space, according
|
||||||
// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
||||||
|
//
|
||||||
// LWS = [CRLF] 1*( SP | HT )
|
// LWS = [CRLF] 1*( SP | HT )
|
||||||
func isLWS(b byte) bool { return b == ' ' || b == '\t' }
|
func isLWS(b byte) bool { return b == ' ' || b == '\t' }
|
||||||
|
|
||||||
// isCTL reports whether b is a control byte, according
|
// isCTL reports whether b is a control byte, according
|
||||||
// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
||||||
|
//
|
||||||
// CTL = <any US-ASCII control character
|
// CTL = <any US-ASCII control character
|
||||||
// (octets 0 - 31) and DEL (127)>
|
// (octets 0 - 31) and DEL (127)>
|
||||||
func isCTL(b byte) bool {
|
func isCTL(b byte) bool {
|
||||||
|
@ -190,6 +192,7 @@ func isCTL(b byte) bool {
|
||||||
// letters are not allowed.
|
// letters are not allowed.
|
||||||
//
|
//
|
||||||
// RFC 7230 says:
|
// RFC 7230 says:
|
||||||
|
//
|
||||||
// header-field = field-name ":" OWS field-value OWS
|
// header-field = field-name ":" OWS field-value OWS
|
||||||
// field-name = token
|
// field-name = token
|
||||||
// token = 1*tchar
|
// token = 1*tchar
|
||||||
|
@ -282,6 +285,7 @@ var validHostByte = [256]bool{
|
||||||
// (octets 0 - 31) and DEL (127)>
|
// (octets 0 - 31) and DEL (127)>
|
||||||
//
|
//
|
||||||
// RFC 7230 says:
|
// RFC 7230 says:
|
||||||
|
//
|
||||||
// field-value = *( field-content / obs-fold )
|
// field-value = *( field-content / obs-fold )
|
||||||
// obj-fold = N/A to http2, and deprecated
|
// obj-fold = N/A to http2, and deprecated
|
||||||
// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
||||||
|
|
|
@ -139,7 +139,6 @@ func (p *clientConnPool) getStartDialLocked(ctx context.Context, addr string) *d
|
||||||
func (c *dialCall) dial(ctx context.Context, addr string) {
|
func (c *dialCall) dial(ctx context.Context, addr string) {
|
||||||
const singleUse = false // shared conn
|
const singleUse = false // shared conn
|
||||||
c.res, c.err = c.p.t.dialClientConn(ctx, addr, singleUse)
|
c.res, c.err = c.p.t.dialClientConn(ctx, addr, singleUse)
|
||||||
close(c.done)
|
|
||||||
|
|
||||||
c.p.mu.Lock()
|
c.p.mu.Lock()
|
||||||
delete(c.p.dialing, addr)
|
delete(c.p.dialing, addr)
|
||||||
|
@ -147,6 +146,8 @@ func (c *dialCall) dial(ctx context.Context, addr string) {
|
||||||
c.p.addConnLocked(addr, c.res)
|
c.p.addConnLocked(addr, c.res)
|
||||||
}
|
}
|
||||||
c.p.mu.Unlock()
|
c.p.mu.Unlock()
|
||||||
|
|
||||||
|
close(c.done)
|
||||||
}
|
}
|
||||||
|
|
||||||
// addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't
|
// addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't
|
||||||
|
|
|
@ -136,7 +136,7 @@ func (e headerFieldNameError) Error() string {
|
||||||
type headerFieldValueError string
|
type headerFieldValueError string
|
||||||
|
|
||||||
func (e headerFieldValueError) Error() string {
|
func (e headerFieldValueError) Error() string {
|
||||||
return fmt.Sprintf("invalid header field value %q", string(e))
|
return fmt.Sprintf("invalid header field value for %q", string(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -1532,7 +1532,8 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
|
||||||
fr.debugReadLoggerf("http2: decoded hpack field %+v", hf)
|
fr.debugReadLoggerf("http2: decoded hpack field %+v", hf)
|
||||||
}
|
}
|
||||||
if !httpguts.ValidHeaderFieldValue(hf.Value) {
|
if !httpguts.ValidHeaderFieldValue(hf.Value) {
|
||||||
invalid = headerFieldValueError(hf.Value)
|
// Don't include the value in the error, because it may be sensitive.
|
||||||
|
invalid = headerFieldValueError(hf.Name)
|
||||||
}
|
}
|
||||||
isPseudo := strings.HasPrefix(hf.Name, ":")
|
isPseudo := strings.HasPrefix(hf.Name, ":")
|
||||||
if isPseudo {
|
if isPseudo {
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2021 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 go1.18
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tlsUnderlyingConn(tc *tls.Conn) net.Conn {
|
||||||
|
return tc.NetConn()
|
||||||
|
}
|
|
@ -169,25 +169,50 @@ func buildRootHuffmanNode() {
|
||||||
// AppendHuffmanString appends s, as encoded in Huffman codes, to dst
|
// AppendHuffmanString appends s, as encoded in Huffman codes, to dst
|
||||||
// and returns the extended buffer.
|
// and returns the extended buffer.
|
||||||
func AppendHuffmanString(dst []byte, s string) []byte {
|
func AppendHuffmanString(dst []byte, s string) []byte {
|
||||||
rembits := uint8(8)
|
// This relies on the maximum huffman code length being 30 (See tables.go huffmanCodeLen array)
|
||||||
|
// So if a uint64 buffer has less than 32 valid bits can always accommodate another huffmanCode.
|
||||||
|
var (
|
||||||
|
x uint64 // buffer
|
||||||
|
n uint // number valid of bits present in x
|
||||||
|
)
|
||||||
for i := 0; i < len(s); i++ {
|
for i := 0; i < len(s); i++ {
|
||||||
if rembits == 8 {
|
c := s[i]
|
||||||
dst = append(dst, 0)
|
n += uint(huffmanCodeLen[c])
|
||||||
|
x <<= huffmanCodeLen[c] % 64
|
||||||
|
x |= uint64(huffmanCodes[c])
|
||||||
|
if n >= 32 {
|
||||||
|
n %= 32 // Normally would be -= 32 but %= 32 informs compiler 0 <= n <= 31 for upcoming shift
|
||||||
|
y := uint32(x >> n) // Compiler doesn't combine memory writes if y isn't uint32
|
||||||
|
dst = append(dst, byte(y>>24), byte(y>>16), byte(y>>8), byte(y))
|
||||||
}
|
}
|
||||||
dst, rembits = appendByteToHuffmanCode(dst, rembits, s[i])
|
|
||||||
}
|
}
|
||||||
|
// Add padding bits if necessary
|
||||||
if rembits < 8 {
|
if over := n % 8; over > 0 {
|
||||||
// special EOS symbol
|
const (
|
||||||
code := uint32(0x3fffffff)
|
eosCode = 0x3fffffff
|
||||||
nbits := uint8(30)
|
eosNBits = 30
|
||||||
|
eosPadByte = eosCode >> (eosNBits - 8)
|
||||||
t := uint8(code >> (nbits - rembits))
|
)
|
||||||
dst[len(dst)-1] |= t
|
pad := 8 - over
|
||||||
|
x = (x << pad) | (eosPadByte >> over)
|
||||||
|
n += pad // 8 now divides into n exactly
|
||||||
}
|
}
|
||||||
|
// n in (0, 8, 16, 24, 32)
|
||||||
|
switch n / 8 {
|
||||||
|
case 0:
|
||||||
return dst
|
return dst
|
||||||
|
case 1:
|
||||||
|
return append(dst, byte(x))
|
||||||
|
case 2:
|
||||||
|
y := uint16(x)
|
||||||
|
return append(dst, byte(y>>8), byte(y))
|
||||||
|
case 3:
|
||||||
|
y := uint16(x >> 8)
|
||||||
|
return append(dst, byte(y>>8), byte(y), byte(x))
|
||||||
|
}
|
||||||
|
// case 4:
|
||||||
|
y := uint32(x)
|
||||||
|
return append(dst, byte(y>>24), byte(y>>16), byte(y>>8), byte(y))
|
||||||
}
|
}
|
||||||
|
|
||||||
// HuffmanEncodeLength returns the number of bytes required to encode
|
// HuffmanEncodeLength returns the number of bytes required to encode
|
||||||
|
@ -199,35 +224,3 @@ func HuffmanEncodeLength(s string) uint64 {
|
||||||
}
|
}
|
||||||
return (n + 7) / 8
|
return (n + 7) / 8
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendByteToHuffmanCode appends Huffman code for c to dst and
|
|
||||||
// returns the extended buffer and the remaining bits in the last
|
|
||||||
// element. The appending is not byte aligned and the remaining bits
|
|
||||||
// in the last element of dst is given in rembits.
|
|
||||||
func appendByteToHuffmanCode(dst []byte, rembits uint8, c byte) ([]byte, uint8) {
|
|
||||||
code := huffmanCodes[c]
|
|
||||||
nbits := huffmanCodeLen[c]
|
|
||||||
|
|
||||||
for {
|
|
||||||
if rembits > nbits {
|
|
||||||
t := uint8(code << (rembits - nbits))
|
|
||||||
dst[len(dst)-1] |= t
|
|
||||||
rembits -= nbits
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
t := uint8(code >> (nbits - rembits))
|
|
||||||
dst[len(dst)-1] |= t
|
|
||||||
|
|
||||||
nbits -= rembits
|
|
||||||
rembits = 8
|
|
||||||
|
|
||||||
if nbits == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
dst = append(dst, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return dst, rembits
|
|
||||||
}
|
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
// See https://http2.github.io/ for more information on HTTP/2.
|
// See https://http2.github.io/ for more information on HTTP/2.
|
||||||
//
|
//
|
||||||
// See https://http2.golang.org/ for a test server running this code.
|
// See https://http2.golang.org/ for a test server running this code.
|
||||||
//
|
|
||||||
package http2 // import "golang.org/x/net/http2"
|
package http2 // import "golang.org/x/net/http2"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -176,6 +175,7 @@ func (s SettingID) String() string {
|
||||||
// name (key). See httpguts.ValidHeaderName for the base rules.
|
// name (key). See httpguts.ValidHeaderName for the base rules.
|
||||||
//
|
//
|
||||||
// Further, http2 says:
|
// Further, http2 says:
|
||||||
|
//
|
||||||
// "Just as in HTTP/1.x, header field names are strings of ASCII
|
// "Just as in HTTP/1.x, header field names are strings of ASCII
|
||||||
// characters that are compared in a case-insensitive
|
// characters that are compared in a case-insensitive
|
||||||
// fashion. However, header field names MUST be converted to
|
// fashion. However, header field names MUST be converted to
|
||||||
|
@ -365,8 +365,8 @@ func (s *sorter) SortStrings(ss []string) {
|
||||||
// validPseudoPath reports whether v is a valid :path pseudo-header
|
// validPseudoPath reports whether v is a valid :path pseudo-header
|
||||||
// value. It must be either:
|
// value. It must be either:
|
||||||
//
|
//
|
||||||
// *) a non-empty string starting with '/'
|
// - a non-empty string starting with '/'
|
||||||
// *) the string '*', for OPTIONS requests.
|
// - the string '*', for OPTIONS requests.
|
||||||
//
|
//
|
||||||
// For now this is only used a quick check for deciding when to clean
|
// For now this is only used a quick check for deciding when to clean
|
||||||
// up Opaque URLs before sending requests from the Transport.
|
// up Opaque URLs before sending requests from the Transport.
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2021 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 !go1.18
|
||||||
|
// +build !go1.18
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tlsUnderlyingConn(tc *tls.Conn) net.Conn {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -315,6 +315,20 @@ type ServeConnOpts struct {
|
||||||
// requests. If nil, BaseConfig.Handler is used. If BaseConfig
|
// requests. If nil, BaseConfig.Handler is used. If BaseConfig
|
||||||
// or BaseConfig.Handler is nil, http.DefaultServeMux is used.
|
// or BaseConfig.Handler is nil, http.DefaultServeMux is used.
|
||||||
Handler http.Handler
|
Handler http.Handler
|
||||||
|
|
||||||
|
// UpgradeRequest is an initial request received on a connection
|
||||||
|
// undergoing an h2c upgrade. The request body must have been
|
||||||
|
// completely read from the connection before calling ServeConn,
|
||||||
|
// and the 101 Switching Protocols response written.
|
||||||
|
UpgradeRequest *http.Request
|
||||||
|
|
||||||
|
// Settings is the decoded contents of the HTTP2-Settings header
|
||||||
|
// in an h2c upgrade request.
|
||||||
|
Settings []byte
|
||||||
|
|
||||||
|
// SawClientPreface is set if the HTTP/2 connection preface
|
||||||
|
// has already been read from the connection.
|
||||||
|
SawClientPreface bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ServeConnOpts) context() context.Context {
|
func (o *ServeConnOpts) context() context.Context {
|
||||||
|
@ -383,6 +397,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
||||||
headerTableSize: initialHeaderTableSize,
|
headerTableSize: initialHeaderTableSize,
|
||||||
serveG: newGoroutineLock(),
|
serveG: newGoroutineLock(),
|
||||||
pushEnabled: true,
|
pushEnabled: true,
|
||||||
|
sawClientPreface: opts.SawClientPreface,
|
||||||
}
|
}
|
||||||
|
|
||||||
s.state.registerConn(sc)
|
s.state.registerConn(sc)
|
||||||
|
@ -400,7 +415,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
||||||
if s.NewWriteScheduler != nil {
|
if s.NewWriteScheduler != nil {
|
||||||
sc.writeSched = s.NewWriteScheduler()
|
sc.writeSched = s.NewWriteScheduler()
|
||||||
} else {
|
} else {
|
||||||
sc.writeSched = NewRandomWriteScheduler()
|
sc.writeSched = NewPriorityWriteScheduler(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// These start at the RFC-specified defaults. If there is a higher
|
// These start at the RFC-specified defaults. If there is a higher
|
||||||
|
@ -465,9 +480,27 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.Settings != nil {
|
||||||
|
fr := &SettingsFrame{
|
||||||
|
FrameHeader: FrameHeader{valid: true},
|
||||||
|
p: opts.Settings,
|
||||||
|
}
|
||||||
|
if err := fr.ForeachSetting(sc.processSetting); err != nil {
|
||||||
|
sc.rejectConn(ErrCodeProtocol, "invalid settings")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
opts.Settings = nil
|
||||||
|
}
|
||||||
|
|
||||||
if hook := testHookGetServerConn; hook != nil {
|
if hook := testHookGetServerConn; hook != nil {
|
||||||
hook(sc)
|
hook(sc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.UpgradeRequest != nil {
|
||||||
|
sc.upgradeRequest(opts.UpgradeRequest)
|
||||||
|
opts.UpgradeRequest = nil
|
||||||
|
}
|
||||||
|
|
||||||
sc.serve()
|
sc.serve()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,6 +545,7 @@ type serverConn struct {
|
||||||
// Everything following is owned by the serve loop; use serveG.check():
|
// Everything following is owned by the serve loop; use serveG.check():
|
||||||
serveG goroutineLock // used to verify funcs are on serve()
|
serveG goroutineLock // used to verify funcs are on serve()
|
||||||
pushEnabled bool
|
pushEnabled bool
|
||||||
|
sawClientPreface bool // preface has already been read, used in h2c upgrade
|
||||||
sawFirstSettings bool // got the initial SETTINGS frame after the preface
|
sawFirstSettings bool // got the initial SETTINGS frame after the preface
|
||||||
needToSendSettingsAck bool
|
needToSendSettingsAck bool
|
||||||
unackedSettings int // how many SETTINGS have we sent without ACKs?
|
unackedSettings int // how many SETTINGS have we sent without ACKs?
|
||||||
|
@ -974,6 +1008,9 @@ var errPrefaceTimeout = errors.New("timeout waiting for client preface")
|
||||||
// returns errPrefaceTimeout on timeout, or an error if the greeting
|
// returns errPrefaceTimeout on timeout, or an error if the greeting
|
||||||
// is invalid.
|
// is invalid.
|
||||||
func (sc *serverConn) readPreface() error {
|
func (sc *serverConn) readPreface() error {
|
||||||
|
if sc.sawClientPreface {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
errc := make(chan error, 1)
|
errc := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
// Read the client preface
|
// Read the client preface
|
||||||
|
@ -1915,6 +1952,26 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sc *serverConn) upgradeRequest(req *http.Request) {
|
||||||
|
sc.serveG.check()
|
||||||
|
id := uint32(1)
|
||||||
|
sc.maxClientStreamID = id
|
||||||
|
st := sc.newStream(id, 0, stateHalfClosedRemote)
|
||||||
|
st.reqTrailer = req.Trailer
|
||||||
|
if st.reqTrailer != nil {
|
||||||
|
st.trailer = make(http.Header)
|
||||||
|
}
|
||||||
|
rw := sc.newResponseWriter(st, req)
|
||||||
|
|
||||||
|
// Disable any read deadline set by the net/http package
|
||||||
|
// prior to the upgrade.
|
||||||
|
if sc.hs.ReadTimeout != 0 {
|
||||||
|
sc.conn.SetReadDeadline(time.Time{})
|
||||||
|
}
|
||||||
|
|
||||||
|
go sc.runHandler(rw, req, sc.handler.ServeHTTP)
|
||||||
|
}
|
||||||
|
|
||||||
func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
|
func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
|
||||||
sc := st.sc
|
sc := st.sc
|
||||||
sc.serveG.check()
|
sc.serveG.check()
|
||||||
|
@ -2145,6 +2202,11 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
|
||||||
}
|
}
|
||||||
req = req.WithContext(st.ctx)
|
req = req.WithContext(st.ctx)
|
||||||
|
|
||||||
|
rw := sc.newResponseWriter(st, req)
|
||||||
|
return rw, req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *serverConn) newResponseWriter(st *stream, req *http.Request) *responseWriter {
|
||||||
rws := responseWriterStatePool.Get().(*responseWriterState)
|
rws := responseWriterStatePool.Get().(*responseWriterState)
|
||||||
bwSave := rws.bw
|
bwSave := rws.bw
|
||||||
*rws = responseWriterState{} // zero all the fields
|
*rws = responseWriterState{} // zero all the fields
|
||||||
|
@ -2153,10 +2215,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
|
||||||
rws.bw.Reset(chunkWriter{rws})
|
rws.bw.Reset(chunkWriter{rws})
|
||||||
rws.stream = st
|
rws.stream = st
|
||||||
rws.req = req
|
rws.req = req
|
||||||
rws.body = body
|
return &responseWriter{rws: rws}
|
||||||
|
|
||||||
rw := &responseWriter{rws: rws}
|
|
||||||
return rw, req, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run on its own goroutine.
|
// Run on its own goroutine.
|
||||||
|
@ -2316,17 +2375,18 @@ type requestBody struct {
|
||||||
_ incomparable
|
_ incomparable
|
||||||
stream *stream
|
stream *stream
|
||||||
conn *serverConn
|
conn *serverConn
|
||||||
closed bool // for use by Close only
|
closeOnce sync.Once // for use by Close only
|
||||||
sawEOF bool // for use by Read only
|
sawEOF bool // for use by Read only
|
||||||
pipe *pipe // non-nil if we have a HTTP entity message body
|
pipe *pipe // non-nil if we have a HTTP entity message body
|
||||||
needsContinue bool // need to send a 100-continue
|
needsContinue bool // need to send a 100-continue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *requestBody) Close() error {
|
func (b *requestBody) Close() error {
|
||||||
if b.pipe != nil && !b.closed {
|
b.closeOnce.Do(func() {
|
||||||
|
if b.pipe != nil {
|
||||||
b.pipe.BreakWithError(errClosedBody)
|
b.pipe.BreakWithError(errClosedBody)
|
||||||
}
|
}
|
||||||
b.closed = true
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2370,7 +2430,6 @@ type responseWriterState struct {
|
||||||
// immutable within a request:
|
// immutable within a request:
|
||||||
stream *stream
|
stream *stream
|
||||||
req *http.Request
|
req *http.Request
|
||||||
body *requestBody // to close at end of request, if DATA frames didn't
|
|
||||||
conn *serverConn
|
conn *serverConn
|
||||||
|
|
||||||
// TODO: adjust buffer writing sizes based on server config, frame size updates from peer, etc
|
// TODO: adjust buffer writing sizes based on server config, frame size updates from peer, etc
|
||||||
|
@ -2546,6 +2605,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
||||||
// prior to the headers being written. If the set of trailers is fixed
|
// prior to the headers being written. If the set of trailers is fixed
|
||||||
// or known before the header is written, the normal Go trailers mechanism
|
// or known before the header is written, the normal Go trailers mechanism
|
||||||
// is preferred:
|
// is preferred:
|
||||||
|
//
|
||||||
// https://golang.org/pkg/net/http/#ResponseWriter
|
// https://golang.org/pkg/net/http/#ResponseWriter
|
||||||
// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
|
// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
|
||||||
const TrailerPrefix = "Trailer:"
|
const TrailerPrefix = "Trailer:"
|
||||||
|
@ -2643,8 +2703,7 @@ func checkWriteHeaderCode(code int) {
|
||||||
// Issue 22880: require valid WriteHeader status codes.
|
// Issue 22880: require valid WriteHeader status codes.
|
||||||
// For now we only enforce that it's three digits.
|
// For now we only enforce that it's three digits.
|
||||||
// In the future we might block things over 599 (600 and above aren't defined
|
// In the future we might block things over 599 (600 and above aren't defined
|
||||||
// at http://httpwg.org/specs/rfc7231.html#status.codes)
|
// at http://httpwg.org/specs/rfc7231.html#status.codes).
|
||||||
// and we might block under 200 (once we have more mature 1xx support).
|
|
||||||
// But for now any three digits.
|
// But for now any three digits.
|
||||||
//
|
//
|
||||||
// We used to send "HTTP/1.1 000 0" on the wire in responses but there's
|
// We used to send "HTTP/1.1 000 0" on the wire in responses but there's
|
||||||
|
@ -2665,15 +2724,43 @@ func (w *responseWriter) WriteHeader(code int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rws *responseWriterState) writeHeader(code int) {
|
func (rws *responseWriterState) writeHeader(code int) {
|
||||||
if !rws.wroteHeader {
|
if rws.wroteHeader {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
checkWriteHeaderCode(code)
|
checkWriteHeaderCode(code)
|
||||||
|
|
||||||
|
// Handle informational headers
|
||||||
|
if code >= 100 && code <= 199 {
|
||||||
|
// Per RFC 8297 we must not clear the current header map
|
||||||
|
h := rws.handlerHeader
|
||||||
|
|
||||||
|
_, cl := h["Content-Length"]
|
||||||
|
_, te := h["Transfer-Encoding"]
|
||||||
|
if cl || te {
|
||||||
|
h = h.Clone()
|
||||||
|
h.Del("Content-Length")
|
||||||
|
h.Del("Transfer-Encoding")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rws.conn.writeHeaders(rws.stream, &writeResHeaders{
|
||||||
|
streamID: rws.stream.id,
|
||||||
|
httpResCode: code,
|
||||||
|
h: h,
|
||||||
|
endStream: rws.handlerDone && !rws.hasTrailers(),
|
||||||
|
}) != nil {
|
||||||
|
rws.dirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
rws.wroteHeader = true
|
rws.wroteHeader = true
|
||||||
rws.status = code
|
rws.status = code
|
||||||
if len(rws.handlerHeader) > 0 {
|
if len(rws.handlerHeader) > 0 {
|
||||||
rws.snapHeader = cloneHeader(rws.handlerHeader)
|
rws.snapHeader = cloneHeader(rws.handlerHeader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func cloneHeader(h http.Header) http.Header {
|
func cloneHeader(h http.Header) http.Header {
|
||||||
h2 := make(http.Header, len(h))
|
h2 := make(http.Header, len(h))
|
||||||
|
|
|
@ -16,7 +16,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
mathrand "math/rand"
|
mathrand "math/rand"
|
||||||
|
@ -501,12 +500,14 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
|
||||||
if req, err = shouldRetryRequest(req, err); err == nil {
|
if req, err = shouldRetryRequest(req, err); err == nil {
|
||||||
// After the first retry, do exponential backoff with 10% jitter.
|
// After the first retry, do exponential backoff with 10% jitter.
|
||||||
if retry == 0 {
|
if retry == 0 {
|
||||||
|
t.vlogf("RoundTrip retrying after failure: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
backoff := float64(uint(1) << (uint(retry) - 1))
|
backoff := float64(uint(1) << (uint(retry) - 1))
|
||||||
backoff += backoff * (0.1 * mathrand.Float64())
|
backoff += backoff * (0.1 * mathrand.Float64())
|
||||||
select {
|
select {
|
||||||
case <-time.After(time.Second * time.Duration(backoff)):
|
case <-time.After(time.Second * time.Duration(backoff)):
|
||||||
|
t.vlogf("RoundTrip retrying after failure: %v", err)
|
||||||
continue
|
continue
|
||||||
case <-req.Context().Done():
|
case <-req.Context().Done():
|
||||||
err = req.Context().Err()
|
err = req.Context().Err()
|
||||||
|
@ -732,11 +733,13 @@ func (cc *ClientConn) healthCheck() {
|
||||||
// trigger the healthCheck again if there is no frame received.
|
// trigger the healthCheck again if there is no frame received.
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), pingTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), pingTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
cc.vlogf("http2: Transport sending health check")
|
||||||
err := cc.Ping(ctx)
|
err := cc.Ping(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
cc.vlogf("http2: Transport health check failure: %v", err)
|
||||||
cc.closeForLostPing()
|
cc.closeForLostPing()
|
||||||
cc.t.connPool().MarkDead(cc)
|
} else {
|
||||||
return
|
cc.vlogf("http2: Transport health check success")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -907,6 +910,24 @@ func (cc *ClientConn) onIdleTimeout() {
|
||||||
cc.closeIfIdle()
|
cc.closeIfIdle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cc *ClientConn) closeConn() error {
|
||||||
|
t := time.AfterFunc(250*time.Millisecond, cc.forceCloseConn)
|
||||||
|
defer t.Stop()
|
||||||
|
return cc.tconn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A tls.Conn.Close can hang for a long time if the peer is unresponsive.
|
||||||
|
// Try to shut it down more aggressively.
|
||||||
|
func (cc *ClientConn) forceCloseConn() {
|
||||||
|
tc, ok := cc.tconn.(*tls.Conn)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if nc := tlsUnderlyingConn(tc); nc != nil {
|
||||||
|
nc.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (cc *ClientConn) closeIfIdle() {
|
func (cc *ClientConn) closeIfIdle() {
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
if len(cc.streams) > 0 || cc.streamsReserved > 0 {
|
if len(cc.streams) > 0 || cc.streamsReserved > 0 {
|
||||||
|
@ -921,7 +942,7 @@ func (cc *ClientConn) closeIfIdle() {
|
||||||
if VerboseLogs {
|
if VerboseLogs {
|
||||||
cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, nextID-2)
|
cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, nextID-2)
|
||||||
}
|
}
|
||||||
cc.tconn.Close()
|
cc.closeConn()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc *ClientConn) isDoNotReuseAndIdle() bool {
|
func (cc *ClientConn) isDoNotReuseAndIdle() bool {
|
||||||
|
@ -938,7 +959,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Wait for all in-flight streams to complete or connection to close
|
// Wait for all in-flight streams to complete or connection to close
|
||||||
done := make(chan error, 1)
|
done := make(chan struct{})
|
||||||
cancelled := false // guarded by cc.mu
|
cancelled := false // guarded by cc.mu
|
||||||
go func() {
|
go func() {
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
|
@ -946,7 +967,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
|
||||||
for {
|
for {
|
||||||
if len(cc.streams) == 0 || cc.closed {
|
if len(cc.streams) == 0 || cc.closed {
|
||||||
cc.closed = true
|
cc.closed = true
|
||||||
done <- cc.tconn.Close()
|
close(done)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if cancelled {
|
if cancelled {
|
||||||
|
@ -957,8 +978,8 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
|
||||||
}()
|
}()
|
||||||
shutdownEnterWaitStateHook()
|
shutdownEnterWaitStateHook()
|
||||||
select {
|
select {
|
||||||
case err := <-done:
|
case <-done:
|
||||||
return err
|
return cc.closeConn()
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
// Free the goroutine above
|
// Free the goroutine above
|
||||||
|
@ -1001,9 +1022,9 @@ func (cc *ClientConn) closeForError(err error) error {
|
||||||
for _, cs := range cc.streams {
|
for _, cs := range cc.streams {
|
||||||
cs.abortStreamLocked(err)
|
cs.abortStreamLocked(err)
|
||||||
}
|
}
|
||||||
defer cc.cond.Broadcast()
|
cc.cond.Broadcast()
|
||||||
defer cc.mu.Unlock()
|
cc.mu.Unlock()
|
||||||
return cc.tconn.Close()
|
return cc.closeConn()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the client connection immediately.
|
// Close closes the client connection immediately.
|
||||||
|
@ -1748,7 +1769,8 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
||||||
}
|
}
|
||||||
for _, v := range vv {
|
for _, v := range vv {
|
||||||
if !httpguts.ValidHeaderFieldValue(v) {
|
if !httpguts.ValidHeaderFieldValue(v) {
|
||||||
return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k)
|
// Don't include the value in the error, because it may be sensitive.
|
||||||
|
return nil, fmt.Errorf("invalid HTTP header value for header %q", k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1978,7 +2000,7 @@ func (cc *ClientConn) forgetStreamID(id uint32) {
|
||||||
cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, cc.nextStreamID-2)
|
cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, cc.nextStreamID-2)
|
||||||
}
|
}
|
||||||
cc.closed = true
|
cc.closed = true
|
||||||
defer cc.tconn.Close()
|
defer cc.closeConn()
|
||||||
}
|
}
|
||||||
|
|
||||||
cc.mu.Unlock()
|
cc.mu.Unlock()
|
||||||
|
@ -2025,8 +2047,8 @@ func isEOFOrNetReadError(err error) bool {
|
||||||
|
|
||||||
func (rl *clientConnReadLoop) cleanup() {
|
func (rl *clientConnReadLoop) cleanup() {
|
||||||
cc := rl.cc
|
cc := rl.cc
|
||||||
defer cc.tconn.Close()
|
cc.t.connPool().MarkDead(cc)
|
||||||
defer cc.t.connPool().MarkDead(cc)
|
defer cc.closeConn()
|
||||||
defer close(cc.readerDone)
|
defer close(cc.readerDone)
|
||||||
|
|
||||||
if cc.idleTimer != nil {
|
if cc.idleTimer != nil {
|
||||||
|
@ -2881,7 +2903,12 @@ func (t *Transport) logf(format string, args ...interface{}) {
|
||||||
log.Printf(format, args...)
|
log.Printf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
var noBody io.ReadCloser = ioutil.NopCloser(bytes.NewReader(nil))
|
var noBody io.ReadCloser = noBodyReader{}
|
||||||
|
|
||||||
|
type noBodyReader struct{}
|
||||||
|
|
||||||
|
func (noBodyReader) Close() error { return nil }
|
||||||
|
func (noBodyReader) Read([]byte) (int, error) { return 0, io.EOF }
|
||||||
|
|
||||||
type missingBody struct{}
|
type missingBody struct{}
|
||||||
|
|
||||||
|
|
|
@ -383,16 +383,15 @@ func (ws *priorityWriteScheduler) AdjustStream(streamID uint32, priority Priorit
|
||||||
|
|
||||||
func (ws *priorityWriteScheduler) Push(wr FrameWriteRequest) {
|
func (ws *priorityWriteScheduler) Push(wr FrameWriteRequest) {
|
||||||
var n *priorityNode
|
var n *priorityNode
|
||||||
if id := wr.StreamID(); id == 0 {
|
if wr.isControl() {
|
||||||
n = &ws.root
|
n = &ws.root
|
||||||
} else {
|
} else {
|
||||||
|
id := wr.StreamID()
|
||||||
n = ws.nodes[id]
|
n = ws.nodes[id]
|
||||||
if n == nil {
|
if n == nil {
|
||||||
// id is an idle or closed stream. wr should not be a HEADERS or
|
// id is an idle or closed stream. wr should not be a HEADERS or
|
||||||
// DATA frame. However, wr can be a RST_STREAM. In this case, we
|
// DATA frame. In other case, we push wr onto the root, rather
|
||||||
// push wr onto the root, rather than creating a new priorityNode,
|
// than creating a new priorityNode.
|
||||||
// since RST_STREAM is tiny and the stream's priority is unknown
|
|
||||||
// anyway. See issue #17919.
|
|
||||||
if wr.DataSize() > 0 {
|
if wr.DataSize() > 0 {
|
||||||
panic("add DATA on non-open stream")
|
panic("add DATA on non-open stream")
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
||||||
|
|
|
@ -4,22 +4,8 @@
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
||||||
import "syscall"
|
|
||||||
|
|
||||||
func (h *cmsghdr) set(l, lvl, typ int) {
|
func (h *cmsghdr) set(l, lvl, typ int) {
|
||||||
h.Len = int32(l)
|
h.Len = int32(l)
|
||||||
h.Level = int32(lvl)
|
h.Level = int32(lvl)
|
||||||
h.Type = int32(typ)
|
h.Type = int32(typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
func controlHeaderLen() int {
|
|
||||||
return syscall.CmsgLen(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func controlMessageLen(dataLen int) int {
|
|
||||||
return syscall.CmsgLen(dataLen)
|
|
||||||
}
|
|
||||||
|
|
||||||
func controlMessageSpace(dataLen int) int {
|
|
||||||
return syscall.CmsgSpace(dataLen)
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,7 +9,9 @@ package socket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mmsghdrs []mmsghdr
|
type mmsghdrs []mmsghdr
|
||||||
|
@ -93,22 +95,86 @@ func (p *mmsghdrsPacker) pack(ms []Message, parseFn func([]byte, string) (net.Ad
|
||||||
return hs
|
return hs
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultMmsghdrsPool = mmsghdrsPool{
|
// syscaller is a helper to invoke recvmmsg and sendmmsg via the RawConn.Read/Write interface.
|
||||||
|
// It is reusable, to amortize the overhead of allocating a closure for the function passed to
|
||||||
|
// RawConn.Read/Write.
|
||||||
|
type syscaller struct {
|
||||||
|
n int
|
||||||
|
operr error
|
||||||
|
hs mmsghdrs
|
||||||
|
flags int
|
||||||
|
|
||||||
|
boundRecvmmsgF func(uintptr) bool
|
||||||
|
boundSendmmsgF func(uintptr) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *syscaller) init() {
|
||||||
|
r.boundRecvmmsgF = r.recvmmsgF
|
||||||
|
r.boundSendmmsgF = r.sendmmsgF
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *syscaller) recvmmsg(c syscall.RawConn, hs mmsghdrs, flags int) (int, error) {
|
||||||
|
r.n = 0
|
||||||
|
r.operr = nil
|
||||||
|
r.hs = hs
|
||||||
|
r.flags = flags
|
||||||
|
if err := c.Read(r.boundRecvmmsgF); err != nil {
|
||||||
|
return r.n, err
|
||||||
|
}
|
||||||
|
if r.operr != nil {
|
||||||
|
return r.n, os.NewSyscallError("recvmmsg", r.operr)
|
||||||
|
}
|
||||||
|
return r.n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *syscaller) recvmmsgF(s uintptr) bool {
|
||||||
|
r.n, r.operr = recvmmsg(s, r.hs, r.flags)
|
||||||
|
return ioComplete(r.flags, r.operr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *syscaller) sendmmsg(c syscall.RawConn, hs mmsghdrs, flags int) (int, error) {
|
||||||
|
r.n = 0
|
||||||
|
r.operr = nil
|
||||||
|
r.hs = hs
|
||||||
|
r.flags = flags
|
||||||
|
if err := c.Write(r.boundSendmmsgF); err != nil {
|
||||||
|
return r.n, err
|
||||||
|
}
|
||||||
|
if r.operr != nil {
|
||||||
|
return r.n, os.NewSyscallError("sendmmsg", r.operr)
|
||||||
|
}
|
||||||
|
return r.n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *syscaller) sendmmsgF(s uintptr) bool {
|
||||||
|
r.n, r.operr = sendmmsg(s, r.hs, r.flags)
|
||||||
|
return ioComplete(r.flags, r.operr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mmsgTmps holds reusable temporary helpers for recvmmsg and sendmmsg.
|
||||||
|
type mmsgTmps struct {
|
||||||
|
packer mmsghdrsPacker
|
||||||
|
syscaller syscaller
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultMmsgTmpsPool = mmsgTmpsPool{
|
||||||
p: sync.Pool{
|
p: sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() interface{} {
|
||||||
return new(mmsghdrsPacker)
|
tmps := new(mmsgTmps)
|
||||||
|
tmps.syscaller.init()
|
||||||
|
return tmps
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
type mmsghdrsPool struct {
|
type mmsgTmpsPool struct {
|
||||||
p sync.Pool
|
p sync.Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *mmsghdrsPool) Get() *mmsghdrsPacker {
|
func (p *mmsgTmpsPool) Get() *mmsgTmps {
|
||||||
return p.p.Get().(*mmsghdrsPacker)
|
return p.p.Get().(*mmsgTmps)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *mmsghdrsPool) Put(packer *mmsghdrsPacker) {
|
func (p *mmsgTmpsPool) Put(tmps *mmsgTmps) {
|
||||||
p.p.Put(packer)
|
p.p.Put(tmps)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,32 +9,23 @@ package socket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
|
func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
|
||||||
for i := range ms {
|
for i := range ms {
|
||||||
ms[i].raceWrite()
|
ms[i].raceWrite()
|
||||||
}
|
}
|
||||||
packer := defaultMmsghdrsPool.Get()
|
tmps := defaultMmsgTmpsPool.Get()
|
||||||
defer defaultMmsghdrsPool.Put(packer)
|
defer defaultMmsgTmpsPool.Put(tmps)
|
||||||
var parseFn func([]byte, string) (net.Addr, error)
|
var parseFn func([]byte, string) (net.Addr, error)
|
||||||
if c.network != "tcp" {
|
if c.network != "tcp" {
|
||||||
parseFn = parseInetAddr
|
parseFn = parseInetAddr
|
||||||
}
|
}
|
||||||
hs := packer.pack(ms, parseFn, nil)
|
hs := tmps.packer.pack(ms, parseFn, nil)
|
||||||
var operr error
|
n, err := tmps.syscaller.recvmmsg(c.c, hs, flags)
|
||||||
var n int
|
if err != nil {
|
||||||
fn := func(s uintptr) bool {
|
|
||||||
n, operr = recvmmsg(s, hs, flags)
|
|
||||||
return ioComplete(flags, operr)
|
|
||||||
}
|
|
||||||
if err := c.c.Read(fn); err != nil {
|
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
if operr != nil {
|
|
||||||
return n, os.NewSyscallError("recvmmsg", operr)
|
|
||||||
}
|
|
||||||
if err := hs[:n].unpack(ms[:n], parseFn, c.network); err != nil {
|
if err := hs[:n].unpack(ms[:n], parseFn, c.network); err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
@ -45,25 +36,17 @@ func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) {
|
||||||
for i := range ms {
|
for i := range ms {
|
||||||
ms[i].raceRead()
|
ms[i].raceRead()
|
||||||
}
|
}
|
||||||
packer := defaultMmsghdrsPool.Get()
|
tmps := defaultMmsgTmpsPool.Get()
|
||||||
defer defaultMmsghdrsPool.Put(packer)
|
defer defaultMmsgTmpsPool.Put(tmps)
|
||||||
var marshalFn func(net.Addr, []byte) int
|
var marshalFn func(net.Addr, []byte) int
|
||||||
if c.network != "tcp" {
|
if c.network != "tcp" {
|
||||||
marshalFn = marshalInetAddr
|
marshalFn = marshalInetAddr
|
||||||
}
|
}
|
||||||
hs := packer.pack(ms, nil, marshalFn)
|
hs := tmps.packer.pack(ms, nil, marshalFn)
|
||||||
var operr error
|
n, err := tmps.syscaller.sendmmsg(c.c, hs, flags)
|
||||||
var n int
|
if err != nil {
|
||||||
fn := func(s uintptr) bool {
|
|
||||||
n, operr = sendmmsg(s, hs, flags)
|
|
||||||
return ioComplete(flags, operr)
|
|
||||||
}
|
|
||||||
if err := c.c.Write(fn); err != nil {
|
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
if operr != nil {
|
|
||||||
return n, os.NewSyscallError("sendmmsg", operr)
|
|
||||||
}
|
|
||||||
if err := hs[:n].unpack(ms[:n], nil, ""); err != nil {
|
if err := hs[:n].unpack(ms[:n], nil, ""); err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
// 3376.
|
// 3376.
|
||||||
// Source-specific multicast is defined in RFC 4607.
|
// Source-specific multicast is defined in RFC 4607.
|
||||||
//
|
//
|
||||||
//
|
// # Unicasting
|
||||||
// Unicasting
|
|
||||||
//
|
//
|
||||||
// The options for unicasting are available for net.TCPConn,
|
// The options for unicasting are available for net.TCPConn,
|
||||||
// net.UDPConn and net.IPConn which are created as network connections
|
// net.UDPConn and net.IPConn which are created as network connections
|
||||||
|
@ -51,8 +50,7 @@
|
||||||
// }(c)
|
// }(c)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
//
|
// # Multicasting
|
||||||
// Multicasting
|
|
||||||
//
|
//
|
||||||
// The options for multicasting are available for net.UDPConn and
|
// The options for multicasting are available for net.UDPConn and
|
||||||
// net.IPConn which are created as network connections that use the
|
// net.IPConn which are created as network connections that use the
|
||||||
|
@ -141,8 +139,7 @@
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
//
|
// # More multicasting
|
||||||
// More multicasting
|
|
||||||
//
|
//
|
||||||
// An application that uses PacketConn or RawConn may join multiple
|
// An application that uses PacketConn or RawConn may join multiple
|
||||||
// multicast groups. For example, a UDP listener with port 1024 might
|
// multicast groups. For example, a UDP listener with port 1024 might
|
||||||
|
@ -200,8 +197,7 @@
|
||||||
// // error handling
|
// // error handling
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
//
|
// # Source-specific multicasting
|
||||||
// Source-specific multicasting
|
|
||||||
//
|
//
|
||||||
// An application that uses PacketConn or RawConn on IGMPv3 supported
|
// An application that uses PacketConn or RawConn on IGMPv3 supported
|
||||||
// platform is able to join source-specific multicast groups.
|
// platform is able to join source-specific multicast groups.
|
||||||
|
|
|
@ -17,8 +17,7 @@
|
||||||
// On Darwin, this package requires OS X Mavericks version 10.9 or
|
// On Darwin, this package requires OS X Mavericks version 10.9 or
|
||||||
// above, or equivalent.
|
// above, or equivalent.
|
||||||
//
|
//
|
||||||
//
|
// # Unicasting
|
||||||
// Unicasting
|
|
||||||
//
|
//
|
||||||
// The options for unicasting are available for net.TCPConn,
|
// The options for unicasting are available for net.TCPConn,
|
||||||
// net.UDPConn and net.IPConn which are created as network connections
|
// net.UDPConn and net.IPConn which are created as network connections
|
||||||
|
@ -52,8 +51,7 @@
|
||||||
// }(c)
|
// }(c)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
//
|
// # Multicasting
|
||||||
// Multicasting
|
|
||||||
//
|
//
|
||||||
// The options for multicasting are available for net.UDPConn and
|
// The options for multicasting are available for net.UDPConn and
|
||||||
// net.IPConn which are created as network connections that use the
|
// net.IPConn which are created as network connections that use the
|
||||||
|
@ -140,8 +138,7 @@
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
//
|
// # More multicasting
|
||||||
// More multicasting
|
|
||||||
//
|
//
|
||||||
// An application that uses PacketConn may join multiple multicast
|
// An application that uses PacketConn may join multiple multicast
|
||||||
// groups. For example, a UDP listener with port 1024 might join two
|
// groups. For example, a UDP listener with port 1024 might join two
|
||||||
|
@ -199,8 +196,7 @@
|
||||||
// // error handling
|
// // error handling
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
//
|
// # Source-specific multicasting
|
||||||
// Source-specific multicasting
|
|
||||||
//
|
//
|
||||||
// An application that uses PacketConn on MLDv2 supported platform is
|
// An application that uses PacketConn on MLDv2 supported platform is
|
||||||
// able to join source-specific multicast groups.
|
// able to join source-specific multicast groups.
|
||||||
|
|
|
@ -416,7 +416,6 @@ Trivial usage:
|
||||||
// send binary frame
|
// send binary frame
|
||||||
data = []byte{0, 1, 2}
|
data = []byte{0, 1, 2}
|
||||||
websocket.Message.Send(ws, data)
|
websocket.Message.Send(ws, data)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
var Message = Codec{marshal, unmarshal}
|
var Message = Codec{marshal, unmarshal}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ func hostByteOrder() byteOrder {
|
||||||
case "386", "amd64", "amd64p32",
|
case "386", "amd64", "amd64p32",
|
||||||
"alpha",
|
"alpha",
|
||||||
"arm", "arm64",
|
"arm", "arm64",
|
||||||
|
"loong64",
|
||||||
"mipsle", "mips64le", "mips64p32le",
|
"mipsle", "mips64le", "mips64p32le",
|
||||||
"nios2",
|
"nios2",
|
||||||
"ppc64le",
|
"ppc64le",
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
// 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 loong64
|
||||||
|
// +build loong64
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
const cacheLineSize = 64
|
||||||
|
|
||||||
|
func initOptions() {
|
||||||
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
// Recreate a getsystemcfg syscall handler instead of
|
// Recreate a getsystemcfg syscall handler instead of
|
||||||
// using the one provided by x/sys/unix to avoid having
|
// using the one provided by x/sys/unix to avoid having
|
||||||
// the dependency between them. (See golang.org/issue/32102)
|
// the dependency between them. (See golang.org/issue/32102)
|
||||||
// Morever, this file will be used during the building of
|
// Moreover, this file will be used during the building of
|
||||||
// gccgo's libgo and thus must not used a CGo method.
|
// gccgo's libgo and thus must not used a CGo method.
|
||||||
|
|
||||||
//go:build aix && gccgo
|
//go:build aix && gccgo
|
||||||
|
|
|
@ -53,7 +53,7 @@ func relError(file, path string) error {
|
||||||
// LookPath instead returns an error.
|
// LookPath instead returns an error.
|
||||||
func LookPath(file string) (string, error) {
|
func LookPath(file string) (string, error) {
|
||||||
path, err := exec.LookPath(file)
|
path, err := exec.LookPath(file)
|
||||||
if err != nil {
|
if err != nil && !isGo119ErrDot(err) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if filepath.Base(file) == file && !filepath.IsAbs(path) {
|
if filepath.Base(file) == file && !filepath.IsAbs(path) {
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// 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 !go1.19
|
||||||
|
// +build !go1.19
|
||||||
|
|
||||||
|
package execabs
|
||||||
|
|
||||||
|
func isGo119ErrDot(err error) bool {
|
||||||
|
return false
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
// 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 go1.19
|
||||||
|
// +build go1.19
|
||||||
|
|
||||||
|
package execabs
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func isGo119ErrDot(err error) bool {
|
||||||
|
// TODO: return errors.Is(err, exec.ErrDot)
|
||||||
|
return strings.Contains(err.Error(), "current directory")
|
||||||
|
}
|
|
@ -113,5 +113,6 @@ func (tv *Timeval) Nano() int64 {
|
||||||
|
|
||||||
// use is a no-op, but the compiler cannot see that it is.
|
// use is a no-op, but the compiler cannot see that it is.
|
||||||
// Calling use(p) ensures that p is kept live until that point.
|
// Calling use(p) ensures that p is kept live until that point.
|
||||||
|
//
|
||||||
//go:noescape
|
//go:noescape
|
||||||
func use(p unsafe.Pointer)
|
func use(p unsafe.Pointer)
|
||||||
|
|
|
@ -115,6 +115,7 @@ func Write(fd int, p []byte) (n int, err error) {
|
||||||
var ioSync int64
|
var ioSync int64
|
||||||
|
|
||||||
//sys fd2path(fd int, buf []byte) (err error)
|
//sys fd2path(fd int, buf []byte) (err error)
|
||||||
|
|
||||||
func Fd2path(fd int) (path string, err error) {
|
func Fd2path(fd int) (path string, err error) {
|
||||||
var buf [512]byte
|
var buf [512]byte
|
||||||
|
|
||||||
|
@ -126,6 +127,7 @@ func Fd2path(fd int) (path string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys pipe(p *[2]int32) (err error)
|
//sys pipe(p *[2]int32) (err error)
|
||||||
|
|
||||||
func Pipe(p []int) (err error) {
|
func Pipe(p []int) (err error) {
|
||||||
if len(p) != 2 {
|
if len(p) != 2 {
|
||||||
return syscall.ErrorString("bad arg in system call")
|
return syscall.ErrorString("bad arg in system call")
|
||||||
|
@ -180,6 +182,7 @@ func (w Waitmsg) ExitStatus() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys await(s []byte) (n int, err error)
|
//sys await(s []byte) (n int, err error)
|
||||||
|
|
||||||
func Await(w *Waitmsg) (err error) {
|
func Await(w *Waitmsg) (err error) {
|
||||||
var buf [512]byte
|
var buf [512]byte
|
||||||
var f [5][]byte
|
var f [5][]byte
|
||||||
|
@ -301,42 +304,49 @@ func Getgroups() (gids []int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys open(path string, mode int) (fd int, err error)
|
//sys open(path string, mode int) (fd int, err error)
|
||||||
|
|
||||||
func Open(path string, mode int) (fd int, err error) {
|
func Open(path string, mode int) (fd int, err error) {
|
||||||
fixwd()
|
fixwd()
|
||||||
return open(path, mode)
|
return open(path, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys create(path string, mode int, perm uint32) (fd int, err error)
|
//sys create(path string, mode int, perm uint32) (fd int, err error)
|
||||||
|
|
||||||
func Create(path string, mode int, perm uint32) (fd int, err error) {
|
func Create(path string, mode int, perm uint32) (fd int, err error) {
|
||||||
fixwd()
|
fixwd()
|
||||||
return create(path, mode, perm)
|
return create(path, mode, perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys remove(path string) (err error)
|
//sys remove(path string) (err error)
|
||||||
|
|
||||||
func Remove(path string) error {
|
func Remove(path string) error {
|
||||||
fixwd()
|
fixwd()
|
||||||
return remove(path)
|
return remove(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys stat(path string, edir []byte) (n int, err error)
|
//sys stat(path string, edir []byte) (n int, err error)
|
||||||
|
|
||||||
func Stat(path string, edir []byte) (n int, err error) {
|
func Stat(path string, edir []byte) (n int, err error) {
|
||||||
fixwd()
|
fixwd()
|
||||||
return stat(path, edir)
|
return stat(path, edir)
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys bind(name string, old string, flag int) (err error)
|
//sys bind(name string, old string, flag int) (err error)
|
||||||
|
|
||||||
func Bind(name string, old string, flag int) (err error) {
|
func Bind(name string, old string, flag int) (err error) {
|
||||||
fixwd()
|
fixwd()
|
||||||
return bind(name, old, flag)
|
return bind(name, old, flag)
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys mount(fd int, afd int, old string, flag int, aname string) (err error)
|
//sys mount(fd int, afd int, old string, flag int, aname string) (err error)
|
||||||
|
|
||||||
func Mount(fd int, afd int, old string, flag int, aname string) (err error) {
|
func Mount(fd int, afd int, old string, flag int, aname string) (err error) {
|
||||||
fixwd()
|
fixwd()
|
||||||
return mount(fd, afd, old, flag, aname)
|
return mount(fd, afd, old, flag, aname)
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys wstat(path string, edir []byte) (err error)
|
//sys wstat(path string, edir []byte) (err error)
|
||||||
|
|
||||||
func Wstat(path string, edir []byte) (err error) {
|
func Wstat(path string, edir []byte) (err error) {
|
||||||
fixwd()
|
fixwd()
|
||||||
return wstat(path, edir)
|
return wstat(path, edir)
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
// 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 linux && loong64 && gc
|
||||||
|
// +build linux
|
||||||
|
// +build loong64
|
||||||
|
// +build gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
JAL runtime·entersyscall(SB)
|
||||||
|
MOVV a1+8(FP), R4
|
||||||
|
MOVV a2+16(FP), R5
|
||||||
|
MOVV a3+24(FP), R6
|
||||||
|
MOVV R0, R7
|
||||||
|
MOVV R0, R8
|
||||||
|
MOVV R0, R9
|
||||||
|
MOVV trap+0(FP), R11 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVV R4, r1+32(FP)
|
||||||
|
MOVV R5, r2+40(FP)
|
||||||
|
JAL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
MOVV a1+8(FP), R4
|
||||||
|
MOVV a2+16(FP), R5
|
||||||
|
MOVV a3+24(FP), R6
|
||||||
|
MOVV R0, R7
|
||||||
|
MOVV R0, R8
|
||||||
|
MOVV R0, R9
|
||||||
|
MOVV trap+0(FP), R11 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVV R4, r1+32(FP)
|
||||||
|
MOVV R5, r2+40(FP)
|
||||||
|
RET
|
|
@ -2,8 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
//
|
//
|
||||||
//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh
|
//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh
|
||||||
// +build 386 amd64 amd64p32 alpha arm arm64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh
|
// +build 386 amd64 amd64p32 alpha arm arm64 loong64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -45,13 +44,7 @@ func NewIfreq(name string) (*Ifreq, error) {
|
||||||
|
|
||||||
// Name returns the interface name associated with the Ifreq.
|
// Name returns the interface name associated with the Ifreq.
|
||||||
func (ifr *Ifreq) Name() string {
|
func (ifr *Ifreq) Name() string {
|
||||||
// BytePtrToString requires a NULL terminator or the program may crash. If
|
return ByteSliceToString(ifr.raw.Ifrn[:])
|
||||||
// one is not present, just return the empty string.
|
|
||||||
if !bytes.Contains(ifr.raw.Ifrn[:], []byte{0x00}) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return BytePtrToString(&ifr.raw.Ifrn[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// According to netdevice(7), only AF_INET addresses are returned for numerous
|
// According to netdevice(7), only AF_INET addresses are returned for numerous
|
||||||
|
|
|
@ -194,3 +194,26 @@ func ioctlIfreqData(fd int, req uint, value *ifreqData) error {
|
||||||
// identical so pass *IfreqData directly.
|
// identical so pass *IfreqData directly.
|
||||||
return ioctlPtr(fd, req, unsafe.Pointer(value))
|
return ioctlPtr(fd, req, unsafe.Pointer(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IoctlKCMClone attaches a new file descriptor to a multiplexor by cloning an
|
||||||
|
// existing KCM socket, returning a structure containing the file descriptor of
|
||||||
|
// the new socket.
|
||||||
|
func IoctlKCMClone(fd int) (*KCMClone, error) {
|
||||||
|
var info KCMClone
|
||||||
|
if err := ioctlPtr(fd, SIOCKCMCLONE, unsafe.Pointer(&info)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IoctlKCMAttach attaches a TCP socket and associated BPF program file
|
||||||
|
// descriptor to a multiplexor.
|
||||||
|
func IoctlKCMAttach(fd int, info KCMAttach) error {
|
||||||
|
return ioctlPtr(fd, SIOCKCMATTACH, unsafe.Pointer(&info))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IoctlKCMUnattach unattaches a TCP socket file descriptor from a multiplexor.
|
||||||
|
func IoctlKCMUnattach(fd int, info KCMUnattach) error {
|
||||||
|
return ioctlPtr(fd, SIOCKCMUNATTACH, unsafe.Pointer(&info))
|
||||||
|
}
|
||||||
|
|
|
@ -205,6 +205,7 @@ struct ltchars {
|
||||||
#include <linux/bpf.h>
|
#include <linux/bpf.h>
|
||||||
#include <linux/can.h>
|
#include <linux/can.h>
|
||||||
#include <linux/can/error.h>
|
#include <linux/can/error.h>
|
||||||
|
#include <linux/can/netlink.h>
|
||||||
#include <linux/can/raw.h>
|
#include <linux/can/raw.h>
|
||||||
#include <linux/capability.h>
|
#include <linux/capability.h>
|
||||||
#include <linux/cryptouser.h>
|
#include <linux/cryptouser.h>
|
||||||
|
@ -214,6 +215,7 @@ struct ltchars {
|
||||||
#include <linux/ethtool_netlink.h>
|
#include <linux/ethtool_netlink.h>
|
||||||
#include <linux/falloc.h>
|
#include <linux/falloc.h>
|
||||||
#include <linux/fanotify.h>
|
#include <linux/fanotify.h>
|
||||||
|
#include <linux/fib_rules.h>
|
||||||
#include <linux/filter.h>
|
#include <linux/filter.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/fscrypt.h>
|
#include <linux/fscrypt.h>
|
||||||
|
@ -231,6 +233,7 @@ struct ltchars {
|
||||||
#include <linux/if_packet.h>
|
#include <linux/if_packet.h>
|
||||||
#include <linux/if_xdp.h>
|
#include <linux/if_xdp.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
#include <linux/kcm.h>
|
||||||
#include <linux/kexec.h>
|
#include <linux/kexec.h>
|
||||||
#include <linux/keyctl.h>
|
#include <linux/keyctl.h>
|
||||||
#include <linux/landlock.h>
|
#include <linux/landlock.h>
|
||||||
|
@ -503,6 +506,7 @@ ccflags="$@"
|
||||||
$2 ~ /^O?XTABS$/ ||
|
$2 ~ /^O?XTABS$/ ||
|
||||||
$2 ~ /^TC[IO](ON|OFF)$/ ||
|
$2 ~ /^TC[IO](ON|OFF)$/ ||
|
||||||
$2 ~ /^IN_/ ||
|
$2 ~ /^IN_/ ||
|
||||||
|
$2 ~ /^KCM/ ||
|
||||||
$2 ~ /^LANDLOCK_/ ||
|
$2 ~ /^LANDLOCK_/ ||
|
||||||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
|
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
|
||||||
$2 ~ /^LO_(KEY|NAME)_SIZE$/ ||
|
$2 ~ /^LO_(KEY|NAME)_SIZE$/ ||
|
||||||
|
@ -597,8 +601,10 @@ ccflags="$@"
|
||||||
$2 ~ /^DEVLINK_/ ||
|
$2 ~ /^DEVLINK_/ ||
|
||||||
$2 ~ /^ETHTOOL_/ ||
|
$2 ~ /^ETHTOOL_/ ||
|
||||||
$2 ~ /^LWTUNNEL_IP/ ||
|
$2 ~ /^LWTUNNEL_IP/ ||
|
||||||
|
$2 ~ /^ITIMER_/ ||
|
||||||
$2 !~ "WMESGLEN" &&
|
$2 !~ "WMESGLEN" &&
|
||||||
$2 ~ /^W[A-Z0-9]+$/ ||
|
$2 ~ /^W[A-Z0-9]+$/ ||
|
||||||
|
$2 ~ /^P_/ ||
|
||||||
$2 ~/^PPPIOC/ ||
|
$2 ~/^PPPIOC/ ||
|
||||||
$2 ~ /^FAN_|FANOTIFY_/ ||
|
$2 ~ /^FAN_|FANOTIFY_/ ||
|
||||||
$2 == "HID_MAX_DESCRIPTOR_SIZE" ||
|
$2 == "HID_MAX_DESCRIPTOR_SIZE" ||
|
||||||
|
@ -608,6 +614,7 @@ ccflags="$@"
|
||||||
$2 ~ /^OTP/ ||
|
$2 ~ /^OTP/ ||
|
||||||
$2 ~ /^MEM/ ||
|
$2 ~ /^MEM/ ||
|
||||||
$2 ~ /^WG/ ||
|
$2 ~ /^WG/ ||
|
||||||
|
$2 ~ /^FIB_RULE_/ ||
|
||||||
$2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)}
|
$2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)}
|
||||||
$2 ~ /^__WCOREFLAG$/ {next}
|
$2 ~ /^__WCOREFLAG$/ {next}
|
||||||
$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}
|
$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}
|
||||||
|
|
|
@ -37,6 +37,7 @@ func Creat(path string, mode uint32) (fd int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys utimes(path string, times *[2]Timeval) (err error)
|
//sys utimes(path string, times *[2]Timeval) (err error)
|
||||||
|
|
||||||
func Utimes(path string, tv []Timeval) error {
|
func Utimes(path string, tv []Timeval) error {
|
||||||
if len(tv) != 2 {
|
if len(tv) != 2 {
|
||||||
return EINVAL
|
return EINVAL
|
||||||
|
@ -45,6 +46,7 @@ func Utimes(path string, tv []Timeval) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys utimensat(dirfd int, path string, times *[2]Timespec, flag int) (err error)
|
//sys utimensat(dirfd int, path string, times *[2]Timespec, flag int) (err error)
|
||||||
|
|
||||||
func UtimesNano(path string, ts []Timespec) error {
|
func UtimesNano(path string, ts []Timespec) error {
|
||||||
if len(ts) != 2 {
|
if len(ts) != 2 {
|
||||||
return EINVAL
|
return EINVAL
|
||||||
|
@ -215,18 +217,12 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
|
||||||
// Recvmsg not implemented on AIX
|
// Recvmsg not implemented on AIX
|
||||||
sa := new(SockaddrUnix)
|
return -1, -1, -1, ENOSYS
|
||||||
return -1, -1, -1, sa, ENOSYS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
|
func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
|
||||||
_, err = SendmsgN(fd, p, oob, to, flags)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
|
||||||
// SendmsgN not implemented on AIX
|
// SendmsgN not implemented on AIX
|
||||||
return -1, ENOSYS
|
return -1, ENOSYS
|
||||||
}
|
}
|
||||||
|
@ -306,11 +302,13 @@ func direntNamlen(buf []byte) (uint64, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys getdirent(fd int, buf []byte) (n int, err error)
|
//sys getdirent(fd int, buf []byte) (n int, err error)
|
||||||
|
|
||||||
func Getdents(fd int, buf []byte) (n int, err error) {
|
func Getdents(fd int, buf []byte) (n int, err error) {
|
||||||
return getdirent(fd, buf)
|
return getdirent(fd, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys wait4(pid Pid_t, status *_C_int, options int, rusage *Rusage) (wpid Pid_t, err error)
|
//sys wait4(pid Pid_t, status *_C_int, options int, rusage *Rusage) (wpid Pid_t, err error)
|
||||||
|
|
||||||
func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) {
|
func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) {
|
||||||
var status _C_int
|
var status _C_int
|
||||||
var r Pid_t
|
var r Pid_t
|
||||||
|
@ -378,6 +376,7 @@ func (w WaitStatus) TrapCause() int { return -1 }
|
||||||
//sys fcntl(fd int, cmd int, arg int) (val int, err error)
|
//sys fcntl(fd int, cmd int, arg int) (val int, err error)
|
||||||
|
|
||||||
//sys fsyncRange(fd int, how int, start int64, length int64) (err error) = fsync_range
|
//sys fsyncRange(fd int, how int, start int64, length int64) (err error) = fsync_range
|
||||||
|
|
||||||
func Fsync(fd int) error {
|
func Fsync(fd int) error {
|
||||||
return fsyncRange(fd, O_SYNC, 0, 0)
|
return fsyncRange(fd, O_SYNC, 0, 0)
|
||||||
}
|
}
|
||||||
|
@ -458,8 +457,8 @@ func Fsync(fd int) error {
|
||||||
//sys Listen(s int, n int) (err error)
|
//sys Listen(s int, n int) (err error)
|
||||||
//sys lstat(path string, stat *Stat_t) (err error)
|
//sys lstat(path string, stat *Stat_t) (err error)
|
||||||
//sys Pause() (err error)
|
//sys Pause() (err error)
|
||||||
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = pread64
|
//sys pread(fd int, p []byte, offset int64) (n int, err error) = pread64
|
||||||
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = pwrite64
|
//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = pwrite64
|
||||||
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error)
|
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error)
|
||||||
//sys Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error)
|
//sys Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error)
|
||||||
//sysnb Setregid(rgid int, egid int) (err error)
|
//sysnb Setregid(rgid int, egid int) (err error)
|
||||||
|
@ -542,6 +541,7 @@ func Poll(fds []PollFd, timeout int) (n int, err error) {
|
||||||
//sys Getsystemcfg(label int) (n uint64)
|
//sys Getsystemcfg(label int) (n uint64)
|
||||||
|
|
||||||
//sys umount(target string) (err error)
|
//sys umount(target string) (err error)
|
||||||
|
|
||||||
func Unmount(target string, flags int) (err error) {
|
func Unmount(target string, flags int) (err error) {
|
||||||
if flags != 0 {
|
if flags != 0 {
|
||||||
// AIX doesn't have any flags for umount.
|
// AIX doesn't have any flags for umount.
|
||||||
|
|
|
@ -325,10 +325,9 @@ func GetsockoptString(fd, level, opt int) (string, error) {
|
||||||
//sys sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error)
|
//sys sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error)
|
||||||
//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error)
|
//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error)
|
||||||
|
|
||||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
|
||||||
var msg Msghdr
|
var msg Msghdr
|
||||||
var rsa RawSockaddrAny
|
msg.Name = (*byte)(unsafe.Pointer(rsa))
|
||||||
msg.Name = (*byte)(unsafe.Pointer(&rsa))
|
|
||||||
msg.Namelen = uint32(SizeofSockaddrAny)
|
msg.Namelen = uint32(SizeofSockaddrAny)
|
||||||
var iov Iovec
|
var iov Iovec
|
||||||
if len(p) > 0 {
|
if len(p) > 0 {
|
||||||
|
@ -352,29 +351,12 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
|
||||||
}
|
}
|
||||||
oobn = int(msg.Controllen)
|
oobn = int(msg.Controllen)
|
||||||
recvflags = int(msg.Flags)
|
recvflags = int(msg.Flags)
|
||||||
// source address is only specified if the socket is unconnected
|
|
||||||
if rsa.Addr.Family != AF_UNSPEC {
|
|
||||||
from, err = anyToSockaddr(fd, &rsa)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
|
//sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
|
||||||
|
|
||||||
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
|
func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
|
||||||
_, err = SendmsgN(fd, p, oob, to, flags)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
|
||||||
var ptr unsafe.Pointer
|
|
||||||
var salen _Socklen
|
|
||||||
if to != nil {
|
|
||||||
ptr, salen, err = to.sockaddr()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var msg Msghdr
|
var msg Msghdr
|
||||||
msg.Name = (*byte)(unsafe.Pointer(ptr))
|
msg.Name = (*byte)(unsafe.Pointer(ptr))
|
||||||
msg.Namelen = uint32(salen)
|
msg.Namelen = uint32(salen)
|
||||||
|
@ -571,12 +553,7 @@ func UtimesNano(path string, ts []Timespec) error {
|
||||||
if len(ts) != 2 {
|
if len(ts) != 2 {
|
||||||
return EINVAL
|
return EINVAL
|
||||||
}
|
}
|
||||||
// Darwin setattrlist can set nanosecond timestamps
|
err := utimensat(AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0)
|
||||||
err := setattrlistTimes(path, ts, 0)
|
|
||||||
if err != ENOSYS {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = utimensat(AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0)
|
|
||||||
if err != ENOSYS {
|
if err != ENOSYS {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -596,10 +573,6 @@ func UtimesNanoAt(dirfd int, path string, ts []Timespec, flags int) error {
|
||||||
if len(ts) != 2 {
|
if len(ts) != 2 {
|
||||||
return EINVAL
|
return EINVAL
|
||||||
}
|
}
|
||||||
err := setattrlistTimes(path, ts, flags)
|
|
||||||
if err != ENOSYS {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return utimensat(dirfd, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), flags)
|
return utimensat(dirfd, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,16 +141,6 @@ func direntNamlen(buf []byte) (uint64, bool) {
|
||||||
func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) }
|
func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) }
|
||||||
func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) }
|
func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) }
|
||||||
|
|
||||||
type attrList struct {
|
|
||||||
bitmapCount uint16
|
|
||||||
_ uint16
|
|
||||||
CommonAttr uint32
|
|
||||||
VolAttr uint32
|
|
||||||
DirAttr uint32
|
|
||||||
FileAttr uint32
|
|
||||||
Forkattr uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
//sysnb pipe(p *[2]int32) (err error)
|
//sysnb pipe(p *[2]int32) (err error)
|
||||||
|
|
||||||
func Pipe(p []int) (err error) {
|
func Pipe(p []int) (err error) {
|
||||||
|
@ -282,36 +272,7 @@ func Flistxattr(fd int, dest []byte) (sz int, err error) {
|
||||||
return flistxattr(fd, xattrPointer(dest), len(dest), 0)
|
return flistxattr(fd, xattrPointer(dest), len(dest), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setattrlistTimes(path string, times []Timespec, flags int) error {
|
//sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error)
|
||||||
_p0, err := BytePtrFromString(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var attrList attrList
|
|
||||||
attrList.bitmapCount = ATTR_BIT_MAP_COUNT
|
|
||||||
attrList.CommonAttr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME
|
|
||||||
|
|
||||||
// order is mtime, atime: the opposite of Chtimes
|
|
||||||
attributes := [2]Timespec{times[1], times[0]}
|
|
||||||
options := 0
|
|
||||||
if flags&AT_SYMLINK_NOFOLLOW != 0 {
|
|
||||||
options |= FSOPT_NOFOLLOW
|
|
||||||
}
|
|
||||||
return setattrlist(
|
|
||||||
_p0,
|
|
||||||
unsafe.Pointer(&attrList),
|
|
||||||
unsafe.Pointer(&attributes),
|
|
||||||
unsafe.Sizeof(attributes),
|
|
||||||
options)
|
|
||||||
}
|
|
||||||
|
|
||||||
//sys setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error)
|
|
||||||
|
|
||||||
func utimensat(dirfd int, path string, times *[2]Timespec, flags int) error {
|
|
||||||
// Darwin doesn't support SYS_UTIMENSAT
|
|
||||||
return ENOSYS
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wrapped
|
* Wrapped
|
||||||
|
@ -543,11 +504,12 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) {
|
||||||
//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
|
//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
|
||||||
//sys Mkfifo(path string, mode uint32) (err error)
|
//sys Mkfifo(path string, mode uint32) (err error)
|
||||||
//sys Mknod(path string, mode uint32, dev int) (err error)
|
//sys Mknod(path string, mode uint32, dev int) (err error)
|
||||||
|
//sys Mount(fsType string, dir string, flags int, data unsafe.Pointer) (err error)
|
||||||
//sys Open(path string, mode int, perm uint32) (fd int, err error)
|
//sys Open(path string, mode int, perm uint32) (fd int, err error)
|
||||||
//sys Openat(dirfd int, path string, mode int, perm uint32) (fd int, err error)
|
//sys Openat(dirfd int, path string, mode int, perm uint32) (fd int, err error)
|
||||||
//sys Pathconf(path string, name int) (val int, err error)
|
//sys Pathconf(path string, name int) (val int, err error)
|
||||||
//sys Pread(fd int, p []byte, offset int64) (n int, err error)
|
//sys pread(fd int, p []byte, offset int64) (n int, err error)
|
||||||
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
|
//sys pwrite(fd int, p []byte, offset int64) (n int, err error)
|
||||||
//sys read(fd int, p []byte) (n int, err error)
|
//sys read(fd int, p []byte) (n int, err error)
|
||||||
//sys Readlink(path string, buf []byte) (n int, err error)
|
//sys Readlink(path string, buf []byte) (n int, err error)
|
||||||
//sys Readlinkat(dirfd int, path string, buf []byte) (n int, err error)
|
//sys Readlinkat(dirfd int, path string, buf []byte) (n int, err error)
|
||||||
|
@ -611,7 +573,6 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) {
|
||||||
// Nfssvc
|
// Nfssvc
|
||||||
// Getfh
|
// Getfh
|
||||||
// Quotactl
|
// Quotactl
|
||||||
// Mount
|
|
||||||
// Csops
|
// Csops
|
||||||
// Waitid
|
// Waitid
|
||||||
// Add_profil
|
// Add_profil
|
||||||
|
|
|
@ -125,12 +125,14 @@ func Pipe2(p []int, flags int) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys extpread(fd int, p []byte, flags int, offset int64) (n int, err error)
|
//sys extpread(fd int, p []byte, flags int, offset int64) (n int, err error)
|
||||||
func Pread(fd int, p []byte, offset int64) (n int, err error) {
|
|
||||||
|
func pread(fd int, p []byte, offset int64) (n int, err error) {
|
||||||
return extpread(fd, p, 0, offset)
|
return extpread(fd, p, 0, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys extpwrite(fd int, p []byte, flags int, offset int64) (n int, err error)
|
//sys extpwrite(fd int, p []byte, flags int, offset int64) (n int, err error)
|
||||||
func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
|
|
||||||
|
func pwrite(fd int, p []byte, offset int64) (n int, err error) {
|
||||||
return extpwrite(fd, p, 0, offset)
|
return extpwrite(fd, p, 0, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,11 +171,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func setattrlistTimes(path string, times []Timespec, flags int) error {
|
|
||||||
// used on Darwin for UtimesNano
|
|
||||||
return ENOSYS
|
|
||||||
}
|
|
||||||
|
|
||||||
//sys ioctl(fd int, req uint, arg uintptr) (err error)
|
//sys ioctl(fd int, req uint, arg uintptr) (err error)
|
||||||
|
|
||||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
|
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
|
||||||
|
|
|
@ -194,11 +194,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func setattrlistTimes(path string, times []Timespec, flags int) error {
|
|
||||||
// used on Darwin for UtimesNano
|
|
||||||
return ENOSYS
|
|
||||||
}
|
|
||||||
|
|
||||||
//sys ioctl(fd int, req uint, arg uintptr) (err error)
|
//sys ioctl(fd int, req uint, arg uintptr) (err error)
|
||||||
|
|
||||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
|
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
|
||||||
|
@ -638,8 +633,8 @@ func PtraceSingleStep(pid int) (err error) {
|
||||||
//sys Open(path string, mode int, perm uint32) (fd int, err error)
|
//sys Open(path string, mode int, perm uint32) (fd int, err error)
|
||||||
//sys Openat(fdat int, path string, mode int, perm uint32) (fd int, err error)
|
//sys Openat(fdat int, path string, mode int, perm uint32) (fd int, err error)
|
||||||
//sys Pathconf(path string, name int) (val int, err error)
|
//sys Pathconf(path string, name int) (val int, err error)
|
||||||
//sys Pread(fd int, p []byte, offset int64) (n int, err error)
|
//sys pread(fd int, p []byte, offset int64) (n int, err error)
|
||||||
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
|
//sys pwrite(fd int, p []byte, offset int64) (n int, err error)
|
||||||
//sys read(fd int, p []byte) (n int, err error)
|
//sys read(fd int, p []byte) (n int, err error)
|
||||||
//sys Readlink(path string, buf []byte) (n int, err error)
|
//sys Readlink(path string, buf []byte) (n int, err error)
|
||||||
//sys Readlinkat(dirfd int, path string, buf []byte) (n int, err error)
|
//sys Readlinkat(dirfd int, path string, buf []byte) (n int, err error)
|
||||||
|
|
|
@ -14,6 +14,7 @@ package unix
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -249,6 +250,13 @@ func Getwd() (wd string, err error) {
|
||||||
if n < 1 || n > len(buf) || buf[n-1] != 0 {
|
if n < 1 || n > len(buf) || buf[n-1] != 0 {
|
||||||
return "", EINVAL
|
return "", EINVAL
|
||||||
}
|
}
|
||||||
|
// In some cases, Linux can return a path that starts with the
|
||||||
|
// "(unreachable)" prefix, which can potentially be a valid relative
|
||||||
|
// path. To work around that, return ENOENT if path is not absolute.
|
||||||
|
if buf[0] != '/' {
|
||||||
|
return "", ENOENT
|
||||||
|
}
|
||||||
|
|
||||||
return string(buf[0 : n-1]), nil
|
return string(buf[0 : n-1]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,6 +366,8 @@ func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//sys Waitid(idType int, id int, info *Siginfo, options int, rusage *Rusage) (err error)
|
||||||
|
|
||||||
func Mkfifo(path string, mode uint32) error {
|
func Mkfifo(path string, mode uint32) error {
|
||||||
return Mknod(path, mode|S_IFIFO, 0)
|
return Mknod(path, mode|S_IFIFO, 0)
|
||||||
}
|
}
|
||||||
|
@ -1489,10 +1499,9 @@ func KeyctlRestrictKeyring(ringid int, keyType string, restriction string) error
|
||||||
//sys keyctlRestrictKeyringByType(cmd int, arg2 int, keyType string, restriction string) (err error) = SYS_KEYCTL
|
//sys keyctlRestrictKeyringByType(cmd int, arg2 int, keyType string, restriction string) (err error) = SYS_KEYCTL
|
||||||
//sys keyctlRestrictKeyring(cmd int, arg2 int) (err error) = SYS_KEYCTL
|
//sys keyctlRestrictKeyring(cmd int, arg2 int) (err error) = SYS_KEYCTL
|
||||||
|
|
||||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
|
||||||
var msg Msghdr
|
var msg Msghdr
|
||||||
var rsa RawSockaddrAny
|
msg.Name = (*byte)(unsafe.Pointer(rsa))
|
||||||
msg.Name = (*byte)(unsafe.Pointer(&rsa))
|
|
||||||
msg.Namelen = uint32(SizeofSockaddrAny)
|
msg.Namelen = uint32(SizeofSockaddrAny)
|
||||||
var iov Iovec
|
var iov Iovec
|
||||||
if len(p) > 0 {
|
if len(p) > 0 {
|
||||||
|
@ -1523,28 +1532,10 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
|
||||||
}
|
}
|
||||||
oobn = int(msg.Controllen)
|
oobn = int(msg.Controllen)
|
||||||
recvflags = int(msg.Flags)
|
recvflags = int(msg.Flags)
|
||||||
// source address is only specified if the socket is unconnected
|
|
||||||
if rsa.Addr.Family != AF_UNSPEC {
|
|
||||||
from, err = anyToSockaddr(fd, &rsa)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
|
func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
|
||||||
_, err = SendmsgN(fd, p, oob, to, flags)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
|
||||||
var ptr unsafe.Pointer
|
|
||||||
var salen _Socklen
|
|
||||||
if to != nil {
|
|
||||||
var err error
|
|
||||||
ptr, salen, err = to.sockaddr()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var msg Msghdr
|
var msg Msghdr
|
||||||
msg.Name = (*byte)(ptr)
|
msg.Name = (*byte)(ptr)
|
||||||
msg.Namelen = uint32(salen)
|
msg.Namelen = uint32(salen)
|
||||||
|
@ -1838,6 +1829,9 @@ func Dup2(oldfd, newfd int) error {
|
||||||
//sys Fremovexattr(fd int, attr string) (err error)
|
//sys Fremovexattr(fd int, attr string) (err error)
|
||||||
//sys Fsetxattr(fd int, attr string, dest []byte, flags int) (err error)
|
//sys Fsetxattr(fd int, attr string, dest []byte, flags int) (err error)
|
||||||
//sys Fsync(fd int) (err error)
|
//sys Fsync(fd int) (err error)
|
||||||
|
//sys Fsmount(fd int, flags int, mountAttrs int) (fsfd int, err error)
|
||||||
|
//sys Fsopen(fsName string, flags int) (fd int, err error)
|
||||||
|
//sys Fspick(dirfd int, pathName string, flags int) (fd int, err error)
|
||||||
//sys Getdents(fd int, buf []byte) (n int, err error) = SYS_GETDENTS64
|
//sys Getdents(fd int, buf []byte) (n int, err error) = SYS_GETDENTS64
|
||||||
//sysnb Getpgid(pid int) (pgid int, err error)
|
//sysnb Getpgid(pid int) (pgid int, err error)
|
||||||
|
|
||||||
|
@ -1868,7 +1862,9 @@ func Getpgrp() (pid int) {
|
||||||
//sys MemfdCreate(name string, flags int) (fd int, err error)
|
//sys MemfdCreate(name string, flags int) (fd int, err error)
|
||||||
//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
|
//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
|
||||||
//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error)
|
//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error)
|
||||||
|
//sys MoveMount(fromDirfd int, fromPathName string, toDirfd int, toPathName string, flags int) (err error)
|
||||||
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
|
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
|
||||||
|
//sys OpenTree(dfd int, fileName string, flags uint) (r int, err error)
|
||||||
//sys PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error)
|
//sys PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error)
|
||||||
//sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT
|
//sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT
|
||||||
//sysnb Prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) = SYS_PRLIMIT64
|
//sysnb Prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) = SYS_PRLIMIT64
|
||||||
|
@ -2193,7 +2189,7 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
|
||||||
gid = Getgid()
|
gid = Getgid()
|
||||||
}
|
}
|
||||||
|
|
||||||
if uint32(gid) == st.Gid || isGroupMember(gid) {
|
if uint32(gid) == st.Gid || isGroupMember(int(st.Gid)) {
|
||||||
fmode = (st.Mode >> 3) & 7
|
fmode = (st.Mode >> 3) & 7
|
||||||
} else {
|
} else {
|
||||||
fmode = st.Mode & 7
|
fmode = st.Mode & 7
|
||||||
|
@ -2308,17 +2304,63 @@ type RemoteIovec struct {
|
||||||
|
|
||||||
//sys PidfdOpen(pid int, flags int) (fd int, err error) = SYS_PIDFD_OPEN
|
//sys PidfdOpen(pid int, flags int) (fd int, err error) = SYS_PIDFD_OPEN
|
||||||
//sys PidfdGetfd(pidfd int, targetfd int, flags int) (fd int, err error) = SYS_PIDFD_GETFD
|
//sys PidfdGetfd(pidfd int, targetfd int, flags int) (fd int, err error) = SYS_PIDFD_GETFD
|
||||||
|
//sys PidfdSendSignal(pidfd int, sig Signal, info *Siginfo, flags int) (err error) = SYS_PIDFD_SEND_SIGNAL
|
||||||
|
|
||||||
//sys shmat(id int, addr uintptr, flag int) (ret uintptr, err error)
|
//sys shmat(id int, addr uintptr, flag int) (ret uintptr, err error)
|
||||||
//sys shmctl(id int, cmd int, buf *SysvShmDesc) (result int, err error)
|
//sys shmctl(id int, cmd int, buf *SysvShmDesc) (result int, err error)
|
||||||
//sys shmdt(addr uintptr) (err error)
|
//sys shmdt(addr uintptr) (err error)
|
||||||
//sys shmget(key int, size int, flag int) (id int, err error)
|
//sys shmget(key int, size int, flag int) (id int, err error)
|
||||||
|
|
||||||
|
//sys getitimer(which int, currValue *Itimerval) (err error)
|
||||||
|
//sys setitimer(which int, newValue *Itimerval, oldValue *Itimerval) (err error)
|
||||||
|
|
||||||
|
// MakeItimerval creates an Itimerval from interval and value durations.
|
||||||
|
func MakeItimerval(interval, value time.Duration) Itimerval {
|
||||||
|
return Itimerval{
|
||||||
|
Interval: NsecToTimeval(interval.Nanoseconds()),
|
||||||
|
Value: NsecToTimeval(value.Nanoseconds()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A value which may be passed to the which parameter for Getitimer and
|
||||||
|
// Setitimer.
|
||||||
|
type ItimerWhich int
|
||||||
|
|
||||||
|
// Possible which values for Getitimer and Setitimer.
|
||||||
|
const (
|
||||||
|
ItimerReal ItimerWhich = ITIMER_REAL
|
||||||
|
ItimerVirtual ItimerWhich = ITIMER_VIRTUAL
|
||||||
|
ItimerProf ItimerWhich = ITIMER_PROF
|
||||||
|
)
|
||||||
|
|
||||||
|
// Getitimer wraps getitimer(2) to return the current value of the timer
|
||||||
|
// specified by which.
|
||||||
|
func Getitimer(which ItimerWhich) (Itimerval, error) {
|
||||||
|
var it Itimerval
|
||||||
|
if err := getitimer(int(which), &it); err != nil {
|
||||||
|
return Itimerval{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return it, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setitimer wraps setitimer(2) to arm or disarm the timer specified by which.
|
||||||
|
// It returns the previous value of the timer.
|
||||||
|
//
|
||||||
|
// If the Itimerval argument is the zero value, the timer will be disarmed.
|
||||||
|
func Setitimer(which ItimerWhich, it Itimerval) (Itimerval, error) {
|
||||||
|
var prev Itimerval
|
||||||
|
if err := setitimer(int(which), &it, &prev); err != nil {
|
||||||
|
return Itimerval{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return prev, nil
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unimplemented
|
* Unimplemented
|
||||||
*/
|
*/
|
||||||
// AfsSyscall
|
// AfsSyscall
|
||||||
// Alarm
|
|
||||||
// ArchPrctl
|
// ArchPrctl
|
||||||
// Brk
|
// Brk
|
||||||
// ClockNanosleep
|
// ClockNanosleep
|
||||||
|
@ -2334,7 +2376,6 @@ type RemoteIovec struct {
|
||||||
// GetMempolicy
|
// GetMempolicy
|
||||||
// GetRobustList
|
// GetRobustList
|
||||||
// GetThreadArea
|
// GetThreadArea
|
||||||
// Getitimer
|
|
||||||
// Getpmsg
|
// Getpmsg
|
||||||
// IoCancel
|
// IoCancel
|
||||||
// IoDestroy
|
// IoDestroy
|
||||||
|
@ -2412,5 +2453,4 @@ type RemoteIovec struct {
|
||||||
// Vfork
|
// Vfork
|
||||||
// Vhangup
|
// Vhangup
|
||||||
// Vserver
|
// Vserver
|
||||||
// Waitid
|
|
||||||
// _Sysctl
|
// _Sysctl
|
||||||
|
|
|
@ -35,8 +35,8 @@ func setTimeval(sec, usec int64) Timeval {
|
||||||
//sys Iopl(level int) (err error)
|
//sys Iopl(level int) (err error)
|
||||||
//sys Lchown(path string, uid int, gid int) (err error) = SYS_LCHOWN32
|
//sys Lchown(path string, uid int, gid int) (err error) = SYS_LCHOWN32
|
||||||
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64
|
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64
|
||||||
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
|
//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
|
||||||
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
|
//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
|
||||||
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
|
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
|
||||||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
|
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
|
||||||
//sys setfsgid(gid int) (prev int, err error) = SYS_SETFSGID32
|
//sys setfsgid(gid int) (prev int, err error) = SYS_SETFSGID32
|
||||||
|
@ -173,14 +173,6 @@ const (
|
||||||
_SENDMMSG = 20
|
_SENDMMSG = 20
|
||||||
)
|
)
|
||||||
|
|
||||||
func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
|
|
||||||
fd, e := socketcall(_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0)
|
|
||||||
if e != 0 {
|
|
||||||
err = e
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) {
|
func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) {
|
||||||
fd, e := socketcall(_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0)
|
fd, e := socketcall(_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0)
|
||||||
if e != 0 {
|
if e != 0 {
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
// 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 linux && (386 || amd64 || mips || mipsle || mips64 || mipsle || ppc64 || ppc64le || ppc || s390x || sparc64)
|
||||||
|
// +build linux
|
||||||
|
// +build 386 amd64 mips mipsle mips64 mipsle ppc64 ppc64le ppc s390x sparc64
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
// SYS_ALARM is not defined on arm or riscv, but is available for other GOARCH
|
||||||
|
// values.
|
||||||
|
|
||||||
|
//sys Alarm(seconds uint) (remaining uint, err error)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue