mirror of
https://github.com/cloudflare/cloudflared
synced 2026-06-08 13:33:07 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 569a7c3c9e | |||
| bec683b67d | |||
| 38d3c3cae5 | |||
| f2d765351d | |||
| 5d8f60873d | |||
| b474778cf1 | |||
| 65247b6f0f |
@@ -4,7 +4,7 @@ jobs:
|
||||
check:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.19.x]
|
||||
go-version: [1.20.x]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
# use a builder image for building cloudflare
|
||||
ARG TARGET_GOOS
|
||||
ARG TARGET_GOARCH
|
||||
FROM golang:1.19 as builder
|
||||
FROM golang:1.20.6 as builder
|
||||
ENV GO111MODULE=on \
|
||||
CGO_ENABLED=0 \
|
||||
TARGET_GOOS=${TARGET_GOOS} \
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# use a builder image for building cloudflare
|
||||
FROM golang:1.19 as builder
|
||||
FROM golang:1.20.6 as builder
|
||||
ENV GO111MODULE=on \
|
||||
CGO_ENABLED=0
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# use a builder image for building cloudflare
|
||||
FROM golang:1.19 as builder
|
||||
FROM golang:1.20.6 as builder
|
||||
ENV GO111MODULE=on \
|
||||
CGO_ENABLED=0
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
2023.8.2
|
||||
- 2023-08-25 TUN-7700: Implement feature selector to determine if connections will prefer post quantum cryptography
|
||||
- 2023-08-22 TUN-7707: Use X25519Kyber768Draft00 curve when post-quantum feature is enabled
|
||||
|
||||
2023.8.1
|
||||
- 2023-08-23 TUN-7718: Update R2 Token to no longer encode secret
|
||||
|
||||
2023.8.0
|
||||
- 2023-07-26 TUN-7584: Bump go 1.20.6
|
||||
|
||||
2023.7.3
|
||||
- 2023-07-25 TUN-7628: Correct Host parsing for Access
|
||||
- 2023-07-24 TUN-7624: Fix flaky TestBackoffGracePeriod test in cloudflared
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
pinned_go: &pinned_go go=1.19.6-1
|
||||
pinned_go_fips: &pinned_go_fips go-boring=1.19.6-1
|
||||
pinned_go: &pinned_go go=1.20.6-1
|
||||
pinned_go_fips: &pinned_go_fips go-boring=1.20.6-1
|
||||
|
||||
build_dir: &build_dir /cfsetup_build
|
||||
default-flavor: bullseye
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build !windows && !darwin && !linux
|
||||
// +build !windows,!darwin,!linux
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -392,7 +392,7 @@ func StartServer(
|
||||
observer.SendURL(quickTunnelURL)
|
||||
}
|
||||
|
||||
tunnelConfig, orchestratorConfig, err := prepareTunnelConfig(c, info, log, logTransport, observer, namedTunnel)
|
||||
tunnelConfig, orchestratorConfig, err := prepareTunnelConfig(ctx, c, info, log, logTransport, observer, namedTunnel)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Couldn't start tunnel")
|
||||
return err
|
||||
|
||||
@@ -4,10 +4,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cloudflare/cloudflared/features"
|
||||
)
|
||||
|
||||
func TestDedup(t *testing.T) {
|
||||
expected := []string{"a", "b"}
|
||||
actual := dedup([]string{"a", "b", "a"})
|
||||
actual := features.Dedup([]string{"a", "b", "a"})
|
||||
require.ElementsMatch(t, expected, actual)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
mathRand "math/rand"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
@@ -113,6 +113,7 @@ func dnsProxyStandAlone(c *cli.Context, namedTunnel *connection.NamedTunnelPrope
|
||||
}
|
||||
|
||||
func prepareTunnelConfig(
|
||||
ctx context.Context,
|
||||
c *cli.Context,
|
||||
info *cliutil.BuildInfo,
|
||||
log, logTransport *zerolog.Logger,
|
||||
@@ -132,22 +133,36 @@ func prepareTunnelConfig(
|
||||
tags = append(tags, tunnelpogs.Tag{Name: "ID", Value: clientID.String()})
|
||||
|
||||
transportProtocol := c.String("protocol")
|
||||
needPQ := c.Bool("post-quantum")
|
||||
if needPQ {
|
||||
|
||||
clientFeatures := features.Dedup(append(c.StringSlice("features"), features.DefaultFeatures...))
|
||||
|
||||
staticFeatures := features.StaticFeatures{}
|
||||
if c.Bool("post-quantum") {
|
||||
if FipsEnabled {
|
||||
return nil, nil, fmt.Errorf("post-quantum not supported in FIPS mode")
|
||||
}
|
||||
pqMode := features.PostQuantumStrict
|
||||
staticFeatures.PostQuantumMode = &pqMode
|
||||
}
|
||||
featureSelector, err := features.NewFeatureSelector(ctx, namedTunnel.Credentials.AccountTag, staticFeatures, log)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to create feature selector")
|
||||
}
|
||||
pqMode := featureSelector.PostQuantumMode()
|
||||
if pqMode == features.PostQuantumStrict {
|
||||
// Error if the user tries to force a non-quic transport protocol
|
||||
if transportProtocol != connection.AutoSelectFlag && transportProtocol != connection.QUIC.String() {
|
||||
return nil, nil, fmt.Errorf("post-quantum is only supported with the quic transport")
|
||||
}
|
||||
transportProtocol = connection.QUIC.String()
|
||||
clientFeatures = append(clientFeatures, features.FeaturePostQuantum)
|
||||
|
||||
log.Info().Msgf(
|
||||
"Using hybrid post-quantum key agreement %s",
|
||||
supervisor.PQKexName,
|
||||
)
|
||||
}
|
||||
|
||||
clientFeatures := dedup(append(c.StringSlice("features"), features.DefaultFeatures...))
|
||||
if needPQ {
|
||||
clientFeatures = append(clientFeatures, features.FeaturePostQuantum)
|
||||
}
|
||||
namedTunnel.Client = tunnelpogs.ClientInfo{
|
||||
ClientID: clientID[:],
|
||||
Features: clientFeatures,
|
||||
@@ -203,15 +218,6 @@ func prepareTunnelConfig(
|
||||
log.Warn().Str("edgeIPVersion", edgeIPVersion.String()).Err(err).Msg("Overriding edge-ip-version")
|
||||
}
|
||||
|
||||
var pqKexIdx int
|
||||
if needPQ {
|
||||
pqKexIdx = mathRand.Intn(len(supervisor.PQKexes))
|
||||
log.Info().Msgf(
|
||||
"Using experimental hybrid post-quantum key agreement %s",
|
||||
supervisor.PQKexNames[supervisor.PQKexes[pqKexIdx]],
|
||||
)
|
||||
}
|
||||
|
||||
tunnelConfig := &supervisor.TunnelConfig{
|
||||
GracePeriod: gracePeriod,
|
||||
ReplaceExisting: c.Bool("force"),
|
||||
@@ -236,8 +242,7 @@ func prepareTunnelConfig(
|
||||
NamedTunnel: namedTunnel,
|
||||
ProtocolSelector: protocolSelector,
|
||||
EdgeTLSConfigs: edgeTLSConfigs,
|
||||
NeedPQ: needPQ,
|
||||
PQKexIdx: pqKexIdx,
|
||||
FeatureSelector: featureSelector,
|
||||
MaxEdgeAddrRetries: uint8(c.Int("max-edge-addr-retries")),
|
||||
UDPUnregisterSessionTimeout: c.Duration(udpUnregisterSessionTimeoutFlag),
|
||||
DisableQUICPathMTUDiscovery: c.Bool(quicDisablePathMTUDiscovery),
|
||||
@@ -280,25 +285,6 @@ func isRunningFromTerminal() bool {
|
||||
return term.IsTerminal(int(os.Stdout.Fd()))
|
||||
}
|
||||
|
||||
// Remove any duplicates from the slice
|
||||
func dedup(slice []string) []string {
|
||||
|
||||
// Convert the slice into a set
|
||||
set := make(map[string]bool, 0)
|
||||
for _, str := range slice {
|
||||
set[str] = true
|
||||
}
|
||||
|
||||
// Convert the set back into a slice
|
||||
keys := make([]string, len(set))
|
||||
i := 0
|
||||
for str := range set {
|
||||
keys[i] = str
|
||||
i++
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// ParseConfigIPVersion returns the IP version from possible expected values from config
|
||||
func parseConfigIPVersion(version string) (v allregions.ConfigIPVersion, err error) {
|
||||
switch version {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
// TODO: Remove the above build tag and include this test when we start compiling with Golang 1.10.0+
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package tunnel
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package updater
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package main
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.19 as builder
|
||||
FROM golang:1.20.6 as builder
|
||||
ENV GO111MODULE=on \
|
||||
CGO_ENABLED=0
|
||||
WORKDIR /go/src/github.com/cloudflare/cloudflared/
|
||||
|
||||
@@ -28,3 +28,22 @@ func Contains(feature string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Remove any duplicates from the slice
|
||||
func Dedup(slice []string) []string {
|
||||
|
||||
// Convert the slice into a set
|
||||
set := make(map[string]bool, 0)
|
||||
for _, str := range slice {
|
||||
set[str] = true
|
||||
}
|
||||
|
||||
// Convert the set back into a slice
|
||||
keys := make([]string, len(set))
|
||||
i := 0
|
||||
for str := range set {
|
||||
keys[i] = str
|
||||
i++
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
package features
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
const (
|
||||
featureSelectorHostname = "cfd-features.argotunnel.com"
|
||||
defaultRefreshFreq = time.Hour * 6
|
||||
lookupTimeout = time.Second * 10
|
||||
)
|
||||
|
||||
type PostQuantumMode uint8
|
||||
|
||||
const (
|
||||
PostQuantumDisabled PostQuantumMode = iota
|
||||
// Prefer post quantum, but fallback if connection cannot be established
|
||||
PostQuantumPrefer
|
||||
// If the user passes the --post-quantum flag, we override
|
||||
// CurvePreferences to only support hybrid post-quantum key agreements.
|
||||
PostQuantumStrict
|
||||
)
|
||||
|
||||
// If the TXT record adds other fields, the umarshal logic will ignore those keys
|
||||
// If the TXT record is missing a key, the field will unmarshal to the default Go value
|
||||
type featuresRecord struct {
|
||||
PostQuantumPercentage int32 `json:"pq"`
|
||||
}
|
||||
|
||||
func NewFeatureSelector(ctx context.Context, accountTag string, staticFeatures StaticFeatures, logger *zerolog.Logger) (*FeatureSelector, error) {
|
||||
return newFeatureSelector(ctx, accountTag, logger, newDNSResolver(), staticFeatures, defaultRefreshFreq)
|
||||
}
|
||||
|
||||
// FeatureSelector determines if this account will try new features. It preiodically queries a DNS TXT record
|
||||
// to see which features are turned on
|
||||
type FeatureSelector struct {
|
||||
accountHash int32
|
||||
logger *zerolog.Logger
|
||||
resolver resolver
|
||||
|
||||
staticFeatures StaticFeatures
|
||||
|
||||
// lock protects concurrent access to dynamic features
|
||||
lock sync.RWMutex
|
||||
features featuresRecord
|
||||
}
|
||||
|
||||
// Features set by user provided flags
|
||||
type StaticFeatures struct {
|
||||
PostQuantumMode *PostQuantumMode
|
||||
}
|
||||
|
||||
func newFeatureSelector(ctx context.Context, accountTag string, logger *zerolog.Logger, resolver resolver, staticFeatures StaticFeatures, refreshFreq time.Duration) (*FeatureSelector, error) {
|
||||
selector := &FeatureSelector{
|
||||
accountHash: switchThreshold(accountTag),
|
||||
logger: logger,
|
||||
resolver: resolver,
|
||||
staticFeatures: staticFeatures,
|
||||
}
|
||||
|
||||
if err := selector.refresh(ctx); err != nil {
|
||||
logger.Err(err).Msg("Failed to fetch features, default to disable")
|
||||
}
|
||||
|
||||
go selector.refreshLoop(ctx, refreshFreq)
|
||||
|
||||
return selector, nil
|
||||
}
|
||||
|
||||
func (fs *FeatureSelector) PostQuantumMode() PostQuantumMode {
|
||||
if fs.staticFeatures.PostQuantumMode != nil {
|
||||
return *fs.staticFeatures.PostQuantumMode
|
||||
}
|
||||
|
||||
fs.lock.RLock()
|
||||
defer fs.lock.RUnlock()
|
||||
|
||||
if fs.features.PostQuantumPercentage > fs.accountHash {
|
||||
return PostQuantumPrefer
|
||||
}
|
||||
return PostQuantumDisabled
|
||||
}
|
||||
|
||||
func (fs *FeatureSelector) refreshLoop(ctx context.Context, refreshFreq time.Duration) {
|
||||
ticker := time.NewTicker(refreshFreq)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
err := fs.refresh(ctx)
|
||||
if err != nil {
|
||||
fs.logger.Err(err).Msg("Failed to refresh feature selector")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *FeatureSelector) refresh(ctx context.Context) error {
|
||||
record, err := fs.resolver.lookupRecord(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var features featuresRecord
|
||||
if err := json.Unmarshal(record, &features); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pq_enabled := features.PostQuantumPercentage > fs.accountHash
|
||||
fs.logger.Debug().Int32("account_hash", fs.accountHash).Int32("pq_perct", features.PostQuantumPercentage).Bool("pq_enabled", pq_enabled).Msg("Refreshed feature")
|
||||
|
||||
fs.lock.Lock()
|
||||
defer fs.lock.Unlock()
|
||||
|
||||
fs.features = features
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolver represents an object that can look up featuresRecord
|
||||
type resolver interface {
|
||||
lookupRecord(ctx context.Context) ([]byte, error)
|
||||
}
|
||||
|
||||
type dnsResolver struct {
|
||||
resolver *net.Resolver
|
||||
}
|
||||
|
||||
func newDNSResolver() *dnsResolver {
|
||||
return &dnsResolver{
|
||||
resolver: net.DefaultResolver,
|
||||
}
|
||||
}
|
||||
|
||||
func (dr *dnsResolver) lookupRecord(ctx context.Context) ([]byte, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, lookupTimeout)
|
||||
defer cancel()
|
||||
|
||||
records, err := dr.resolver.LookupTXT(ctx, featureSelectorHostname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(records) == 0 {
|
||||
return nil, fmt.Errorf("No TXT record found for %s to determine which features to opt-in", featureSelectorHostname)
|
||||
}
|
||||
|
||||
return []byte(records[0]), nil
|
||||
}
|
||||
|
||||
func switchThreshold(accountTag string) int32 {
|
||||
h := fnv.New32a()
|
||||
_, _ = h.Write([]byte(accountTag))
|
||||
return int32(h.Sum32() % 100)
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package features
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUnmarshalFeaturesRecord(t *testing.T) {
|
||||
tests := []struct {
|
||||
record []byte
|
||||
expectedPercentage int32
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
record: []byte(`{"pq":0}`),
|
||||
expectedPercentage: 0,
|
||||
},
|
||||
{
|
||||
record: []byte(`{"pq":39}`),
|
||||
expectedPercentage: 39,
|
||||
},
|
||||
{
|
||||
record: []byte(`{"pq":100}`),
|
||||
expectedPercentage: 100,
|
||||
},
|
||||
{
|
||||
record: []byte(`{}`),
|
||||
expectedPercentage: 0, // Unmarshal to default struct if key is not present
|
||||
},
|
||||
{
|
||||
record: []byte(`{"kyber":768}`),
|
||||
expectedPercentage: 0, // Unmarshal to default struct if key is not present
|
||||
},
|
||||
{
|
||||
record: []byte(`{"pq":"kyber768"}`),
|
||||
expectedErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
var features featuresRecord
|
||||
err := json.Unmarshal(test.record, &features)
|
||||
if test.expectedErr {
|
||||
require.Error(t, err, test)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.expectedPercentage, features.PostQuantumPercentage, test)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshFeaturesRecord(t *testing.T) {
|
||||
// The hash of the accountTag is 82
|
||||
accountTag := t.Name()
|
||||
threshold := switchThreshold(accountTag)
|
||||
|
||||
percentages := []int32{0, 10, 80, 83, 100}
|
||||
refreshFreq := time.Millisecond * 10
|
||||
selector := newTestSelector(t, percentages, nil, refreshFreq)
|
||||
|
||||
for _, percentage := range percentages {
|
||||
if percentage > threshold {
|
||||
require.Equal(t, PostQuantumPrefer, selector.PostQuantumMode())
|
||||
} else {
|
||||
require.Equal(t, PostQuantumDisabled, selector.PostQuantumMode())
|
||||
}
|
||||
|
||||
time.Sleep(refreshFreq + time.Millisecond)
|
||||
}
|
||||
|
||||
// Make sure error doesn't override the last fetched features
|
||||
require.Equal(t, PostQuantumPrefer, selector.PostQuantumMode())
|
||||
}
|
||||
|
||||
func TestStaticFeatures(t *testing.T) {
|
||||
percentages := []int32{0}
|
||||
pqMode := PostQuantumStrict
|
||||
selector := newTestSelector(t, percentages, &pqMode, time.Millisecond*10)
|
||||
require.Equal(t, PostQuantumStrict, selector.PostQuantumMode())
|
||||
}
|
||||
|
||||
// Verify that if the first refresh fails, the selector will use default features
|
||||
func TestFailedRefreshInitToDefault(t *testing.T) {
|
||||
selector := newTestSelector(t, []int32{}, nil, time.Second)
|
||||
require.Equal(t, featuresRecord{}, selector.features)
|
||||
require.Equal(t, PostQuantumDisabled, selector.PostQuantumMode())
|
||||
}
|
||||
|
||||
func newTestSelector(t *testing.T, percentages []int32, pqMode *PostQuantumMode, refreshFreq time.Duration) *FeatureSelector {
|
||||
accountTag := t.Name()
|
||||
logger := zerolog.Nop()
|
||||
|
||||
resolver := &mockResolver{
|
||||
percentages: percentages,
|
||||
}
|
||||
|
||||
staticFeatures := StaticFeatures{
|
||||
PostQuantumMode: pqMode,
|
||||
}
|
||||
selector, err := newFeatureSelector(context.Background(), accountTag, &logger, resolver, staticFeatures, refreshFreq)
|
||||
require.NoError(t, err)
|
||||
|
||||
return selector
|
||||
}
|
||||
|
||||
type mockResolver struct {
|
||||
nextIndex int
|
||||
percentages []int32
|
||||
}
|
||||
|
||||
func (mr *mockResolver) lookupRecord(ctx context.Context) ([]byte, error) {
|
||||
if mr.nextIndex >= len(mr.percentages) {
|
||||
return nil, fmt.Errorf("no more record to lookup")
|
||||
}
|
||||
|
||||
record, err := json.Marshal(featuresRecord{
|
||||
PostQuantumPercentage: mr.percentages[mr.nextIndex],
|
||||
})
|
||||
mr.nextIndex++
|
||||
|
||||
return record, err
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/cloudflare/cloudflared
|
||||
|
||||
go 1.19
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc
|
||||
@@ -110,8 +110,5 @@ replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1
|
||||
replace github.com/quic-go/quic-go => github.com/devincarr/quic-go v0.0.0-20230502200822-d1f4edacbee7
|
||||
|
||||
// Post-quantum tunnel RTG-1339
|
||||
replace (
|
||||
// Branches go1.19 go1.20 on github.com/cloudflare/qtls-pq
|
||||
github.com/quic-go/qtls-go1-19 => github.com/cloudflare/qtls-pq v0.0.0-20230320123031-3faac1a945b2
|
||||
github.com/quic-go/qtls-go1-20 => github.com/cloudflare/qtls-pq v0.0.0-20230320122459-4ed280d0d633
|
||||
)
|
||||
// Branches go1.20 on github.com/cloudflare/qtls-pq
|
||||
replace github.com/quic-go/qtls-go1-20 => github.com/cloudflare/qtls-pq v0.0.0-20230320122459-4ed280d0d633
|
||||
|
||||
@@ -65,8 +65,6 @@ github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc h1:Dvk3ySBsOm5Ev
|
||||
github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc/go.mod h1:HlgKKR8V5a1wroIDDIz3/A+T+9Janfq+7n1P5sEFdi0=
|
||||
github.com/cloudflare/qtls-pq v0.0.0-20230320122459-4ed280d0d633 h1:ZTub2XMOBpxyBiJf6Q+UKqAi07yt1rZmFitriHvFd8M=
|
||||
github.com/cloudflare/qtls-pq v0.0.0-20230320122459-4ed280d0d633/go.mod h1:j/igSUc4PgBMayIsBGjAFu2i7g663rm6kZrKy4htb7E=
|
||||
github.com/cloudflare/qtls-pq v0.0.0-20230320123031-3faac1a945b2 h1:0/KuLjh9lBMiXlooAdwoo+FbLVD5DABtquB0ImEFOK0=
|
||||
github.com/cloudflare/qtls-pq v0.0.0-20230320123031-3faac1a945b2/go.mod h1:XzuZIjv4mF5cM205RHHW1d60PQtWGwMR6jx38YKuYHs=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
@@ -337,6 +335,8 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package proxy
|
||||
|
||||
|
||||
+1
-3
@@ -13,7 +13,6 @@ import base64
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
from hashlib import sha256
|
||||
from pathlib import Path
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
@@ -35,7 +34,6 @@ class PkgUploader:
|
||||
|
||||
def upload_pkg_to_r2(self, filename, upload_file_path):
|
||||
endpoint_url = f"https://{self.account_id}.r2.cloudflarestorage.com"
|
||||
token_secret_hash = sha256(self.client_secret.encode()).hexdigest()
|
||||
|
||||
config = Config(
|
||||
region_name='auto',
|
||||
@@ -48,7 +46,7 @@ class PkgUploader:
|
||||
"s3",
|
||||
endpoint_url=endpoint_url,
|
||||
aws_access_key_id=self.client_id,
|
||||
aws_secret_access_key=token_secret_hash,
|
||||
aws_secret_access_key=self.client_secret,
|
||||
config=config,
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package sshgen
|
||||
|
||||
|
||||
+38
-10
@@ -4,24 +4,23 @@ import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/cloudflare/cloudflared/features"
|
||||
)
|
||||
|
||||
// When experimental post-quantum tunnels are enabled, and we're hitting an
|
||||
// issue creating the tunnel, we'll report the first error
|
||||
// to https://pqtunnels.cloudflareresearch.com.
|
||||
|
||||
var (
|
||||
PQKexes = [...]tls.CurveID{
|
||||
tls.CurveID(0xfe30), // X25519Kyber512Draft00
|
||||
tls.CurveID(0xfe31), // X25519Kyber768Draft00
|
||||
}
|
||||
PQKexNames map[tls.CurveID]string = map[tls.CurveID]string{
|
||||
tls.CurveID(0xfe30): "X25519Kyber512Draft00",
|
||||
tls.CurveID(0xfe31): "X25519Kyber768Draft00",
|
||||
}
|
||||
const (
|
||||
PQKex = tls.CurveID(0xfe31) // X25519Kyber768Draft00
|
||||
PQKexName = "X25519Kyber768Draft00"
|
||||
)
|
||||
|
||||
var (
|
||||
pqtMux sync.Mutex // protects pqtSubmitted and pqtWaitForMessage
|
||||
pqtSubmitted bool // whether an error has already been submitted
|
||||
|
||||
@@ -70,7 +69,7 @@ func submitPQTunnelError(rep error, config *TunnelConfig) {
|
||||
Message string `json:"m"`
|
||||
Version string `json:"v"`
|
||||
}{
|
||||
Group: int(PQKexes[config.PQKexIdx]),
|
||||
Group: int(PQKex),
|
||||
Message: rep.Error(),
|
||||
Version: config.ReportedVersion,
|
||||
})
|
||||
@@ -98,3 +97,32 @@ func submitPQTunnelError(rep error, config *TunnelConfig) {
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
func curvePreference(pqMode features.PostQuantumMode, currentCurve []tls.CurveID) ([]tls.CurveID, error) {
|
||||
switch pqMode {
|
||||
case features.PostQuantumStrict:
|
||||
// If the user passes the -post-quantum flag, we override
|
||||
// CurvePreferences to only support hybrid post-quantum key agreements.
|
||||
return []tls.CurveID{PQKex}, nil
|
||||
case features.PostQuantumPrefer:
|
||||
if len(currentCurve) == 0 {
|
||||
return []tls.CurveID{PQKex}, nil
|
||||
}
|
||||
|
||||
if currentCurve[0] != PQKex {
|
||||
return append([]tls.CurveID{PQKex}, currentCurve...), nil
|
||||
}
|
||||
return currentCurve, nil
|
||||
case features.PostQuantumDisabled:
|
||||
curvePref := currentCurve
|
||||
// Remove PQ from curve preference
|
||||
for i, curve := range currentCurve {
|
||||
if curve == PQKex {
|
||||
curvePref = append(curvePref[:i], curvePref[i+1:]...)
|
||||
}
|
||||
}
|
||||
return curvePref, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Unexpected post quantum mode")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package supervisor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
@@ -10,6 +11,8 @@ import (
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
qtls120 "github.com/quic-go/qtls-go1-20"
|
||||
|
||||
"github.com/cloudflare/cloudflared/connection"
|
||||
"github.com/cloudflare/cloudflared/edgediscovery"
|
||||
"github.com/cloudflare/cloudflared/orchestration"
|
||||
@@ -78,6 +81,8 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato
|
||||
|
||||
reconnectCredentialManager := newReconnectCredentialManager(connection.MetricsNamespace, connection.TunnelSubsystem, config.HAConnections)
|
||||
|
||||
registerTLSEventLogger(config.Log)
|
||||
|
||||
tracker := tunnelstate.NewConnTracker(config.Log)
|
||||
log := NewConnAwareLogger(config.Log, tracker, config.Observer)
|
||||
|
||||
@@ -336,3 +341,26 @@ func (s *Supervisor) waitForNextTunnel(index int) bool {
|
||||
func (s *Supervisor) unusedIPs() bool {
|
||||
return s.edgeIPs.AvailableAddrs() > s.config.HAConnections
|
||||
}
|
||||
|
||||
func registerTLSEventLogger(logger *zerolog.Logger) {
|
||||
qtls120.SetCFEventHandler(func(ev qtls120.CFEvent) {
|
||||
logger.Debug().Bool("handshake", ev.IsHandshake()).Str("handshake_duration", ev.Duration().String()).Str("curve", tlsCurveName(ev.KEX())).Msg("QUIC TLS event")
|
||||
})
|
||||
}
|
||||
|
||||
func tlsCurveName(curve tls.CurveID) string {
|
||||
switch curve {
|
||||
case tls.CurveP256:
|
||||
return "p256"
|
||||
case tls.CurveP384:
|
||||
return "p384"
|
||||
case tls.CurveP521:
|
||||
return "p521"
|
||||
case tls.X25519:
|
||||
return "X25519"
|
||||
case PQKex:
|
||||
return PQKexName
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
+15
-18
@@ -61,9 +61,6 @@ type TunnelConfig struct {
|
||||
|
||||
NeedPQ bool
|
||||
|
||||
// Index into PQKexes of post-quantum kex to use if NeedPQ is set.
|
||||
PQKexIdx int
|
||||
|
||||
NamedTunnel *connection.NamedTunnelProperties
|
||||
ProtocolSelector connection.ProtocolSelector
|
||||
EdgeTLSConfigs map[connection.Protocol]*tls.Config
|
||||
@@ -72,6 +69,8 @@ type TunnelConfig struct {
|
||||
UDPUnregisterSessionTimeout time.Duration
|
||||
|
||||
DisableQUICPathMTUDiscovery bool
|
||||
|
||||
FeatureSelector *features.FeatureSelector
|
||||
}
|
||||
|
||||
func (c *TunnelConfig) registrationOptions(connectionID uint8, OriginLocalIP string, uuid uuid.UUID) *tunnelpogs.RegistrationOptions {
|
||||
@@ -539,7 +538,8 @@ func (e *EdgeTunnelServer) serveHTTP2(
|
||||
controlStreamHandler connection.ControlStreamHandler,
|
||||
connIndex uint8,
|
||||
) error {
|
||||
if e.config.NeedPQ {
|
||||
pqMode := e.config.FeatureSelector.PostQuantumMode()
|
||||
if pqMode == features.PostQuantumStrict {
|
||||
return unrecoverableError{errors.New("HTTP/2 transport does not support post-quantum")}
|
||||
}
|
||||
|
||||
@@ -582,21 +582,18 @@ func (e *EdgeTunnelServer) serveQUIC(
|
||||
) (err error, recoverable bool) {
|
||||
tlsConfig := e.config.EdgeTLSConfigs[connection.QUIC]
|
||||
|
||||
if e.config.NeedPQ {
|
||||
// If the user passes the -post-quantum flag, we override
|
||||
// CurvePreferences to only support hybrid post-quantum key agreements.
|
||||
cs := make([]tls.CurveID, len(PQKexes))
|
||||
copy(cs, PQKexes[:])
|
||||
|
||||
// It is unclear whether Kyber512 or Kyber768 will become the standard.
|
||||
// Kyber768 is a bit bigger (and doesn't fit in one initial
|
||||
// datagram anymore). We're enabling both, but pick randomly which
|
||||
// one to put first. (TLS will use the first one in the list
|
||||
// and allows a fallback to the second.)
|
||||
cs[0], cs[e.config.PQKexIdx] = cs[e.config.PQKexIdx], cs[0]
|
||||
tlsConfig.CurvePreferences = cs
|
||||
pqMode := e.config.FeatureSelector.PostQuantumMode()
|
||||
if pqMode == features.PostQuantumStrict || pqMode == features.PostQuantumPrefer {
|
||||
connOptions.Client.Features = features.Dedup(append(connOptions.Client.Features, features.FeaturePostQuantum))
|
||||
}
|
||||
|
||||
curvePref, err := curvePreference(pqMode, tlsConfig.CurvePreferences)
|
||||
if err != nil {
|
||||
return err, true
|
||||
}
|
||||
|
||||
tlsConfig.CurvePreferences = curvePref
|
||||
|
||||
quicConfig := &quic.Config{
|
||||
HandshakeIdleTimeout: quicpogs.HandshakeIdleTimeout,
|
||||
MaxIdleTimeout: quicpogs.MaxIdleTimeout,
|
||||
@@ -624,7 +621,7 @@ func (e *EdgeTunnelServer) serveQUIC(
|
||||
e.config.UDPUnregisterSessionTimeout,
|
||||
)
|
||||
if err != nil {
|
||||
if e.config.NeedPQ {
|
||||
if pqMode == features.PostQuantumStrict || pqMode == features.PostQuantumPrefer {
|
||||
handlePQTunnelError(err, e.config)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package token
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build !windows && !darwin && !linux && !netbsd && !freebsd && !openbsd
|
||||
// +build !windows,!darwin,!linux,!netbsd,!freebsd,!openbsd
|
||||
|
||||
package token
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build linux || freebsd || openbsd || netbsd
|
||||
// +build linux freebsd openbsd netbsd
|
||||
|
||||
package token
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package token
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package token
|
||||
|
||||
|
||||
-170
@@ -1,170 +0,0 @@
|
||||
// Copyright 2022 Cloudflare, Inc. All rights reserved. Use of this source code
|
||||
// is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
//
|
||||
// Glue to add Circl's (post-quantum) hybrid KEMs.
|
||||
//
|
||||
// To enable set CurvePreferences with the desired scheme as the first element:
|
||||
//
|
||||
// import (
|
||||
// "github.com/cloudflare/circl/kem/tls"
|
||||
// "github.com/cloudflare/circl/kem/hybrid"
|
||||
//
|
||||
// [...]
|
||||
//
|
||||
// config.CurvePreferences = []tls.CurveID{
|
||||
// qtls.X25519Kyber512Draft00,
|
||||
// qtls.X25519,
|
||||
// qtls.P256,
|
||||
// }
|
||||
|
||||
package qtls
|
||||
|
||||
import (
|
||||
"github.com/cloudflare/circl/kem"
|
||||
"github.com/cloudflare/circl/kem/hybrid"
|
||||
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Either ecdheParameters or kem.PrivateKey
|
||||
type clientKeySharePrivate interface{}
|
||||
|
||||
var (
|
||||
X25519Kyber512Draft00 = CurveID(0xfe30)
|
||||
X25519Kyber768Draft00 = CurveID(0xfe31)
|
||||
invalidCurveID = CurveID(0)
|
||||
)
|
||||
|
||||
func kemSchemeKeyToCurveID(s kem.Scheme) CurveID {
|
||||
switch s.Name() {
|
||||
case "Kyber512-X25519":
|
||||
return X25519Kyber512Draft00
|
||||
case "Kyber768-X25519":
|
||||
return X25519Kyber768Draft00
|
||||
default:
|
||||
return invalidCurveID
|
||||
}
|
||||
}
|
||||
|
||||
// Extract CurveID from clientKeySharePrivate
|
||||
func clientKeySharePrivateCurveID(ks clientKeySharePrivate) CurveID {
|
||||
switch v := ks.(type) {
|
||||
case kem.PrivateKey:
|
||||
ret := kemSchemeKeyToCurveID(v.Scheme())
|
||||
if ret == invalidCurveID {
|
||||
panic("cfkem: internal error: don't know CurveID for this KEM")
|
||||
}
|
||||
return ret
|
||||
case ecdheParameters:
|
||||
return v.CurveID()
|
||||
default:
|
||||
panic("cfkem: internal error: unknown clientKeySharePrivate")
|
||||
}
|
||||
}
|
||||
|
||||
// Returns scheme by CurveID if supported by Circl
|
||||
func curveIdToCirclScheme(id CurveID) kem.Scheme {
|
||||
switch id {
|
||||
case X25519Kyber512Draft00:
|
||||
return hybrid.Kyber512X25519()
|
||||
case X25519Kyber768Draft00:
|
||||
return hybrid.Kyber768X25519()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generate a new shared secret and encapsulates it for the packed
|
||||
// public key in ppk using randomness from rnd.
|
||||
func encapsulateForKem(scheme kem.Scheme, rnd io.Reader, ppk []byte) (
|
||||
ct, ss []byte, alert alert, err error) {
|
||||
pk, err := scheme.UnmarshalBinaryPublicKey(ppk)
|
||||
if err != nil {
|
||||
return nil, nil, alertIllegalParameter, fmt.Errorf("unpack pk: %w", err)
|
||||
}
|
||||
seed := make([]byte, scheme.EncapsulationSeedSize())
|
||||
if _, err := io.ReadFull(rnd, seed); err != nil {
|
||||
return nil, nil, alertInternalError, fmt.Errorf("random: %w", err)
|
||||
}
|
||||
ct, ss, err = scheme.EncapsulateDeterministically(pk, seed)
|
||||
return ct, ss, alertIllegalParameter, err
|
||||
}
|
||||
|
||||
// Generate a new keypair using randomness from rnd.
|
||||
func generateKemKeyPair(scheme kem.Scheme, rnd io.Reader) (
|
||||
kem.PublicKey, kem.PrivateKey, error) {
|
||||
seed := make([]byte, scheme.SeedSize())
|
||||
if _, err := io.ReadFull(rnd, seed); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pk, sk := scheme.DeriveKeyPair(seed)
|
||||
return pk, sk, nil
|
||||
}
|
||||
|
||||
// Events. We cannot use the same approach as used in our plain Go fork
|
||||
// as we cannot change tls.Config, tls.ConnectionState, etc. Also we do
|
||||
// not want to maintain a fork of quic-go itself as well. This seems
|
||||
// the simplest option.
|
||||
|
||||
// CFEvent. There are two events: one emitted on HRR and one emitted
|
||||
type CFEvent interface {
|
||||
// Common to all events
|
||||
ServerSide() bool // true if server-side; false if on client-side
|
||||
|
||||
// HRR event. Emitted when an HRR happened.
|
||||
IsHRR() bool // true if this is an HRR event
|
||||
|
||||
// Handshake event.
|
||||
IsHandshake() bool // true if this is a handshake event.
|
||||
Duration() time.Duration // how long did the handshake take?
|
||||
KEX() tls.CurveID // which kex was established?
|
||||
}
|
||||
|
||||
type CFEventHandler func(CFEvent)
|
||||
|
||||
// Registers a handler to be called when a CFEvent is emitted; returns
|
||||
// the previous handler.
|
||||
func SetCFEventHandler(handler CFEventHandler) CFEventHandler {
|
||||
cfEventMux.Lock()
|
||||
ret := cfEventHandler
|
||||
cfEventHandler = handler
|
||||
cfEventMux.Unlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
func raiseCFEvent(ev CFEvent) {
|
||||
cfEventMux.Lock()
|
||||
handler := cfEventHandler
|
||||
cfEventMux.Unlock()
|
||||
if handler != nil {
|
||||
handler(ev)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
cfEventMux sync.Mutex
|
||||
cfEventHandler CFEventHandler
|
||||
)
|
||||
|
||||
type cfEventHRR struct{ serverSide bool }
|
||||
|
||||
func (*cfEventHRR) IsHRR() bool { return true }
|
||||
func (ev *cfEventHRR) ServerSide() bool { return ev.serverSide }
|
||||
func (*cfEventHRR) IsHandshake() bool { return false }
|
||||
func (ev *cfEventHRR) Duration() time.Duration { panic("wrong event") }
|
||||
func (ev *cfEventHRR) KEX() tls.CurveID { panic("wrong event") }
|
||||
|
||||
type cfEventHandshake struct {
|
||||
serverSide bool
|
||||
duration time.Duration
|
||||
kex tls.CurveID
|
||||
}
|
||||
|
||||
func (*cfEventHandshake) IsHRR() bool { return false }
|
||||
func (ev *cfEventHandshake) ServerSide() bool { return ev.serverSide }
|
||||
func (*cfEventHandshake) IsHandshake() bool { return true }
|
||||
func (ev *cfEventHandshake) Duration() time.Duration { return ev.duration }
|
||||
func (ev *cfEventHandshake) KEX() tls.CurveID { return ev.kex }
|
||||
+21
-34
@@ -40,7 +40,7 @@ type clientHandshakeState struct {
|
||||
|
||||
var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme
|
||||
|
||||
func (c *Conn) makeClientHello() (*clientHelloMsg, clientKeySharePrivate, error) {
|
||||
func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) {
|
||||
config := c.config
|
||||
if len(config.ServerName) == 0 && !config.InsecureSkipVerify {
|
||||
return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
|
||||
@@ -142,8 +142,11 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, clientKeySharePrivate, error)
|
||||
hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms
|
||||
}
|
||||
|
||||
var secret clientKeySharePrivate
|
||||
var params ecdheParameters
|
||||
if hello.supportedVersions[0] == VersionTLS13 {
|
||||
if len(hello.supportedVersions) == 1 {
|
||||
hello.cipherSuites = hello.cipherSuites[:0]
|
||||
}
|
||||
if hasAESGCMHardwareSupport {
|
||||
hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13...)
|
||||
} else {
|
||||
@@ -151,37 +154,21 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, clientKeySharePrivate, error)
|
||||
}
|
||||
|
||||
curveID := config.curvePreferences()[0]
|
||||
if scheme := curveIdToCirclScheme(curveID); scheme != nil {
|
||||
pk, sk, err := generateKemKeyPair(scheme, config.rand())
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("generateKemKeyPair %s: %w",
|
||||
scheme.Name(), err)
|
||||
}
|
||||
packedPk, err := pk.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("pack circl public key %s: %w",
|
||||
scheme.Name(), err)
|
||||
}
|
||||
hello.keyShares = []keyShare{{group: curveID, data: packedPk}}
|
||||
secret = sk
|
||||
} else {
|
||||
if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
|
||||
return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve")
|
||||
}
|
||||
params, err := generateECDHEParameters(config.rand(), curveID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
|
||||
secret = params
|
||||
if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
|
||||
return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve")
|
||||
}
|
||||
params, err = generateECDHEParameters(config.rand(), curveID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
|
||||
}
|
||||
|
||||
if hello.supportedVersions[0] == VersionTLS13 && c.extraConfig != nil && c.extraConfig.GetExtensions != nil {
|
||||
hello.additionalExtensions = c.extraConfig.GetExtensions(typeClientHello)
|
||||
}
|
||||
|
||||
return hello, secret, nil
|
||||
return hello, params, nil
|
||||
}
|
||||
|
||||
func (c *Conn) clientHandshake(ctx context.Context) (err error) {
|
||||
@@ -274,14 +261,14 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
|
||||
|
||||
if c.vers == VersionTLS13 {
|
||||
hs := &clientHandshakeStateTLS13{
|
||||
c: c,
|
||||
ctx: ctx,
|
||||
serverHello: serverHello,
|
||||
hello: hello,
|
||||
keySharePrivate: ecdheParams,
|
||||
session: session,
|
||||
earlySecret: earlySecret,
|
||||
binderKey: binderKey,
|
||||
c: c,
|
||||
ctx: ctx,
|
||||
serverHello: serverHello,
|
||||
hello: hello,
|
||||
ecdheParams: ecdheParams,
|
||||
session: session,
|
||||
earlySecret: earlySecret,
|
||||
binderKey: binderKey,
|
||||
}
|
||||
|
||||
// In TLS 1.3, session tickets are delivered after the handshake.
|
||||
|
||||
+15
-56
@@ -12,12 +12,10 @@ import (
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
circlKem "github.com/cloudflare/circl/kem"
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
)
|
||||
|
||||
@@ -26,8 +24,7 @@ type clientHandshakeStateTLS13 struct {
|
||||
ctx context.Context
|
||||
serverHello *serverHelloMsg
|
||||
hello *clientHelloMsg
|
||||
|
||||
keySharePrivate clientKeySharePrivate
|
||||
ecdheParams ecdheParameters
|
||||
|
||||
session *clientSessionState
|
||||
earlySecret []byte
|
||||
@@ -47,8 +44,6 @@ type clientHandshakeStateTLS13 struct {
|
||||
func (hs *clientHandshakeStateTLS13) handshake() error {
|
||||
c := hs.c
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
if needFIPS() {
|
||||
return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode")
|
||||
}
|
||||
@@ -61,7 +56,7 @@ func (hs *clientHandshakeStateTLS13) handshake() error {
|
||||
}
|
||||
|
||||
// Consistency check on the presence of a keyShare and its parameters.
|
||||
if hs.keySharePrivate == nil || len(hs.hello.keyShares) != 1 {
|
||||
if hs.ecdheParams == nil || len(hs.hello.keyShares) != 1 {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
|
||||
@@ -119,12 +114,6 @@ func (hs *clientHandshakeStateTLS13) handshake() error {
|
||||
return err
|
||||
}
|
||||
|
||||
raiseCFEvent(&cfEventHandshake{
|
||||
serverSide: false,
|
||||
duration: time.Since(startTime),
|
||||
kex: hs.serverHello.serverShare.group,
|
||||
})
|
||||
|
||||
atomic.StoreUint32(&c.handshakeStatus, 1)
|
||||
c.updateConnectionState()
|
||||
return nil
|
||||
@@ -201,8 +190,6 @@ func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
|
||||
func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
|
||||
c := hs.c
|
||||
|
||||
raiseCFEvent(&cfEventHRR{serverSide: false})
|
||||
|
||||
// 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.
|
||||
@@ -246,38 +233,21 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected unsupported group")
|
||||
}
|
||||
if clientKeySharePrivateCurveID(hs.keySharePrivate) == curveID {
|
||||
if hs.ecdheParams.CurveID() == curveID {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
|
||||
}
|
||||
if scheme := curveIdToCirclScheme(curveID); scheme != nil {
|
||||
pk, sk, err := generateKemKeyPair(scheme, c.config.rand())
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return fmt.Errorf("HRR generateKeyPair %s: %w",
|
||||
scheme.Name(), err)
|
||||
}
|
||||
packedPk, err := pk.MarshalBinary()
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return fmt.Errorf("HRR pack circl public key %s: %w",
|
||||
scheme.Name(), err)
|
||||
}
|
||||
hs.keySharePrivate = sk
|
||||
hs.hello.keyShares = []keyShare{{group: curveID, data: packedPk}}
|
||||
} else {
|
||||
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.keySharePrivate = params
|
||||
hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
|
||||
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
|
||||
@@ -363,7 +333,7 @@ func (hs *clientHandshakeStateTLS13) processServerHello() error {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server did not send a key share")
|
||||
}
|
||||
if hs.serverHello.serverShare.group != clientKeySharePrivateCurveID(hs.keySharePrivate) {
|
||||
if hs.serverHello.serverShare.group != hs.ecdheParams.CurveID() {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected unsupported group")
|
||||
}
|
||||
@@ -401,18 +371,7 @@ func (hs *clientHandshakeStateTLS13) processServerHello() error {
|
||||
func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
|
||||
c := hs.c
|
||||
|
||||
var sharedKey []byte
|
||||
if params, ok := hs.keySharePrivate.(ecdheParameters); ok {
|
||||
sharedKey = params.SharedKey(hs.serverHello.serverShare.data)
|
||||
} else if sk, ok := hs.keySharePrivate.(circlKem.PrivateKey); ok {
|
||||
var err error
|
||||
sharedKey, err = sk.Scheme().Decapsulate(sk, hs.serverHello.serverShare.data)
|
||||
if err != nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return fmt.Errorf("%s decaps: %w", sk.Scheme().Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data)
|
||||
if sharedKey == nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid server key share")
|
||||
|
||||
+7
-28
@@ -11,7 +11,6 @@ import (
|
||||
"crypto/hmac"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"sync/atomic"
|
||||
@@ -47,8 +46,6 @@ type serverHandshakeStateTLS13 struct {
|
||||
func (hs *serverHandshakeStateTLS13) handshake() error {
|
||||
c := hs.c
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
if needFIPS() {
|
||||
return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode")
|
||||
}
|
||||
@@ -88,12 +85,6 @@ func (hs *serverHandshakeStateTLS13) handshake() error {
|
||||
return err
|
||||
}
|
||||
|
||||
raiseCFEvent(&cfEventHandshake{
|
||||
serverSide: true,
|
||||
duration: time.Since(startTime),
|
||||
kex: hs.hello.serverShare.group,
|
||||
})
|
||||
|
||||
atomic.StoreUint32(&c.handshakeStatus, 1)
|
||||
c.updateConnectionState()
|
||||
return nil
|
||||
@@ -208,27 +199,17 @@ GroupSelection:
|
||||
clientKeyShare = &hs.clientHello.keyShares[0]
|
||||
}
|
||||
|
||||
if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && curveIdToCirclScheme(selectedGroup) == nil && !ok {
|
||||
if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && !ok {
|
||||
c.sendAlert(alertInternalError)
|
||||
return errors.New("tls: CurvePreferences includes unsupported curve")
|
||||
}
|
||||
if kem := curveIdToCirclScheme(selectedGroup); kem != nil {
|
||||
ct, ss, alert, err := encapsulateForKem(kem, c.config.rand(), clientKeyShare.data)
|
||||
if err != nil {
|
||||
c.sendAlert(alert)
|
||||
return fmt.Errorf("%s encap: %w", kem.Name(), err)
|
||||
}
|
||||
hs.hello.serverShare = keyShare{group: selectedGroup, data: ct}
|
||||
hs.sharedKey = ss
|
||||
} else {
|
||||
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)
|
||||
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")
|
||||
@@ -458,8 +439,6 @@ func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
|
||||
func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error {
|
||||
c := hs.c
|
||||
|
||||
raiseCFEvent(&cfEventHRR{serverSide: true})
|
||||
|
||||
// The first ClientHello gets double-hashed into the transcript upon a
|
||||
// HelloRetryRequest. See RFC 8446, Section 4.4.1.
|
||||
if err := transcriptMsg(hs.clientHello, hs.transcript); err != nil {
|
||||
|
||||
+1
-1
@@ -168,7 +168,7 @@ type ecdheKeyAgreement struct {
|
||||
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) && curveIdToCirclScheme(c) == nil {
|
||||
if config.supportsCurve(c) {
|
||||
curveID = c
|
||||
break
|
||||
}
|
||||
|
||||
Vendored
+1
-2
@@ -258,7 +258,7 @@ github.com/prometheus/common/model
|
||||
github.com/prometheus/procfs
|
||||
github.com/prometheus/procfs/internal/fs
|
||||
github.com/prometheus/procfs/internal/util
|
||||
# github.com/quic-go/qtls-go1-19 v0.3.2 => github.com/cloudflare/qtls-pq v0.0.0-20230320123031-3faac1a945b2
|
||||
# github.com/quic-go/qtls-go1-19 v0.3.2
|
||||
## explicit; go 1.19
|
||||
github.com/quic-go/qtls-go1-19
|
||||
# github.com/quic-go/qtls-go1-20 v0.2.2 => github.com/cloudflare/qtls-pq v0.0.0-20230320122459-4ed280d0d633
|
||||
@@ -577,5 +577,4 @@ zombiezen.com/go/capnproto2/std/capnp/rpc
|
||||
# github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1
|
||||
# gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1
|
||||
# github.com/quic-go/quic-go => github.com/devincarr/quic-go v0.0.0-20230502200822-d1f4edacbee7
|
||||
# github.com/quic-go/qtls-go1-19 => github.com/cloudflare/qtls-pq v0.0.0-20230320123031-3faac1a945b2
|
||||
# github.com/quic-go/qtls-go1-20 => github.com/cloudflare/qtls-pq v0.0.0-20230320122459-4ed280d0d633
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package watcher
|
||||
|
||||
|
||||
Reference in New Issue
Block a user