mirror of
https://github.com/cloudflare/cloudflared.git
synced 2026-06-22 20:00:16 +00:00
f674b82e2a
Introduce a new crypto/ package as the single source of truth for TLS curve preferences used on every edge-facing connection, and adopt X25519MLKEM768 as the primary post-quantum key exchange for both QUIC and HTTP/2: PQ Prefer (default): X25519MLKEM768, P256Kyber768Draft00, CurveP256 PQ Strict (--post-quantum): X25519MLKEM768, P256Kyber768Draft00 The curve list is identical under FIPS and non-FIPS builds, so crypto.GetCurvePreferences takes only a features.PostQuantumMode and returns a fresh slice on every call. HTTP/2 now applies these curve preferences the same way QUIC does. The previous PostQuantumStrict rejection in serveHTTP2 and the forced QUIC-only selection in NewProtocolSelector are removed since both transports support the same post-quantum curves; the needPQ parameter is dropped from NewProtocolSelector accordingly. Also fix a shared tls.Config race: both the QUIC and HTTP/2 paths now Clone() the per-protocol entry from TunnelConfig.EdgeTLSConfigs before mutating CurvePreferences instead of writing through the shared map entry. Legacy Kyber draft curve X25519Kyber768Draft00 and the unused removeDuplicates helper are removed along with the old supervisor/pqtunnels.go / _test.go files. AGENTS.md is updated with guidance on the new crypto/ package, the cfdcrypto import alias, the tls.Config cloning rule, and the lint workflow implications of .golangci.yaml's whole-files: true setting.
127 lines
4.2 KiB
Go
127 lines
4.2 KiB
Go
package crypto
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"runtime"
|
|
"slices"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/cloudflare/cloudflared/features"
|
|
)
|
|
|
|
// TestCurvePreferences verifies that GetCurvePreferences returns the
|
|
// documented curve list for each supported PostQuantumMode. The expected
|
|
// values correspond to the contract described in the package documentation
|
|
// and must be identical under FIPS and non-FIPS builds (see TUN-10413).
|
|
func TestCurvePreferences(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
expectedCurves []tls.CurveID
|
|
pqMode features.PostQuantumMode
|
|
}{
|
|
{
|
|
name: "Prefer PQ",
|
|
pqMode: features.PostQuantumPrefer,
|
|
expectedCurves: []tls.CurveID{tls.X25519MLKEM768, P256Kyber768Draft00, tls.CurveP256},
|
|
},
|
|
{
|
|
name: "Strict PQ",
|
|
pqMode: features.PostQuantumStrict,
|
|
expectedCurves: []tls.CurveID{tls.X25519MLKEM768, P256Kyber768Draft00},
|
|
},
|
|
}
|
|
|
|
for _, tcase := range tests {
|
|
t.Run(tcase.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
curves, err := getCurvePreferences(tcase.pqMode)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tcase.expectedCurves, curves)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestCurvePreferenceUnknownMode asserts that passing a PostQuantumMode
|
|
// value outside of the documented constants produces an error instead of
|
|
// silently returning a nil or default curve list. This protects callers
|
|
// from accidentally negotiating with an unintended curve set.
|
|
func TestCurvePreferenceUnknownMode(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := getCurvePreferences(features.PostQuantumMode(255))
|
|
require.Error(t, err)
|
|
}
|
|
|
|
// TestReturnedSliceIsIndependent ensures GetCurvePreferences returns a
|
|
// fresh slice on every call, so that callers cannot corrupt the
|
|
// package-level defaults by mutating the result.
|
|
func TestReturnedSliceIsIndependent(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
first, err := getCurvePreferences(features.PostQuantumPrefer)
|
|
require.NoError(t, err)
|
|
// Mutate the returned slice.
|
|
first[0] = tls.CurveP521
|
|
|
|
second, err := getCurvePreferences(features.PostQuantumPrefer)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tls.X25519MLKEM768, second[0], "package defaults must not be affected by caller mutation")
|
|
}
|
|
|
|
// runClientServerHandshake drives a TLS 1.3 handshake with the given curve
|
|
// preferences set on the client and captures the SupportedCurves list
|
|
// advertised by the client in its ClientHello. The helper is used by
|
|
// TestSupportedCurvesNegotiation to exercise the curves end-to-end against
|
|
// the standard library's TLS stack.
|
|
func runClientServerHandshake(t *testing.T, curves []tls.CurveID) []tls.CurveID {
|
|
var advertisedCurves []tls.CurveID
|
|
ts := httptest.NewUnstartedServer(nil)
|
|
ts.TLS = &tls.Config{ // nolint: gosec
|
|
GetConfigForClient: func(chi *tls.ClientHelloInfo) (*tls.Config, error) {
|
|
advertisedCurves = slices.Clone(chi.SupportedCurves)
|
|
return nil, nil
|
|
},
|
|
}
|
|
ts.StartTLS()
|
|
defer ts.Close()
|
|
clientTLSConfig := ts.Client().Transport.(*http.Transport).TLSClientConfig
|
|
clientTLSConfig.CurvePreferences = curves
|
|
resp, err := ts.Client().Head(ts.URL)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return nil
|
|
}
|
|
defer func() { _ = resp.Body.Close() }()
|
|
return advertisedCurves
|
|
}
|
|
|
|
// TestSupportedCurvesNegotiation verifies that the curves returned by
|
|
// GetCurvePreferences survive a real TLS handshake unchanged, i.e. the
|
|
// standard library advertises exactly the curves we expect. Currently only
|
|
// PostQuantumPrefer is exercised because PostQuantumStrict would cause the
|
|
// handshake to fail against httptest servers that do not support
|
|
// X25519MLKEM768 server-side.
|
|
func TestSupportedCurvesNegotiation(t *testing.T) {
|
|
t.Parallel()
|
|
for _, tcase := range []features.PostQuantumMode{features.PostQuantumPrefer} {
|
|
curves, err := getCurvePreferences(tcase)
|
|
require.NoError(t, err)
|
|
advertisedCurves := runClientServerHandshake(t, curves)
|
|
require.True(t, slices.Contains(advertisedCurves, tls.CurveP256))
|
|
require.True(t, slices.Contains(advertisedCurves, tls.X25519MLKEM768))
|
|
expectedLength := 2
|
|
if runtime.GOOS == "linux" {
|
|
// P256Kyber768Draft00 only exists in linux
|
|
require.True(t, slices.Contains(advertisedCurves, P256Kyber768Draft00))
|
|
expectedLength = 3
|
|
}
|
|
require.Len(t, advertisedCurves, expectedLength)
|
|
}
|
|
}
|