mirror of
https://github.com/golang/go
synced 2024-11-14 13:20:30 -07:00
crypto/tls: implement X25519Kyber768Draft00
Forced the testConfig CurvePreferences to exclude X25519Kyber768Draft00 to avoid bloating the transcripts, but I manually tested it and the tests all update and pass successfully, causing 7436 insertions(+), 3251 deletions(-). Fixes #67061 Change-Id: If6f13bca561835777ab0889a490487b7c2366c3c Reviewed-on: https://go-review.googlesource.com/c/go/+/586656 Auto-Submit: Filippo Valsorda <filippo@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
7c52c064df
commit
d0edd9acc8
@ -179,6 +179,10 @@ Previous versions default to `winreadlinkvolume=0`.
|
|||||||
Go 1.23 corrected the semantics of contention reports for runtime-internal locks,
|
Go 1.23 corrected the semantics of contention reports for runtime-internal locks,
|
||||||
and so removed the [`runtimecontentionstacks` setting](/pkg/runtime#hdr-Environment_Variable).
|
and so removed the [`runtimecontentionstacks` setting](/pkg/runtime#hdr-Environment_Variable).
|
||||||
|
|
||||||
|
Go 1.23 enabled the experimental post-quantum key exchange mechanism
|
||||||
|
X25519Kyber768Draft00 by default. The default can be reverted using the
|
||||||
|
[`tlskyber` setting](/pkg/crypto/tls/#Config.CurvePreferences).
|
||||||
|
|
||||||
### Go 1.22
|
### Go 1.22
|
||||||
|
|
||||||
Go 1.22 adds a configurable limit to control the maximum acceptable RSA key size
|
Go 1.22 adds a configurable limit to control the maximum acceptable RSA key size
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
"SendEmptyRecords*": "crypto/tls doesn't implement spam protections",
|
"SendEmptyRecords*": "crypto/tls doesn't implement spam protections",
|
||||||
"SendWarningAlerts*": "crypto/tls doesn't implement spam protections",
|
"SendWarningAlerts*": "crypto/tls doesn't implement spam protections",
|
||||||
"TooManyKeyUpdates": "crypto/tls doesn't implement spam protections (TODO: I think?)",
|
"TooManyKeyUpdates": "crypto/tls doesn't implement spam protections (TODO: I think?)",
|
||||||
|
"KyberNotEnabledByDefaultInClients": "crypto/tls intentionally enables it",
|
||||||
|
"JustConfiguringKyberWorks": "we always send a X25519 key share with Kyber",
|
||||||
|
"KyberKeyShareIncludedSecond": "we always send the Kyber key share first",
|
||||||
|
"KyberKeyShareIncludedThird": "we always send the Kyber key share first",
|
||||||
"SkipNewSessionTicket": "TODO confusing? maybe bug",
|
"SkipNewSessionTicket": "TODO confusing? maybe bug",
|
||||||
"SendUserCanceledAlerts*": "TODO may be a real bug?",
|
"SendUserCanceledAlerts*": "TODO may be a real bug?",
|
||||||
"GREASE-Server-TLS13": "TODO ???",
|
"GREASE-Server-TLS13": "TODO ???",
|
||||||
@ -14,6 +18,12 @@
|
|||||||
"EchoTLS13CompatibilitySessionID": "TODO reject compat session ID",
|
"EchoTLS13CompatibilitySessionID": "TODO reject compat session ID",
|
||||||
"*ECH-Server*": "no ECH server support",
|
"*ECH-Server*": "no ECH server support",
|
||||||
"TLS-ECH-Client-UnsolictedHRRExtension": "TODO",
|
"TLS-ECH-Client-UnsolictedHRRExtension": "TODO",
|
||||||
|
"*Client-P-224*": "no P-224 support",
|
||||||
|
"*Server-P-224*": "no P-224 support",
|
||||||
|
"CurveID-Resume*": "unexposed curveID is not stored in the ticket yet",
|
||||||
|
"CheckLeafCurve": "TODO: first pass, this should be fixed",
|
||||||
|
"DisabledCurve-HelloRetryRequest-TLS13": "TODO: first pass, this should be fixed",
|
||||||
|
"UnsupportedCurve": "TODO: first pass, this should be fixed",
|
||||||
"SupportTicketsWithSessionID": "TODO: first pass, this should be fixed",
|
"SupportTicketsWithSessionID": "TODO: first pass, this should be fixed",
|
||||||
"NoNullCompression-TLS12": "TODO: first pass, this should be fixed",
|
"NoNullCompression-TLS12": "TODO: first pass, this should be fixed",
|
||||||
"KeyUpdate-RequestACK": "TODO: first pass, this should be fixed",
|
"KeyUpdate-RequestACK": "TODO: first pass, this should be fixed",
|
||||||
@ -172,4 +182,4 @@
|
|||||||
"CheckClientCertificateTypes": "TODO: first pass, this should be fixed",
|
"CheckClientCertificateTypes": "TODO: first pass, this should be fixed",
|
||||||
"CheckECDSACurve-TLS12": "TODO: first pass, this should be fixed"
|
"CheckECDSACurve-TLS12": "TODO: first pass, this should be fixed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,6 +42,9 @@ var (
|
|||||||
|
|
||||||
resumeCount = flag.Int("resume-count", 0, "")
|
resumeCount = flag.Int("resume-count", 0, "")
|
||||||
|
|
||||||
|
curves = flagStringSlice("curves", "")
|
||||||
|
expectedCurve = flag.String("expect-curve-id", "", "")
|
||||||
|
|
||||||
shimID = flag.Uint64("shim-id", 0, "")
|
shimID = flag.Uint64("shim-id", 0, "")
|
||||||
_ = flag.Bool("ipv6", false, "")
|
_ = flag.Bool("ipv6", false, "")
|
||||||
|
|
||||||
@ -132,6 +137,23 @@ var (
|
|||||||
// -verify-prefs
|
// -verify-prefs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type stringSlice []string
|
||||||
|
|
||||||
|
func flagStringSlice(name, usage string) *stringSlice {
|
||||||
|
f := &stringSlice{}
|
||||||
|
flag.Var(f, name, usage)
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (saf stringSlice) String() string {
|
||||||
|
return strings.Join(saf, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (saf stringSlice) Set(s string) error {
|
||||||
|
saf = append(saf, s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func bogoShim() {
|
func bogoShim() {
|
||||||
if *isHandshakerSupported {
|
if *isHandshakerSupported {
|
||||||
fmt.Println("No")
|
fmt.Println("No")
|
||||||
@ -177,6 +199,16 @@ func bogoShim() {
|
|||||||
cfg.ClientAuth = RequireAnyClientCert
|
cfg.ClientAuth = RequireAnyClientCert
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(*curves) != 0 {
|
||||||
|
for _, curveStr := range *curves {
|
||||||
|
id, err := strconv.Atoi(curveStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to parse curve id %q: %s", curveStr, err)
|
||||||
|
}
|
||||||
|
cfg.CurvePreferences = append(cfg.CurvePreferences, CurveID(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < *resumeCount+1; i++ {
|
for i := 0; i < *resumeCount+1; i++ {
|
||||||
conn, err := net.Dial("tcp", net.JoinHostPort("localhost", *port))
|
conn, err := net.Dial("tcp", net.JoinHostPort("localhost", *port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -221,6 +253,16 @@ func bogoShim() {
|
|||||||
log.Fatalf("write err: %s", err)
|
log.Fatalf("write err: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *expectedCurve != "" {
|
||||||
|
expectedCurveID, err := strconv.Atoi(*expectedCurve)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to parse -expect-curve-id: %s", err)
|
||||||
|
}
|
||||||
|
if tlsConn.curveID != CurveID(expectedCurveID) {
|
||||||
|
log.Fatalf("unexpected curve id: want %d, got %d", expectedCurveID, tlsConn.curveID)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +280,7 @@ func TestBogoSuite(t *testing.T) {
|
|||||||
t.Skip("#66913: windows network connections are flakey on builders")
|
t.Skip("#66913: windows network connections are flakey on builders")
|
||||||
}
|
}
|
||||||
|
|
||||||
const boringsslModVer = "v0.0.0-20240412155355-1c6e10495e4f"
|
const boringsslModVer = "v0.0.0-20240517213134-ba62c812f01f"
|
||||||
output, err := exec.Command("go", "mod", "download", "-json", "github.com/google/boringssl@"+boringsslModVer).CombinedOutput()
|
output, err := exec.Command("go", "mod", "download", "-json", "github.com/google/boringssl@"+boringsslModVer).CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to download boringssl: %s", err)
|
t.Fatalf("failed to download boringssl: %s", err)
|
||||||
@ -263,6 +305,8 @@ func TestBogoSuite(t *testing.T) {
|
|||||||
"-shim-extra-flags=-bogo-mode",
|
"-shim-extra-flags=-bogo-mode",
|
||||||
"-allow-unimplemented",
|
"-allow-unimplemented",
|
||||||
"-loose-errors", // TODO(roland): this should be removed eventually
|
"-loose-errors", // TODO(roland): this should be removed eventually
|
||||||
|
"-pipe",
|
||||||
|
"-v",
|
||||||
}
|
}
|
||||||
if *bogoFilter != "" {
|
if *bogoFilter != "" {
|
||||||
args = append(args, fmt.Sprintf("-test=%s", *bogoFilter))
|
args = append(args, fmt.Sprintf("-test=%s", *bogoFilter))
|
||||||
@ -273,10 +317,22 @@ func TestBogoSuite(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
cmd := exec.Command(goCmd, args...)
|
cmd := exec.Command(goCmd, args...)
|
||||||
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
|
out := &strings.Builder{}
|
||||||
|
cmd.Stdout, cmd.Stderr = io.MultiWriter(os.Stdout, out), os.Stderr
|
||||||
cmd.Dir = filepath.Join(j.Dir, "ssl/test/runner")
|
cmd.Dir = filepath.Join(j.Dir, "ssl/test/runner")
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("bogo failed: %s", err)
|
t.Fatalf("bogo failed: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *bogoFilter == "" {
|
||||||
|
assertPass := func(t *testing.T, name string) {
|
||||||
|
t.Helper()
|
||||||
|
if !strings.Contains(out.String(), "PASSED ("+name+")\n") {
|
||||||
|
t.Errorf("Expected test %s did not run", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertPass(t, "CurveTest-Client-Kyber-TLS13")
|
||||||
|
assertPass(t, "CurveTest-Server-Kyber-TLS13")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,6 +165,10 @@ func TestBoringServerCurves(t *testing.T) {
|
|||||||
t.Run(fmt.Sprintf("curve=%d", curveid), func(t *testing.T) {
|
t.Run(fmt.Sprintf("curve=%d", curveid), func(t *testing.T) {
|
||||||
clientConfig := testConfig.Clone()
|
clientConfig := testConfig.Clone()
|
||||||
clientConfig.CurvePreferences = []CurveID{curveid}
|
clientConfig.CurvePreferences = []CurveID{curveid}
|
||||||
|
if curveid == x25519Kyber768Draft00 {
|
||||||
|
// x25519Kyber768Draft00 is not supported standalone.
|
||||||
|
clientConfig.CurvePreferences = append(clientConfig.CurvePreferences, X25519)
|
||||||
|
}
|
||||||
if _, _, err := testHandshake(t, clientConfig, serverConfig); err != nil {
|
if _, _, err := testHandshake(t, clientConfig, serverConfig); err != nil {
|
||||||
t.Fatalf("got error: %v, expected success", err)
|
t.Fatalf("got error: %v, expected success", err)
|
||||||
}
|
}
|
||||||
|
@ -130,11 +130,13 @@ const (
|
|||||||
scsvRenegotiation uint16 = 0x00ff
|
scsvRenegotiation uint16 = 0x00ff
|
||||||
)
|
)
|
||||||
|
|
||||||
// CurveID is the type of a TLS identifier for an elliptic curve. See
|
// CurveID is the type of a TLS identifier for a key exchange mechanism. See
|
||||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8.
|
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8.
|
||||||
//
|
//
|
||||||
// In TLS 1.3, this type is called NamedGroup, but at this time this library
|
// In TLS 1.2, this registry used to support only elliptic curves. In TLS 1.3,
|
||||||
// only supports Elliptic Curve based groups. See RFC 8446, Section 4.2.7.
|
// it was extended to other groups and renamed NamedGroup. See RFC 8446, Section
|
||||||
|
// 4.2.7. It was then also extended to other mechanisms, such as hybrid
|
||||||
|
// post-quantum KEMs.
|
||||||
type CurveID uint16
|
type CurveID uint16
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -142,6 +144,11 @@ const (
|
|||||||
CurveP384 CurveID = 24
|
CurveP384 CurveID = 24
|
||||||
CurveP521 CurveID = 25
|
CurveP521 CurveID = 25
|
||||||
X25519 CurveID = 29
|
X25519 CurveID = 29
|
||||||
|
|
||||||
|
// Experimental codepoint for X25519Kyber768Draft00, specified in
|
||||||
|
// draft-tls-westerbaan-xyber768d00-03. Not exported, as support might be
|
||||||
|
// removed in the future.
|
||||||
|
x25519Kyber768Draft00 CurveID = 0x6399 // X25519Kyber768Draft00
|
||||||
)
|
)
|
||||||
|
|
||||||
// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8.
|
// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8.
|
||||||
@ -302,6 +309,10 @@ type ConnectionState struct {
|
|||||||
|
|
||||||
// testingOnlyDidHRR is true if a HelloRetryRequest was sent/received.
|
// testingOnlyDidHRR is true if a HelloRetryRequest was sent/received.
|
||||||
testingOnlyDidHRR bool
|
testingOnlyDidHRR bool
|
||||||
|
|
||||||
|
// testingOnlyCurveID is the selected CurveID, or zero if an RSA exchanges
|
||||||
|
// is performed.
|
||||||
|
testingOnlyCurveID CurveID
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportKeyingMaterial returns length bytes of exported key material in a new
|
// ExportKeyingMaterial returns length bytes of exported key material in a new
|
||||||
@ -375,7 +386,7 @@ type ClientSessionCache interface {
|
|||||||
Put(sessionKey string, cs *ClientSessionState)
|
Put(sessionKey string, cs *ClientSessionState)
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate stringer -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go
|
//go:generate stringer -linecomment -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go
|
||||||
|
|
||||||
// SignatureScheme identifies a signature algorithm supported by TLS. See
|
// SignatureScheme identifies a signature algorithm supported by TLS. See
|
||||||
// RFC 8446, Section 4.2.3.
|
// RFC 8446, Section 4.2.3.
|
||||||
@ -757,6 +768,10 @@ type Config struct {
|
|||||||
// an ECDHE handshake, in preference order. If empty, the default will
|
// an ECDHE handshake, in preference order. If empty, the default will
|
||||||
// be used. The client will use the first preference as the type for
|
// be used. The client will use the first preference as the type for
|
||||||
// its key share in TLS 1.3. This may change in the future.
|
// its key share in TLS 1.3. This may change in the future.
|
||||||
|
//
|
||||||
|
// From Go 1.23, the default includes the X25519Kyber768Draft00 hybrid
|
||||||
|
// post-quantum key exchange. To disable it, set CurvePreferences explicitly
|
||||||
|
// or use the GODEBUG=tlskyber=0 environment variable.
|
||||||
CurvePreferences []CurveID
|
CurvePreferences []CurveID
|
||||||
|
|
||||||
// DynamicRecordSizingDisabled disables adaptive sizing of TLS records.
|
// DynamicRecordSizingDisabled disables adaptive sizing of TLS records.
|
||||||
@ -1084,20 +1099,27 @@ func supportedVersionsFromMax(maxVersion uint16) []uint16 {
|
|||||||
return versions
|
return versions
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultCurvePreferences = []CurveID{X25519, CurveP256, CurveP384, CurveP521}
|
var tlskyber = godebug.New("tlskyber")
|
||||||
|
|
||||||
func (c *Config) curvePreferences() []CurveID {
|
var defaultCurvePreferences = []CurveID{x25519Kyber768Draft00, X25519, CurveP256, CurveP384, CurveP521}
|
||||||
|
|
||||||
|
var defaultCurvePreferencesWithoutKyber = []CurveID{X25519, CurveP256, CurveP384, CurveP521}
|
||||||
|
|
||||||
|
func (c *Config) curvePreferences(version uint16) []CurveID {
|
||||||
if needFIPS() {
|
if needFIPS() {
|
||||||
return fipsCurvePreferences(c)
|
return fipsCurvePreferences(c)
|
||||||
}
|
}
|
||||||
if c == nil || len(c.CurvePreferences) == 0 {
|
if c == nil || len(c.CurvePreferences) == 0 {
|
||||||
|
if version < VersionTLS13 || tlskyber.Value() == "0" {
|
||||||
|
return defaultCurvePreferencesWithoutKyber
|
||||||
|
}
|
||||||
return defaultCurvePreferences
|
return defaultCurvePreferences
|
||||||
}
|
}
|
||||||
return c.CurvePreferences
|
return c.CurvePreferences
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) supportsCurve(curve CurveID) bool {
|
func (c *Config) supportsCurve(version uint16, curve CurveID) bool {
|
||||||
for _, cc := range c.curvePreferences() {
|
for _, cc := range c.curvePreferences(version) {
|
||||||
if cc == curve {
|
if cc == curve {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -1256,7 +1278,7 @@ func (chi *ClientHelloInfo) SupportsCertificate(c *Certificate) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The only signed key exchange we support is ECDHE.
|
// The only signed key exchange we support is ECDHE.
|
||||||
if !supportsECDHE(config, chi.SupportedCurves, chi.SupportedPoints) {
|
if !supportsECDHE(config, vers, chi.SupportedCurves, chi.SupportedPoints) {
|
||||||
return supportsRSAFallback(errors.New("client doesn't support ECDHE, can only use legacy RSA key exchange"))
|
return supportsRSAFallback(errors.New("client doesn't support ECDHE, can only use legacy RSA key exchange"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1277,7 +1299,7 @@ func (chi *ClientHelloInfo) SupportsCertificate(c *Certificate) error {
|
|||||||
}
|
}
|
||||||
var curveOk bool
|
var curveOk bool
|
||||||
for _, c := range chi.SupportedCurves {
|
for _, c := range chi.SupportedCurves {
|
||||||
if c == curve && config.supportsCurve(c) {
|
if c == curve && config.supportsCurve(vers, c) {
|
||||||
curveOk = true
|
curveOk = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Code generated by "stringer -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go"; DO NOT EDIT.
|
// Code generated by "stringer -linecomment -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go"; DO NOT EDIT.
|
||||||
|
|
||||||
package tls
|
package tls
|
||||||
|
|
||||||
@ -71,11 +71,13 @@ func _() {
|
|||||||
_ = x[CurveP384-24]
|
_ = x[CurveP384-24]
|
||||||
_ = x[CurveP521-25]
|
_ = x[CurveP521-25]
|
||||||
_ = x[X25519-29]
|
_ = x[X25519-29]
|
||||||
|
_ = x[x25519Kyber768Draft00-25497]
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_CurveID_name_0 = "CurveP256CurveP384CurveP521"
|
_CurveID_name_0 = "CurveP256CurveP384CurveP521"
|
||||||
_CurveID_name_1 = "X25519"
|
_CurveID_name_1 = "X25519"
|
||||||
|
_CurveID_name_2 = "X25519Kyber768Draft00"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -89,6 +91,8 @@ func (i CurveID) String() string {
|
|||||||
return _CurveID_name_0[_CurveID_index_0[i]:_CurveID_index_0[i+1]]
|
return _CurveID_name_0[_CurveID_index_0[i]:_CurveID_index_0[i+1]]
|
||||||
case i == 29:
|
case i == 29:
|
||||||
return _CurveID_name_1
|
return _CurveID_name_1
|
||||||
|
case i == 25497:
|
||||||
|
return _CurveID_name_2
|
||||||
default:
|
default:
|
||||||
return "CurveID(" + strconv.FormatInt(int64(i), 10) + ")"
|
return "CurveID(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ type Conn struct {
|
|||||||
didResume bool // whether this connection was a session resumption
|
didResume bool // whether this connection was a session resumption
|
||||||
didHRR bool // whether a HelloRetryRequest was sent/received
|
didHRR bool // whether a HelloRetryRequest was sent/received
|
||||||
cipherSuite uint16
|
cipherSuite uint16
|
||||||
|
curveID CurveID
|
||||||
ocspResponse []byte // stapled OCSP response
|
ocspResponse []byte // stapled OCSP response
|
||||||
scts [][]byte // signed certificate timestamps from server
|
scts [][]byte // signed certificate timestamps from server
|
||||||
peerCertificates []*x509.Certificate
|
peerCertificates []*x509.Certificate
|
||||||
@ -1610,6 +1611,8 @@ func (c *Conn) connectionStateLocked() ConnectionState {
|
|||||||
state.NegotiatedProtocol = c.clientProtocol
|
state.NegotiatedProtocol = c.clientProtocol
|
||||||
state.DidResume = c.didResume
|
state.DidResume = c.didResume
|
||||||
state.testingOnlyDidHRR = c.didHRR
|
state.testingOnlyDidHRR = c.didHRR
|
||||||
|
// c.curveID is not set on TLS 1.0–1.2 resumptions. Fix that before exposing it.
|
||||||
|
state.testingOnlyCurveID = c.curveID
|
||||||
state.NegotiatedProtocolIsMutual = true
|
state.NegotiatedProtocolIsMutual = true
|
||||||
state.ServerName = c.serverName
|
state.ServerName = c.serverName
|
||||||
state.CipherSuite = c.cipherSuite
|
state.CipherSuite = c.cipherSuite
|
||||||
|
@ -8,15 +8,16 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdh"
|
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
|
"crypto/internal/mlkem768"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
|
"internal/byteorder"
|
||||||
"internal/godebug"
|
"internal/godebug"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@ -39,7 +40,7 @@ type clientHandshakeState struct {
|
|||||||
|
|
||||||
var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme
|
var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme
|
||||||
|
|
||||||
func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) {
|
func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, error) {
|
||||||
config := c.config
|
config := c.config
|
||||||
if len(config.ServerName) == 0 && !config.InsecureSkipVerify {
|
if len(config.ServerName) == 0 && !config.InsecureSkipVerify {
|
||||||
return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
|
return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
|
||||||
@ -61,30 +62,30 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) {
|
|||||||
if len(supportedVersions) == 0 {
|
if len(supportedVersions) == 0 {
|
||||||
return nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion")
|
return nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion")
|
||||||
}
|
}
|
||||||
|
maxVersion := config.maxSupportedVersion(roleClient)
|
||||||
clientHelloVersion := config.maxSupportedVersion(roleClient)
|
|
||||||
// The version at the beginning of the ClientHello was capped at TLS 1.2
|
|
||||||
// for compatibility reasons. The supported_versions extension is used
|
|
||||||
// to negotiate versions now. See RFC 8446, Section 4.2.1.
|
|
||||||
if clientHelloVersion > VersionTLS12 {
|
|
||||||
clientHelloVersion = VersionTLS12
|
|
||||||
}
|
|
||||||
|
|
||||||
hello := &clientHelloMsg{
|
hello := &clientHelloMsg{
|
||||||
vers: clientHelloVersion,
|
vers: maxVersion,
|
||||||
compressionMethods: []uint8{compressionNone},
|
compressionMethods: []uint8{compressionNone},
|
||||||
random: make([]byte, 32),
|
random: make([]byte, 32),
|
||||||
extendedMasterSecret: true,
|
extendedMasterSecret: true,
|
||||||
ocspStapling: true,
|
ocspStapling: true,
|
||||||
scts: true,
|
scts: true,
|
||||||
serverName: hostnameInSNI(config.ServerName),
|
serverName: hostnameInSNI(config.ServerName),
|
||||||
supportedCurves: config.curvePreferences(),
|
supportedCurves: config.curvePreferences(maxVersion),
|
||||||
supportedPoints: []uint8{pointFormatUncompressed},
|
supportedPoints: []uint8{pointFormatUncompressed},
|
||||||
secureRenegotiationSupported: true,
|
secureRenegotiationSupported: true,
|
||||||
alpnProtocols: config.NextProtos,
|
alpnProtocols: config.NextProtos,
|
||||||
supportedVersions: supportedVersions,
|
supportedVersions: supportedVersions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The version at the beginning of the ClientHello was capped at TLS 1.2
|
||||||
|
// for compatibility reasons. The supported_versions extension is used
|
||||||
|
// to negotiate versions now. See RFC 8446, Section 4.2.1.
|
||||||
|
if hello.vers > VersionTLS12 {
|
||||||
|
hello.vers = VersionTLS12
|
||||||
|
}
|
||||||
|
|
||||||
if c.handshakes > 0 {
|
if c.handshakes > 0 {
|
||||||
hello.secureRenegotiation = c.clientFinished[:]
|
hello.secureRenegotiation = c.clientFinished[:]
|
||||||
}
|
}
|
||||||
@ -103,7 +104,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) {
|
|||||||
}
|
}
|
||||||
// Don't advertise TLS 1.2-only cipher suites unless
|
// Don't advertise TLS 1.2-only cipher suites unless
|
||||||
// we're attempting TLS 1.2.
|
// we're attempting TLS 1.2.
|
||||||
if hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 {
|
if maxVersion < VersionTLS12 && suite.flags&suiteTLS12 != 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
hello.cipherSuites = append(hello.cipherSuites, suiteId)
|
hello.cipherSuites = append(hello.cipherSuites, suiteId)
|
||||||
@ -126,14 +127,14 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if hello.vers >= VersionTLS12 {
|
if maxVersion >= VersionTLS12 {
|
||||||
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
|
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
|
||||||
}
|
}
|
||||||
if testingOnlyForceClientHelloSignatureAlgorithms != nil {
|
if testingOnlyForceClientHelloSignatureAlgorithms != nil {
|
||||||
hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms
|
hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms
|
||||||
}
|
}
|
||||||
|
|
||||||
var key *ecdh.PrivateKey
|
var keyShareKeys *keySharePrivateKeys
|
||||||
if hello.supportedVersions[0] == VersionTLS13 {
|
if hello.supportedVersions[0] == VersionTLS13 {
|
||||||
// Reset the list of ciphers when the client only supports TLS 1.3.
|
// Reset the list of ciphers when the client only supports TLS 1.3.
|
||||||
if len(hello.supportedVersions) == 1 {
|
if len(hello.supportedVersions) == 1 {
|
||||||
@ -145,15 +146,40 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) {
|
|||||||
hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13NoAES...)
|
hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13NoAES...)
|
||||||
}
|
}
|
||||||
|
|
||||||
curveID := config.curvePreferences()[0]
|
curveID := config.curvePreferences(maxVersion)[0]
|
||||||
if _, ok := curveForCurveID(curveID); !ok {
|
keyShareKeys = &keySharePrivateKeys{curveID: curveID}
|
||||||
return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve")
|
if curveID == x25519Kyber768Draft00 {
|
||||||
|
keyShareKeys.ecdhe, err = generateECDHEKey(config.rand(), X25519)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
seed := make([]byte, mlkem768.SeedSize)
|
||||||
|
if _, err := io.ReadFull(config.rand(), seed); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
keyShareKeys.kyber, err = mlkem768.NewKeyFromSeed(seed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// For draft-tls-westerbaan-xyber768d00-03, we send both a hybrid
|
||||||
|
// and a standard X25519 key share, since most servers will only
|
||||||
|
// support the latter. We reuse the same X25519 ephemeral key for
|
||||||
|
// both, as allowed by draft-ietf-tls-hybrid-design-09, Section 3.2.
|
||||||
|
hello.keyShares = []keyShare{
|
||||||
|
{group: x25519Kyber768Draft00, data: append(keyShareKeys.ecdhe.PublicKey().Bytes(),
|
||||||
|
keyShareKeys.kyber.EncapsulationKey()...)},
|
||||||
|
{group: X25519, data: keyShareKeys.ecdhe.PublicKey().Bytes()},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, ok := curveForCurveID(curveID); !ok {
|
||||||
|
return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve")
|
||||||
|
}
|
||||||
|
keyShareKeys.ecdhe, err = generateECDHEKey(config.rand(), curveID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
hello.keyShares = []keyShare{{group: curveID, data: keyShareKeys.ecdhe.PublicKey().Bytes()}}
|
||||||
}
|
}
|
||||||
key, err = generateECDHEKey(config.rand(), curveID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
hello.keyShares = []keyShare{{group: curveID, data: key.PublicKey().Bytes()}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.quic != nil {
|
if c.quic != nil {
|
||||||
@ -167,7 +193,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) {
|
|||||||
hello.quicTransportParameters = p
|
hello.quicTransportParameters = p
|
||||||
}
|
}
|
||||||
|
|
||||||
return hello, key, nil
|
return hello, keyShareKeys, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) clientHandshake(ctx context.Context) (err error) {
|
func (c *Conn) clientHandshake(ctx context.Context) (err error) {
|
||||||
@ -179,7 +205,7 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
|
|||||||
// need to be reset.
|
// need to be reset.
|
||||||
c.didResume = false
|
c.didResume = false
|
||||||
|
|
||||||
hello, ecdheKey, err := c.makeClientHello()
|
hello, keyShareKeys, err := c.makeClientHello()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -249,17 +275,15 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
|
|||||||
|
|
||||||
if c.vers == VersionTLS13 {
|
if c.vers == VersionTLS13 {
|
||||||
hs := &clientHandshakeStateTLS13{
|
hs := &clientHandshakeStateTLS13{
|
||||||
c: c,
|
c: c,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
serverHello: serverHello,
|
serverHello: serverHello,
|
||||||
hello: hello,
|
hello: hello,
|
||||||
ecdheKey: ecdheKey,
|
keyShareKeys: keyShareKeys,
|
||||||
session: session,
|
session: session,
|
||||||
earlySecret: earlySecret,
|
earlySecret: earlySecret,
|
||||||
binderKey: binderKey,
|
binderKey: binderKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
// In TLS 1.3, session tickets are delivered after the handshake.
|
|
||||||
return hs.handshake()
|
return hs.handshake()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,12 +294,7 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
|
|||||||
hello: hello,
|
hello: hello,
|
||||||
session: session,
|
session: session,
|
||||||
}
|
}
|
||||||
|
return hs.handshake()
|
||||||
if err := hs.handshake(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) loadSession(hello *clientHelloMsg) (
|
func (c *Conn) loadSession(hello *clientHelloMsg) (
|
||||||
@ -603,6 +622,9 @@ func (hs *clientHandshakeState) doFullHandshake() error {
|
|||||||
c.sendAlert(alertUnexpectedMessage)
|
c.sendAlert(alertUnexpectedMessage)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(skx.key) >= 3 && skx.key[0] == 3 /* named curve */ {
|
||||||
|
c.curveID = CurveID(byteorder.BeUint16(skx.key[1:]))
|
||||||
|
}
|
||||||
|
|
||||||
msg, err = c.readHandshake(&hs.finishedHash)
|
msg, err = c.readHandshake(&hs.finishedHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -8,20 +8,21 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdh"
|
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
|
"crypto/internal/mlkem768"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"errors"
|
"errors"
|
||||||
"hash"
|
"hash"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type clientHandshakeStateTLS13 struct {
|
type clientHandshakeStateTLS13 struct {
|
||||||
c *Conn
|
c *Conn
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
serverHello *serverHelloMsg
|
serverHello *serverHelloMsg
|
||||||
hello *clientHelloMsg
|
hello *clientHelloMsg
|
||||||
ecdheKey *ecdh.PrivateKey
|
keyShareKeys *keySharePrivateKeys
|
||||||
|
|
||||||
session *SessionState
|
session *SessionState
|
||||||
earlySecret []byte
|
earlySecret []byte
|
||||||
@ -36,7 +37,7 @@ type clientHandshakeStateTLS13 struct {
|
|||||||
trafficSecret []byte // client_application_traffic_secret_0
|
trafficSecret []byte // client_application_traffic_secret_0
|
||||||
}
|
}
|
||||||
|
|
||||||
// handshake requires hs.c, hs.hello, hs.serverHello, hs.ecdheKey, and,
|
// handshake requires hs.c, hs.hello, hs.serverHello, hs.keyShareKeys, and,
|
||||||
// optionally, hs.session, hs.earlySecret and hs.binderKey to be set.
|
// optionally, hs.session, hs.earlySecret and hs.binderKey to be set.
|
||||||
func (hs *clientHandshakeStateTLS13) handshake() error {
|
func (hs *clientHandshakeStateTLS13) handshake() error {
|
||||||
c := hs.c
|
c := hs.c
|
||||||
@ -53,7 +54,7 @@ func (hs *clientHandshakeStateTLS13) handshake() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Consistency check on the presence of a keyShare and its parameters.
|
// Consistency check on the presence of a keyShare and its parameters.
|
||||||
if hs.ecdheKey == nil || len(hs.hello.keyShares) != 1 {
|
if hs.keyShareKeys == nil || hs.keyShareKeys.ecdhe == nil || len(hs.hello.keyShares) == 0 {
|
||||||
return c.sendAlert(alertInternalError)
|
return c.sendAlert(alertInternalError)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,21 +222,22 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
|
|||||||
// a group we advertised but did not send a key share for, and send a key
|
// a group we advertised but did not send a key share for, and send a key
|
||||||
// share for it this time.
|
// share for it this time.
|
||||||
if curveID := hs.serverHello.selectedGroup; curveID != 0 {
|
if curveID := hs.serverHello.selectedGroup; curveID != 0 {
|
||||||
curveOK := false
|
if !slices.Contains(hs.hello.supportedCurves, curveID) {
|
||||||
for _, id := range hs.hello.supportedCurves {
|
|
||||||
if id == curveID {
|
|
||||||
curveOK = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !curveOK {
|
|
||||||
c.sendAlert(alertIllegalParameter)
|
c.sendAlert(alertIllegalParameter)
|
||||||
return errors.New("tls: server selected unsupported group")
|
return errors.New("tls: server selected unsupported group")
|
||||||
}
|
}
|
||||||
if sentID, _ := curveIDForCurve(hs.ecdheKey.Curve()); sentID == curveID {
|
if slices.ContainsFunc(hs.hello.keyShares, func(ks keyShare) bool {
|
||||||
|
return ks.group == curveID
|
||||||
|
}) {
|
||||||
c.sendAlert(alertIllegalParameter)
|
c.sendAlert(alertIllegalParameter)
|
||||||
return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
|
return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
|
||||||
}
|
}
|
||||||
|
// Note: we don't support selecting X25519Kyber768Draft00 in a HRR,
|
||||||
|
// because we currently only support it at all when CurvePreferences is
|
||||||
|
// empty, which will cause us to also send a key share for it.
|
||||||
|
//
|
||||||
|
// This will have to change once we support selecting hybrid KEMs
|
||||||
|
// without sending key shares for them.
|
||||||
if _, ok := curveForCurveID(curveID); !ok {
|
if _, ok := curveForCurveID(curveID); !ok {
|
||||||
c.sendAlert(alertInternalError)
|
c.sendAlert(alertInternalError)
|
||||||
return errors.New("tls: CurvePreferences includes unsupported curve")
|
return errors.New("tls: CurvePreferences includes unsupported curve")
|
||||||
@ -245,7 +247,7 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
|
|||||||
c.sendAlert(alertInternalError)
|
c.sendAlert(alertInternalError)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hs.ecdheKey = key
|
hs.keyShareKeys = &keySharePrivateKeys{curveID: curveID, ecdhe: key}
|
||||||
hs.hello.keyShares = []keyShare{{group: curveID, data: key.PublicKey().Bytes()}}
|
hs.hello.keyShares = []keyShare{{group: curveID, data: key.PublicKey().Bytes()}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,7 +335,9 @@ func (hs *clientHandshakeStateTLS13) processServerHello() error {
|
|||||||
c.sendAlert(alertIllegalParameter)
|
c.sendAlert(alertIllegalParameter)
|
||||||
return errors.New("tls: server did not send a key share")
|
return errors.New("tls: server did not send a key share")
|
||||||
}
|
}
|
||||||
if sentID, _ := curveIDForCurve(hs.ecdheKey.Curve()); hs.serverHello.serverShare.group != sentID {
|
if !slices.ContainsFunc(hs.hello.keyShares, func(ks keyShare) bool {
|
||||||
|
return ks.group == hs.serverHello.serverShare.group
|
||||||
|
}) {
|
||||||
c.sendAlert(alertIllegalParameter)
|
c.sendAlert(alertIllegalParameter)
|
||||||
return errors.New("tls: server selected unsupported group")
|
return errors.New("tls: server selected unsupported group")
|
||||||
}
|
}
|
||||||
@ -372,16 +376,37 @@ func (hs *clientHandshakeStateTLS13) processServerHello() error {
|
|||||||
func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
|
func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
|
||||||
c := hs.c
|
c := hs.c
|
||||||
|
|
||||||
peerKey, err := hs.ecdheKey.Curve().NewPublicKey(hs.serverHello.serverShare.data)
|
ecdhePeerData := hs.serverHello.serverShare.data
|
||||||
|
if hs.serverHello.serverShare.group == x25519Kyber768Draft00 {
|
||||||
|
if len(ecdhePeerData) != x25519PublicKeySize+mlkem768.CiphertextSize {
|
||||||
|
c.sendAlert(alertIllegalParameter)
|
||||||
|
return errors.New("tls: invalid server key share")
|
||||||
|
}
|
||||||
|
ecdhePeerData = hs.serverHello.serverShare.data[:x25519PublicKeySize]
|
||||||
|
}
|
||||||
|
peerKey, err := hs.keyShareKeys.ecdhe.Curve().NewPublicKey(ecdhePeerData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.sendAlert(alertIllegalParameter)
|
c.sendAlert(alertIllegalParameter)
|
||||||
return errors.New("tls: invalid server key share")
|
return errors.New("tls: invalid server key share")
|
||||||
}
|
}
|
||||||
sharedKey, err := hs.ecdheKey.ECDH(peerKey)
|
sharedKey, err := hs.keyShareKeys.ecdhe.ECDH(peerKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.sendAlert(alertIllegalParameter)
|
c.sendAlert(alertIllegalParameter)
|
||||||
return errors.New("tls: invalid server key share")
|
return errors.New("tls: invalid server key share")
|
||||||
}
|
}
|
||||||
|
if hs.serverHello.serverShare.group == x25519Kyber768Draft00 {
|
||||||
|
if hs.keyShareKeys.kyber == nil {
|
||||||
|
return c.sendAlert(alertInternalError)
|
||||||
|
}
|
||||||
|
ciphertext := hs.serverHello.serverShare.data[x25519PublicKeySize:]
|
||||||
|
kyberShared, err := kyberDecapsulate(hs.keyShareKeys.kyber, ciphertext)
|
||||||
|
if err != nil {
|
||||||
|
c.sendAlert(alertIllegalParameter)
|
||||||
|
return errors.New("tls: invalid Kyber server key share")
|
||||||
|
}
|
||||||
|
sharedKey = append(sharedKey, kyberShared...)
|
||||||
|
}
|
||||||
|
c.curveID = hs.serverHello.serverShare.group
|
||||||
|
|
||||||
earlySecret := hs.earlySecret
|
earlySecret := hs.earlySecret
|
||||||
if !hs.usingPSK {
|
if !hs.usingPSK {
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
|
"internal/byteorder"
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -247,7 +248,7 @@ func (hs *serverHandshakeState) processClientHello() error {
|
|||||||
hs.hello.scts = hs.cert.SignedCertificateTimestamps
|
hs.hello.scts = hs.cert.SignedCertificateTimestamps
|
||||||
}
|
}
|
||||||
|
|
||||||
hs.ecdheOk = supportsECDHE(c.config, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints)
|
hs.ecdheOk = supportsECDHE(c.config, c.vers, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints)
|
||||||
|
|
||||||
if hs.ecdheOk && len(hs.clientHello.supportedPoints) > 0 {
|
if hs.ecdheOk && len(hs.clientHello.supportedPoints) > 0 {
|
||||||
// Although omitting the ec_point_formats extension is permitted, some
|
// Although omitting the ec_point_formats extension is permitted, some
|
||||||
@ -318,10 +319,10 @@ func negotiateALPN(serverProtos, clientProtos []string, quic bool) (string, erro
|
|||||||
|
|
||||||
// supportsECDHE returns whether ECDHE key exchanges can be used with this
|
// supportsECDHE returns whether ECDHE key exchanges can be used with this
|
||||||
// pre-TLS 1.3 client.
|
// pre-TLS 1.3 client.
|
||||||
func supportsECDHE(c *Config, supportedCurves []CurveID, supportedPoints []uint8) bool {
|
func supportsECDHE(c *Config, version uint16, supportedCurves []CurveID, supportedPoints []uint8) bool {
|
||||||
supportsCurve := false
|
supportsCurve := false
|
||||||
for _, curve := range supportedCurves {
|
for _, curve := range supportedCurves {
|
||||||
if c.supportsCurve(curve) {
|
if c.supportsCurve(version, curve) {
|
||||||
supportsCurve = true
|
supportsCurve = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -587,6 +588,9 @@ func (hs *serverHandshakeState) doFullHandshake() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if skx != nil {
|
if skx != nil {
|
||||||
|
if len(skx.key) >= 3 && skx.key[0] == 3 /* named curve */ {
|
||||||
|
c.curveID = CurveID(byteorder.BeUint16(skx.key[1:]))
|
||||||
|
}
|
||||||
if _, err := hs.c.writeHandshakeRecord(skx, &hs.finishedHash); err != nil {
|
if _, err := hs.c.writeHandshakeRecord(skx, &hs.finishedHash); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,7 @@ func testClientHelloFailure(t *testing.T, serverConfig *Config, m handshakeMessa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.Close()
|
s.Close()
|
||||||
|
t.Helper()
|
||||||
if len(expectedSubStr) == 0 {
|
if len(expectedSubStr) == 0 {
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
t.Errorf("Got error: %s; expected to succeed", err)
|
t.Errorf("Got error: %s; expected to succeed", err)
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
|
"crypto/internal/mlkem768"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"errors"
|
"errors"
|
||||||
"hash"
|
"hash"
|
||||||
@ -178,11 +179,11 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error {
|
|||||||
hs.hello.cipherSuite = hs.suite.id
|
hs.hello.cipherSuite = hs.suite.id
|
||||||
hs.transcript = hs.suite.hash.New()
|
hs.transcript = hs.suite.hash.New()
|
||||||
|
|
||||||
// Pick the ECDHE group in server preference order, but give priority to
|
// Pick the key exchange method in server preference order, but give
|
||||||
// groups with a key share, to avoid a HelloRetryRequest round-trip.
|
// priority to key shares, to avoid a HelloRetryRequest round-trip.
|
||||||
var selectedGroup CurveID
|
var selectedGroup CurveID
|
||||||
var clientKeyShare *keyShare
|
var clientKeyShare *keyShare
|
||||||
preferredGroups := c.config.curvePreferences()
|
preferredGroups := c.config.curvePreferences(c.vers)
|
||||||
for _, preferredGroup := range preferredGroups {
|
for _, preferredGroup := range preferredGroups {
|
||||||
ki := slices.IndexFunc(hs.clientHello.keyShares, func(ks keyShare) bool {
|
ki := slices.IndexFunc(hs.clientHello.keyShares, func(ks keyShare) bool {
|
||||||
return ks.group == preferredGroup
|
return ks.group == preferredGroup
|
||||||
@ -210,23 +211,35 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error {
|
|||||||
return errors.New("tls: no ECDHE curve supported by both client and server")
|
return errors.New("tls: no ECDHE curve supported by both client and server")
|
||||||
}
|
}
|
||||||
if clientKeyShare == nil {
|
if clientKeyShare == nil {
|
||||||
if err := hs.doHelloRetryRequest(selectedGroup); err != nil {
|
ks, err := hs.doHelloRetryRequest(selectedGroup)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
clientKeyShare = &hs.clientHello.keyShares[0]
|
clientKeyShare = ks
|
||||||
}
|
}
|
||||||
|
c.curveID = selectedGroup
|
||||||
|
|
||||||
if _, ok := curveForCurveID(selectedGroup); !ok {
|
ecdhGroup := selectedGroup
|
||||||
|
ecdhData := clientKeyShare.data
|
||||||
|
if selectedGroup == x25519Kyber768Draft00 {
|
||||||
|
ecdhGroup = X25519
|
||||||
|
if len(ecdhData) != x25519PublicKeySize+mlkem768.EncapsulationKeySize {
|
||||||
|
c.sendAlert(alertIllegalParameter)
|
||||||
|
return errors.New("tls: invalid Kyber client key share")
|
||||||
|
}
|
||||||
|
ecdhData = ecdhData[:x25519PublicKeySize]
|
||||||
|
}
|
||||||
|
if _, ok := curveForCurveID(ecdhGroup); !ok {
|
||||||
c.sendAlert(alertInternalError)
|
c.sendAlert(alertInternalError)
|
||||||
return errors.New("tls: CurvePreferences includes unsupported curve")
|
return errors.New("tls: CurvePreferences includes unsupported curve")
|
||||||
}
|
}
|
||||||
key, err := generateECDHEKey(c.config.rand(), selectedGroup)
|
key, err := generateECDHEKey(c.config.rand(), ecdhGroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.sendAlert(alertInternalError)
|
c.sendAlert(alertInternalError)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hs.hello.serverShare = keyShare{group: selectedGroup, data: key.PublicKey().Bytes()}
|
hs.hello.serverShare = keyShare{group: selectedGroup, data: key.PublicKey().Bytes()}
|
||||||
peerKey, err := key.Curve().NewPublicKey(clientKeyShare.data)
|
peerKey, err := key.Curve().NewPublicKey(ecdhData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.sendAlert(alertIllegalParameter)
|
c.sendAlert(alertIllegalParameter)
|
||||||
return errors.New("tls: invalid client key share")
|
return errors.New("tls: invalid client key share")
|
||||||
@ -236,6 +249,15 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error {
|
|||||||
c.sendAlert(alertIllegalParameter)
|
c.sendAlert(alertIllegalParameter)
|
||||||
return errors.New("tls: invalid client key share")
|
return errors.New("tls: invalid client key share")
|
||||||
}
|
}
|
||||||
|
if selectedGroup == x25519Kyber768Draft00 {
|
||||||
|
ciphertext, kyberShared, err := kyberEncapsulate(clientKeyShare.data[x25519PublicKeySize:])
|
||||||
|
if err != nil {
|
||||||
|
c.sendAlert(alertIllegalParameter)
|
||||||
|
return errors.New("tls: invalid Kyber client key share")
|
||||||
|
}
|
||||||
|
hs.sharedKey = append(hs.sharedKey, kyberShared...)
|
||||||
|
hs.hello.serverShare.data = append(hs.hello.serverShare.data, ciphertext...)
|
||||||
|
}
|
||||||
|
|
||||||
selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols, c.quic != nil)
|
selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols, c.quic != nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -479,13 +501,13 @@ func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
|
|||||||
return hs.c.writeChangeCipherRecord()
|
return hs.c.writeChangeCipherRecord()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error {
|
func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) (*keyShare, error) {
|
||||||
c := hs.c
|
c := hs.c
|
||||||
|
|
||||||
// The first ClientHello gets double-hashed into the transcript upon a
|
// The first ClientHello gets double-hashed into the transcript upon a
|
||||||
// HelloRetryRequest. See RFC 8446, Section 4.4.1.
|
// HelloRetryRequest. See RFC 8446, Section 4.4.1.
|
||||||
if err := transcriptMsg(hs.clientHello, hs.transcript); err != nil {
|
if err := transcriptMsg(hs.clientHello, hs.transcript); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
chHash := hs.transcript.Sum(nil)
|
chHash := hs.transcript.Sum(nil)
|
||||||
hs.transcript.Reset()
|
hs.transcript.Reset()
|
||||||
@ -503,43 +525,49 @@ func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err := hs.c.writeHandshakeRecord(helloRetryRequest, hs.transcript); err != nil {
|
if _, err := hs.c.writeHandshakeRecord(helloRetryRequest, hs.transcript); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// clientHelloMsg is not included in the transcript.
|
// clientHelloMsg is not included in the transcript.
|
||||||
msg, err := c.readHandshake(nil)
|
msg, err := c.readHandshake(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
clientHello, ok := msg.(*clientHelloMsg)
|
clientHello, ok := msg.(*clientHelloMsg)
|
||||||
if !ok {
|
if !ok {
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
c.sendAlert(alertUnexpectedMessage)
|
||||||
return unexpectedMessageError(clientHello, msg)
|
return nil, unexpectedMessageError(clientHello, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(clientHello.keyShares) != 1 || clientHello.keyShares[0].group != selectedGroup {
|
if len(clientHello.keyShares) != 1 {
|
||||||
c.sendAlert(alertIllegalParameter)
|
c.sendAlert(alertIllegalParameter)
|
||||||
return errors.New("tls: client sent invalid key share in second ClientHello")
|
return nil, errors.New("tls: client didn't send one key share in second ClientHello")
|
||||||
|
}
|
||||||
|
ks := &clientHello.keyShares[0]
|
||||||
|
|
||||||
|
if ks.group != selectedGroup {
|
||||||
|
c.sendAlert(alertIllegalParameter)
|
||||||
|
return nil, errors.New("tls: client sent unexpected key share in second ClientHello")
|
||||||
}
|
}
|
||||||
|
|
||||||
if clientHello.earlyData {
|
if clientHello.earlyData {
|
||||||
c.sendAlert(alertIllegalParameter)
|
c.sendAlert(alertIllegalParameter)
|
||||||
return errors.New("tls: client indicated early data in second ClientHello")
|
return nil, errors.New("tls: client indicated early data in second ClientHello")
|
||||||
}
|
}
|
||||||
|
|
||||||
if illegalClientHelloChange(clientHello, hs.clientHello) {
|
if illegalClientHelloChange(clientHello, hs.clientHello) {
|
||||||
c.sendAlert(alertIllegalParameter)
|
c.sendAlert(alertIllegalParameter)
|
||||||
return errors.New("tls: client illegally modified second ClientHello")
|
return nil, errors.New("tls: client illegally modified second ClientHello")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.didHRR = true
|
c.didHRR = true
|
||||||
hs.clientHello = clientHello
|
hs.clientHello = clientHello
|
||||||
return nil
|
return ks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// illegalClientHelloChange reports whether the two ClientHello messages are
|
// illegalClientHelloChange reports whether the two ClientHello messages are
|
||||||
|
@ -378,6 +378,7 @@ func runMain(m *testing.M) int {
|
|||||||
Certificates: make([]Certificate, 2),
|
Certificates: make([]Certificate, 2),
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
CipherSuites: allCipherSuites(),
|
CipherSuites: allCipherSuites(),
|
||||||
|
CurvePreferences: []CurveID{X25519, CurveP256, CurveP384, CurveP521},
|
||||||
MinVersion: VersionTLS10,
|
MinVersion: VersionTLS10,
|
||||||
MaxVersion: VersionTLS13,
|
MaxVersion: VersionTLS13,
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// a keyAgreement implements the client and server side of a TLS key agreement
|
// A keyAgreement implements the client and server side of a TLS 1.0–1.2 key
|
||||||
// protocol by generating and processing key exchange messages.
|
// agreement protocol by generating and processing key exchange messages.
|
||||||
type keyAgreement interface {
|
type keyAgreement interface {
|
||||||
// On the server side, the first two methods are called in order.
|
// On the server side, the first two methods are called in order.
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ func md5SHA1Hash(slices [][]byte) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// hashForServerKeyExchange hashes the given slices and returns their digest
|
// hashForServerKeyExchange hashes the given slices and returns their digest
|
||||||
// using the given hash function (for >= TLS 1.2) or using a default based on
|
// 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
|
// the sigType (for earlier TLS versions). For Ed25519 signatures, which don't
|
||||||
// do pre-hashing, it returns the concatenation of the slices.
|
// do pre-hashing, it returns the concatenation of the slices.
|
||||||
func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint16, slices ...[]byte) []byte {
|
func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint16, slices ...[]byte) []byte {
|
||||||
@ -169,7 +169,7 @@ type ecdheKeyAgreement struct {
|
|||||||
func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
|
func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
|
||||||
var curveID CurveID
|
var curveID CurveID
|
||||||
for _, c := range clientHello.supportedCurves {
|
for _, c := range clientHello.supportedCurves {
|
||||||
if config.supportsCurve(c) {
|
if config.supportsCurve(ka.version, c) {
|
||||||
curveID = c
|
curveID = c
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ package tls
|
|||||||
import (
|
import (
|
||||||
"crypto/ecdh"
|
"crypto/ecdh"
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
|
"crypto/internal/mlkem768"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
@ -14,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/crypto/cryptobyte"
|
"golang.org/x/crypto/cryptobyte"
|
||||||
"golang.org/x/crypto/hkdf"
|
"golang.org/x/crypto/hkdf"
|
||||||
|
"golang.org/x/crypto/sha3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This file contains the functions necessary to compute the TLS 1.3 key
|
// This file contains the functions necessary to compute the TLS 1.3 key
|
||||||
@ -117,6 +119,45 @@ func (c *cipherSuiteTLS13) exportKeyingMaterial(masterSecret []byte, transcript
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type keySharePrivateKeys struct {
|
||||||
|
curveID CurveID
|
||||||
|
ecdhe *ecdh.PrivateKey
|
||||||
|
kyber *mlkem768.DecapsulationKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// kyberDecapsulate implements decapsulation according to Kyber Round 3.
|
||||||
|
func kyberDecapsulate(dk *mlkem768.DecapsulationKey, c []byte) ([]byte, error) {
|
||||||
|
K, err := mlkem768.Decapsulate(dk, c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return kyberSharedSecret(K, c), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// kyberEncapsulate implements encapsulation according to Kyber Round 3.
|
||||||
|
func kyberEncapsulate(ek []byte) (c, ss []byte, err error) {
|
||||||
|
c, ss, err = mlkem768.Encapsulate(ek)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return c, kyberSharedSecret(ss, c), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func kyberSharedSecret(K, c []byte) []byte {
|
||||||
|
// Package mlkem768 implements ML-KEM, which compared to Kyber removed a
|
||||||
|
// final hashing step. Compute SHAKE-256(K || SHA3-256(c), 32) to match Kyber.
|
||||||
|
// See https://words.filippo.io/mlkem768/#bonus-track-using-a-ml-kem-implementation-as-kyber-v3.
|
||||||
|
h := sha3.NewShake256()
|
||||||
|
h.Write(K)
|
||||||
|
ch := sha3.Sum256(c)
|
||||||
|
h.Write(ch[:])
|
||||||
|
out := make([]byte, 32)
|
||||||
|
h.Read(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
const x25519PublicKeySize = 32
|
||||||
|
|
||||||
// generateECDHEKey returns a PrivateKey that implements Diffie-Hellman
|
// generateECDHEKey returns a PrivateKey that implements Diffie-Hellman
|
||||||
// according to RFC 8446, Section 4.2.8.2.
|
// according to RFC 8446, Section 4.2.8.2.
|
||||||
func generateECDHEKey(rand io.Reader, curveID CurveID) (*ecdh.PrivateKey, error) {
|
func generateECDHEKey(rand io.Reader, curveID CurveID) (*ecdh.PrivateKey, error) {
|
||||||
|
@ -6,6 +6,7 @@ package tls
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/internal/mlkem768"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"hash"
|
"hash"
|
||||||
"strings"
|
"strings"
|
||||||
@ -173,3 +174,39 @@ func TestExtract(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestKyberDecapsulate(t *testing.T) {
|
||||||
|
// From https://pq-crystals.org/kyber/data/kyber-submission-nist-round3.zip
|
||||||
|
dkBytes, _ := hex.DecodeString("07638FB69868F3D320E5862BD96933FEB311B362093C9B5D50170BCED43F1B536D9A204BB1F22695950BA1F2A9E8EB828B284488760B3FC84FABA04275D5628E39C5B2471374283C503299C0AB49B66B8BBB56A4186624F919A2BA59BB08D8551880C2BEFC4F87F25F59AB587A79C327D792D54C974A69262FF8A78938289E9A87B688B083E0595FE218B6BB1505941CE2E81A5A64C5AAC60417256985349EE47A52420A5F97477B7236AC76BC70E8288729287EE3E34A3DBC3683C0B7B10029FC203418537E7466BA6385A8FF301EE12708F82AAA1E380FC7A88F8F205AB7E88D7E95952A55BA20D09B79A47141D62BF6EB7DD307B08ECA13A5BC5F6B68581C6865B27BBCDDAB142F4B2CBFF488C8A22705FAA98A2B9EEA3530C76662335CC7EA3A00777725EBCCCD2A4636B2D9122FF3AB77123CE0883C1911115E50C9E8A94194E48DD0D09CFFB3ADCD2C1E92430903D07ADBF00532031575AA7F9E7B5A1F3362DEC936D4043C05F2476C07578BC9CBAF2AB4E382727AD41686A96B2548820BB03B32F11B2811AD62F489E951632ABA0D1DF89680CC8A8B53B481D92A68D70B4EA1C3A6A561C0692882B5CA8CC942A8D495AFCB06DE89498FB935B775908FE7A03E324D54CC19D4E1AABD3593B38B19EE1388FE492B43127E5A504253786A0D69AD32601C28E2C88504A5BA599706023A61363E17C6B9BB59BDC697452CD059451983D738CA3FD034E3F5988854CA05031DB09611498988197C6B30D258DFE26265541C89A4B31D6864E9389B03CB74F7EC4323FB9421A4B9790A26D17B0398A26767350909F84D57B6694DF830664CA8B3C3C03ED2AE67B89006868A68527CCD666459AB7F056671000C6164D3A7F266A14D97CBD7004D6C92CACA770B844A4FA9B182E7B18CA885082AC5646FCB4A14E1685FEB0C9CE3372AB95365C04FD83084F80A23FF10A05BF15F7FA5ACC6C0CB462C33CA524FA6B8BB359043BA68609EAA2536E81D08463B19653B5435BA946C9ADDEB202B04B031CC960DCC12E4518D428B32B257A4FC7313D3A7980D80082E934F9D95C32B0A0191A23604384DD9E079BBBAA266D14C3F756B9F2133107433A4E83FA7187282A809203A4FAF841851833D121AC383843A5E55BC2381425E16C7DB4CC9AB5C1B0D91A47E2B8DE0E582C86B6B0D907BB360B97F40AB5D038F6B75C814B27D9B968D419832BC8C2BEE605EF6E5059D33100D90485D378450014221736C07407CAC260408AA64926619788B8601C2A752D1A6CBF820D7C7A04716203225B3895B9342D147A8185CFC1BB65BA06B4142339903C0AC4651385B45D98A8B19D28CD6BAB088787F7EE1B12461766B43CBCCB96434427D93C065550688F6948ED1B5475A425F1B85209D061C08B56C1CC069F6C0A7C6F29358CAB911087732A649D27C9B98F9A48879387D9B00C25959A71654D6F6A946164513E47A75D005986C2363C09F6B537ECA78B9303A5FA457608A586A653A347DB04DFCC19175B3A301172536062A658A95277570C8852CA8973F4AE123A334047DD711C8927A634A03388A527B034BF7A8170FA702C1F7C23EC32D18A2374890BE9C787A9409C82D192C4BB705A2F996CE405DA72C2D9C843EE9F8313ECC7F86D6294D59159D9A879A542E260922ADF999051CC45200C9FFDB60449C49465979272367C083A7D6267A3ED7A7FD47957C219327F7CA73A4007E1627F00B11CC80573C15AEE6640FB8562DFA6B240CA0AD351AC4AC155B96C14C8AB13DD262CDFD51C4BB5572FD616553D17BDD430ACBEA3E95F0B698D66990AB51E5D03783A8B3D278A5720454CF9695CFDCA08485BA099C51CD92A7EA7587C1D15C28E609A81852601B0604010679AA482D51261EC36E36B8719676217FD74C54786488F4B4969C05A8BA27CA3A77CCE73B965923CA554E422B9B61F4754641608AC16C9B8587A32C1C5DD788F88B36B717A46965635DEB67F45B129B99070909C93EB80B42C2B3F3F70343A7CF37E8520E7BCFC416ACA4F18C7981262BA2BFC756AE03278F0EC66DC2057696824BA6769865A601D7148EF6F54E5AF5686AA2906F994CE38A5E0B938F239007003022C03392DF3401B1E4A3A7EBC6161449F73374C8B0140369343D9295FDF511845C4A46EBAAB6CA5492F6800B98C0CC803653A4B1D6E6AAED1932BACC5FEFAA818BA502859BA5494C5F5402C8536A9C4C1888150617F80098F6B2A99C39BC5DC7CF3B5900A21329AB59053ABAA64ED163E859A8B3B3CA3359B750CCC3E710C7AC43C8191CB5D68870C06391C0CB8AEC72B897AC6BE7FBAACC676ED66314C83630E89448C88A1DF04ACEB23ABF2E409EF333C622289C18A2134E650C45257E47475FA33AA537A5A8F7680214716C50D470E3284963CA64F54677AEC54B5272162BF52BC8142E1D4183FC017454A6B5A496831759064024745978CBD51A6CEDC8955DE4CC6D363670A47466E82BE5C23603A17BF22ACDB7CC984AF08C87E14E27753CF587A8EC3447E62C649E887A67C36C9CE98721B697213275646B194F36758673A8ED11284455AFC7A8529F69C97A3C2D7B8C636C0BA55614B768E624E712930F776169B01715725351BC74B47395ED52B25A1313C95164814C34C979CBDFAB85954662CAB485E75087A98CC74BB82CA2D1B5BF2803238480638C40E90B43C7460E7AA917F010151FAB1169987B372ABB59271F7006C24E60236B84B9DDD600623704254617FB498D89E58B0368BCB2103E79353EB587860C1422E476162E425BC2381DB82C6592737E1DD602864B0167A71EC1F223305C02FE25052AF2B3B5A55A0D7A2022D9A798DC0C5874A98702AAF4054C5D80338A5248B5B7BD09C53B5E2A084B047D277A861B1A73BB51488DE04EF573C85230A0470B73175C9FA50594F66A5F50B4150054C93B68186F8B5CBC49316C8548A642B2B36A1D454C7489AC33B2D2CE6668096782A2C1E0866D21A65E16B585E7AF8618BDF3184C1986878508917277B93E10706B1614972B2A94C7310FE9C708C231A1A8AC8D9314A529A97F469BF64962D820648443099A076D55D4CEA824A58304844F99497C10A25148618A315D72CA857D1B04D575B94F85C01D19BEF211BF0AA3362E7041FD16596D808E867B44C4C00D1CDA3418967717F147D0EB21B42AAEE74AC35D0B92414B958531AADF463EC6305AE5ECAF79174002F26DDECC813BF32672E8529D95A4E730A7AB4A3E8F8A8AF979A665EAFD465FC64A0C5F8F3F9003489415899D59A543D8208C54A3166529B53922D4EC143B50F01423B177895EDEE22BB739F647ECF85F50BC25EF7B5A725DEE868626ED79D451140800E03B59B956F8210E556067407D13DC90FA9E8B872BFB8F")
|
||||||
|
dk, err := mlkem768.NewKeyFromExtendedEncoding(dkBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ct, _ := hex.DecodeString("B52C56B92A4B7CE9E4CB7C5B1B163167A8A1675B2FDEF84A5B67CA15DB694C9F11BD027C30AE22EC921A1D911599AF0585E48D20DA70DF9F39E32EF95D4C8F44BFEFDAA5DA64F1054631D04D6D3CFD0A540DD7BA3886E4B5F13E878788604C95C096EAB3919F427521419A946C26CC041475D7124CDC01D0373E5B09C7A70603CFDB4FB3405023F2264DC3F983C4FC02A2D1B268F2208A1F6E2A6209BFF12F6F465F0B069C3A7F84F606D8A94064003D6EC114C8E808D3053884C1D5A142FBF20112EB360FDA3F0F28B172AE50F5E7D83801FB3F0064B687187074BD7FE30EDDAA334CF8FC04FA8CED899CEADE4B4F28B68372BAF98FF482A415B731155B75CEB976BE0EA0285BA01A27F1857A8FB377A3AE0C23B2AA9A079BFABFF0D5B2F1CD9B718BEA03C42F343A39B4F142D01AD8ACBB50E38853CF9A50C8B44C3CF671A4A9043B26DDBB24959AD6715C08521855C79A23B9C3D6471749C40725BDD5C2776D43AED20204BAA141EFB3304917474B7F9F7A4B08B1A93DAED98C67495359D37D67F7438BEE5E43585634B26C6B3810D7CDCBC0F6EB877A6087E68ACB8480D3A8CF6900447E49B417F15A53B607A0E216B855970D37406870B4568722DA77A4084703816784E2F16BED18996532C5D8B7F5D214464E5F3F6E905867B0CE119E252A66713253544685D208E1723908A0CE97834652E08AE7BDC881A131B73C71E84D20D68FDEFF4F5D70CD1AF57B78E3491A9865942321800A203C05ED1FEEB5A28E584E19F6535E7F84E4A24F84A72DCAF5648B4A4235DD664464482F03176E888C28BFC6C1CB238CFFA35A321E71791D9EA8ED0878C61121BF8D2A4AB2C1A5E120BC40ABB1892D1715090A0EE48252CA297A99AA0E510CF26B1ADD06CA543E1C5D6BDCD3B9C585C8538045DB5C252EC3C8C3C954D9BE5907094A894E60EAB43538CFEE82E8FFC0791B0D0F43AC1627830A61D56DAD96C62958B0DE780B78BD47A604550DAB83FFF227C324049471F35248CFB849B25724FF704D5277AA352D550958BE3B237DFF473EC2ADBAEA48CA2658AEFCC77BBD4264AB374D70EAE5B964416CE8226A7E3255A0F8D7E2ADCA062BCD6D78D60D1B32E11405BE54B66EF0FDDD567702A3BCCFEDE3C584701269ED14809F06F8968356BB9267FE86E514252E88BB5C30A7ECB3D0E621021EE0FBF7871B09342BF84F55C97EAF86C48189C7FF4DF389F077E2806E5FA73B3E9458A16C7E275F4F602275580EB7B7135FB537FA0CD95D6EA58C108CD8943D70C1643111F4F01CA8A8276A902666ED81B78D168B006F16AAA3D8E4CE4F4D0FB0997E41AEFFB5B3DAA838732F357349447F387776C793C0479DE9E99498CC356FDB0075A703F23C55D47B550EC89B02ADE89329086A50843456FEDC3788AC8D97233C54560467EE1D0F024B18428F0D73B30E19F5C63B9ABF11415BEA4D0170130BAABD33C05E6524E5FB5581B22B0433342248266D0F1053B245CC2462DC44D34965102482A8ED9E4E964D5683E5D45D0C8269")
|
||||||
|
ss, err := kyberDecapsulate(dk, ct)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
exp, _ := hex.DecodeString("914CB67FE5C38E73BF74181C0AC50428DEDF7750A98058F7D536708774535B29")
|
||||||
|
if !bytes.Equal(ss, exp) {
|
||||||
|
t.Fatalf("got %x, want %x", ss, exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKyberEncapsulate(t *testing.T) {
|
||||||
|
dk, err := mlkem768.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ct, ss, err := kyberEncapsulate(dk.EncapsulationKey())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
dkSS, err := kyberDecapsulate(dk, ct)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(ss, dkSS) {
|
||||||
|
t.Fatalf("got %x, want %x", ss, dkSS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -1805,3 +1806,135 @@ func testVerifyCertificates(t *testing.T, version uint16) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHandshakeKyber(t *testing.T) {
|
||||||
|
if x25519Kyber768Draft00.String() != "X25519Kyber768Draft00" {
|
||||||
|
t.Fatalf("unexpected CurveID string: %v", x25519Kyber768Draft00.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
clientConfig func(*Config)
|
||||||
|
serverConfig func(*Config)
|
||||||
|
preparation func(*testing.T)
|
||||||
|
expectClientSupport bool
|
||||||
|
expectKyber bool
|
||||||
|
expectHRR bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Default",
|
||||||
|
expectClientSupport: true,
|
||||||
|
expectKyber: true,
|
||||||
|
expectHRR: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ClientCurvePreferences",
|
||||||
|
clientConfig: func(config *Config) {
|
||||||
|
config.CurvePreferences = []CurveID{X25519}
|
||||||
|
},
|
||||||
|
expectClientSupport: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ServerCurvePreferencesX25519",
|
||||||
|
serverConfig: func(config *Config) {
|
||||||
|
config.CurvePreferences = []CurveID{X25519}
|
||||||
|
},
|
||||||
|
expectClientSupport: true,
|
||||||
|
expectKyber: false,
|
||||||
|
expectHRR: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ServerCurvePreferencesHRR",
|
||||||
|
serverConfig: func(config *Config) {
|
||||||
|
config.CurvePreferences = []CurveID{CurveP256}
|
||||||
|
},
|
||||||
|
expectClientSupport: true,
|
||||||
|
expectKyber: false,
|
||||||
|
expectHRR: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ClientTLSv12",
|
||||||
|
clientConfig: func(config *Config) {
|
||||||
|
config.MaxVersion = VersionTLS12
|
||||||
|
},
|
||||||
|
expectClientSupport: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ServerTLSv12",
|
||||||
|
serverConfig: func(config *Config) {
|
||||||
|
config.MaxVersion = VersionTLS12
|
||||||
|
},
|
||||||
|
expectClientSupport: true,
|
||||||
|
expectKyber: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "GODEBUG",
|
||||||
|
preparation: func(t *testing.T) {
|
||||||
|
t.Setenv("GODEBUG", "tlskyber=0")
|
||||||
|
},
|
||||||
|
expectClientSupport: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
baseConfig := testConfig.Clone()
|
||||||
|
baseConfig.CurvePreferences = nil
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
if test.preparation != nil {
|
||||||
|
test.preparation(t)
|
||||||
|
} else {
|
||||||
|
t.Parallel()
|
||||||
|
}
|
||||||
|
serverConfig := baseConfig.Clone()
|
||||||
|
if test.serverConfig != nil {
|
||||||
|
test.serverConfig(serverConfig)
|
||||||
|
}
|
||||||
|
serverConfig.GetConfigForClient = func(hello *ClientHelloInfo) (*Config, error) {
|
||||||
|
if !test.expectClientSupport && slices.Contains(hello.SupportedCurves, x25519Kyber768Draft00) {
|
||||||
|
return nil, errors.New("client supports Kyber768Draft00")
|
||||||
|
} else if test.expectClientSupport && !slices.Contains(hello.SupportedCurves, x25519Kyber768Draft00) {
|
||||||
|
return nil, errors.New("client does not support Kyber768Draft00")
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
clientConfig := baseConfig.Clone()
|
||||||
|
if test.clientConfig != nil {
|
||||||
|
test.clientConfig(clientConfig)
|
||||||
|
}
|
||||||
|
ss, cs, err := testHandshake(t, clientConfig, serverConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if test.expectKyber {
|
||||||
|
if ss.testingOnlyCurveID != x25519Kyber768Draft00 {
|
||||||
|
t.Errorf("got CurveID %v (server), expected %v", ss.testingOnlyCurveID, x25519Kyber768Draft00)
|
||||||
|
}
|
||||||
|
if cs.testingOnlyCurveID != x25519Kyber768Draft00 {
|
||||||
|
t.Errorf("got CurveID %v (client), expected %v", cs.testingOnlyCurveID, x25519Kyber768Draft00)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ss.testingOnlyCurveID == x25519Kyber768Draft00 {
|
||||||
|
t.Errorf("got CurveID %v (server), expected not Kyber", ss.testingOnlyCurveID)
|
||||||
|
}
|
||||||
|
if cs.testingOnlyCurveID == x25519Kyber768Draft00 {
|
||||||
|
t.Errorf("got CurveID %v (client), expected not Kyber", cs.testingOnlyCurveID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if test.expectHRR {
|
||||||
|
if !ss.testingOnlyDidHRR {
|
||||||
|
t.Error("server did not use HRR")
|
||||||
|
}
|
||||||
|
if !cs.testingOnlyDidHRR {
|
||||||
|
t.Error("client did not use HRR")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ss.testingOnlyDidHRR {
|
||||||
|
t.Error("server used HRR")
|
||||||
|
}
|
||||||
|
if cs.testingOnlyDidHRR {
|
||||||
|
t.Error("client used HRR")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -47,6 +47,7 @@ var All = []Info{
|
|||||||
{Name: "randautoseed", Package: "math/rand"},
|
{Name: "randautoseed", Package: "math/rand"},
|
||||||
{Name: "tarinsecurepath", Package: "archive/tar"},
|
{Name: "tarinsecurepath", Package: "archive/tar"},
|
||||||
{Name: "tls10server", Package: "crypto/tls", Changed: 22, Old: "1"},
|
{Name: "tls10server", Package: "crypto/tls", Changed: 22, Old: "1"},
|
||||||
|
{Name: "tlskyber", Package: "crypto/tls", Changed: 23, Old: "0", Opaque: true},
|
||||||
{Name: "tlsmaxrsasize", Package: "crypto/tls"},
|
{Name: "tlsmaxrsasize", Package: "crypto/tls"},
|
||||||
{Name: "tlsrsakex", Package: "crypto/tls", Changed: 22, Old: "1"},
|
{Name: "tlsrsakex", Package: "crypto/tls", Changed: 22, Old: "1"},
|
||||||
{Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"},
|
{Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"},
|
||||||
|
Loading…
Reference in New Issue
Block a user