yuhan6665
2026-09-21 00:14:39 +00:00
committed by RPRX
parent 8cdf7bf9c7
commit 3c98159dee
25 changed files with 1490 additions and 1119 deletions
+2
View File
@@ -12,6 +12,8 @@ import "strconv"
// which wraps AlertError rather than sending a TLS alert.
type AlertError uint8
var _ error = AlertError(0)
func (e AlertError) Error() string {
return alert(e).String()
}
+105 -62
View File
@@ -10,6 +10,7 @@ import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/mldsa"
"crypto/rsa"
"errors"
"fmt"
@@ -18,9 +19,16 @@ import (
"slices"
)
// verifyHandshakeSignature verifies a signature against pre-hashed
// (if required) handshake contents.
// verifyHandshakeSignature verifies a signature against unhashed handshake contents.
func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error {
if hashFunc != directSigning {
if !hashFunc.Available() {
return fmt.Errorf("hash function unavailable: %v", hashFunc)
}
h := hashFunc.New()
h.Write(signed)
signed = h.Sum(nil)
}
switch sigType {
case signatureECDSA:
pubKey, ok := pubkey.(*ecdsa.PublicKey)
@@ -38,6 +46,14 @@ func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc c
if !ed25519.Verify(pubKey, signed, sig) {
return errors.New("Ed25519 verification failure")
}
case signatureMLDSA:
pubKey, ok := pubkey.(*mldsa.PublicKey)
if !ok {
return fmt.Errorf("expected an ML-DSA public key, got %T", pubkey)
}
if err := mldsa.Verify(pubKey, signed, sig, nil); err != nil {
return fmt.Errorf("ML-DSA verification failure: %w", err)
}
case signaturePKCS1v15:
pubKey, ok := pubkey.(*rsa.PublicKey)
if !ok {
@@ -61,6 +77,32 @@ func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc c
return nil
}
// verifyLegacyHandshakeSignature verifies a TLS 1.0 and 1.1 signature against
// pre-hashed handshake contents.
func verifyLegacyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, hashed, sig []byte) error {
switch sigType {
case signatureECDSA:
pubKey, ok := pubkey.(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("expected an ECDSA public key, got %T", pubkey)
}
if !ecdsa.VerifyASN1(pubKey, hashed, sig) {
return errors.New("ECDSA verification failure")
}
case signaturePKCS1v15:
pubKey, ok := pubkey.(*rsa.PublicKey)
if !ok {
return fmt.Errorf("expected an RSA public key, got %T", pubkey)
}
if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, hashed, sig); err != nil {
return err
}
default:
return errors.New("internal error: unknown signature type")
}
return nil
}
const (
serverSignatureContext = "TLS 1.3, server CertificateVerify\x00"
clientSignatureContext = "TLS 1.3, client CertificateVerify\x00"
@@ -77,21 +119,15 @@ var signaturePadding = []byte{
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
}
// signedMessage returns the pre-hashed (if necessary) message to be signed by
// certificate keys in TLS 1.3. See RFC 8446, Section 4.4.3.
func signedMessage(sigHash crypto.Hash, context string, transcript hash.Hash) []byte {
if sigHash == directSigning {
b := &bytes.Buffer{}
b.Write(signaturePadding)
io.WriteString(b, context)
b.Write(transcript.Sum(nil))
return b.Bytes()
}
h := sigHash.New()
h.Write(signaturePadding)
io.WriteString(h, context)
h.Write(transcript.Sum(nil))
return h.Sum(nil)
// signedMessage returns the (unhashed) message to be signed by certificate keys
// in TLS 1.3. See RFC 8446, Section 4.4.3.
func signedMessage(context string, transcript hash.Hash) []byte {
const maxSize = 64 /* signaturePadding */ + len(serverSignatureContext) + 512/8 /* SHA-512 */
b := bytes.NewBuffer(make([]byte, 0, maxSize))
b.Write(signaturePadding)
io.WriteString(b, context)
b.Write(transcript.Sum(nil))
return b.Bytes()
}
// typeAndHashFromSignatureScheme returns the corresponding signature type and
@@ -106,6 +142,8 @@ func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType
sigType = signatureECDSA
case Ed25519:
sigType = signatureEd25519
case MLDSA44, MLDSA65, MLDSA87:
sigType = signatureMLDSA
default:
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
}
@@ -120,6 +158,8 @@ func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType
hash = crypto.SHA512
case Ed25519:
hash = directSigning
case MLDSA44, MLDSA65, MLDSA87:
hash = directSigning
default:
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
}
@@ -141,6 +181,8 @@ func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash c
// full signature, and not even OpenSSL bothers with the
// complexity, so we can't even test it properly.
return 0, 0, fmt.Errorf("tls: Ed25519 public keys are not supported before TLS 1.2")
case *mldsa.PublicKey:
return 0, 0, fmt.Errorf("tls: ML-DSA public keys are not supported before TLS 1.3")
default:
return 0, 0, fmt.Errorf("tls: unsupported public key: %T", pub)
}
@@ -149,90 +191,89 @@ func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash c
var rsaSignatureSchemes = []struct {
scheme SignatureScheme
minModulusBytes int
maxVersion uint16
}{
// RSA-PSS is used with PSSSaltLengthEqualsHash, and requires
// emLen >= hLen + sLen + 2
{PSSWithSHA256, crypto.SHA256.Size()*2 + 2, VersionTLS13},
{PSSWithSHA384, crypto.SHA384.Size()*2 + 2, VersionTLS13},
{PSSWithSHA512, crypto.SHA512.Size()*2 + 2, VersionTLS13},
{PSSWithSHA256, crypto.SHA256.Size()*2 + 2},
{PSSWithSHA384, crypto.SHA384.Size()*2 + 2},
{PSSWithSHA512, crypto.SHA512.Size()*2 + 2},
// PKCS #1 v1.5 uses prefixes from hashPrefixes in crypto/rsa, and requires
// emLen >= len(prefix) + hLen + 11
// TLS 1.3 dropped support for PKCS #1 v1.5 in favor of RSA-PSS.
{PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11, VersionTLS12},
{PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11, VersionTLS12},
{PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11, VersionTLS12},
{PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11, VersionTLS12},
{PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11},
{PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11},
{PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11},
{PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11},
}
// signatureSchemesForCertificate returns the list of supported SignatureSchemes
// for a given certificate, based on the public key and the protocol version,
// and optionally filtered by its explicit SupportedSignatureAlgorithms.
func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme {
priv, ok := cert.PrivateKey.(crypto.Signer)
if !ok {
return nil
}
var sigAlgs []SignatureScheme
switch pub := priv.Public().(type) {
func signatureSchemesForPublicKey(version uint16, pub crypto.PublicKey) []SignatureScheme {
switch pub := pub.(type) {
case *ecdsa.PublicKey:
if version != VersionTLS13 {
if version < VersionTLS13 {
// In TLS 1.2 and earlier, ECDSA algorithms are not
// constrained to a single curve.
sigAlgs = []SignatureScheme{
return []SignatureScheme{
ECDSAWithP256AndSHA256,
ECDSAWithP384AndSHA384,
ECDSAWithP521AndSHA512,
ECDSAWithSHA1,
}
break
}
switch pub.Curve {
case elliptic.P256():
sigAlgs = []SignatureScheme{ECDSAWithP256AndSHA256}
return []SignatureScheme{ECDSAWithP256AndSHA256}
case elliptic.P384():
sigAlgs = []SignatureScheme{ECDSAWithP384AndSHA384}
return []SignatureScheme{ECDSAWithP384AndSHA384}
case elliptic.P521():
sigAlgs = []SignatureScheme{ECDSAWithP521AndSHA512}
return []SignatureScheme{ECDSAWithP521AndSHA512}
default:
return nil
}
case *rsa.PublicKey:
size := pub.Size()
sigAlgs = make([]SignatureScheme, 0, len(rsaSignatureSchemes))
sigAlgs := make([]SignatureScheme, 0, len(rsaSignatureSchemes))
for _, candidate := range rsaSignatureSchemes {
if size >= candidate.minModulusBytes && version <= candidate.maxVersion {
if size >= candidate.minModulusBytes {
sigAlgs = append(sigAlgs, candidate.scheme)
}
}
return sigAlgs
case ed25519.PublicKey:
sigAlgs = []SignatureScheme{Ed25519}
return []SignatureScheme{Ed25519}
case *mldsa.PublicKey:
switch pub.Parameters() {
case mldsa.MLDSA44():
return []SignatureScheme{MLDSA44}
case mldsa.MLDSA65():
return []SignatureScheme{MLDSA65}
case mldsa.MLDSA87():
return []SignatureScheme{MLDSA87}
default:
panic("tls: internal error: unknown ML-DSA parameter set: " + pub.Parameters().String())
}
default:
return nil
}
if cert.SupportedSignatureAlgorithms != nil {
sigAlgs = slices.DeleteFunc(sigAlgs, func(sigAlg SignatureScheme) bool {
return !isSupportedSignatureAlgorithm(sigAlg, cert.SupportedSignatureAlgorithms)
})
}
// Filter out any unsupported signature algorithms, for example due to
// FIPS 140-3 policy, tlssha1=0, or any downstream changes to defaults.go.
supportedAlgs := supportedSignatureAlgorithms(version)
sigAlgs = slices.DeleteFunc(sigAlgs, func(sigAlg SignatureScheme) bool {
return !isSupportedSignatureAlgorithm(sigAlg, supportedAlgs)
})
return sigAlgs
}
// selectSignatureScheme picks a SignatureScheme from the peer's preference list
// that works with the selected certificate. It's only called for protocol
// versions that support signature algorithms, so TLS 1.2 and 1.3.
func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureScheme) (SignatureScheme, error) {
supportedAlgs := signatureSchemesForCertificate(vers, c)
priv, ok := c.PrivateKey.(crypto.Signer)
if !ok {
return 0, unsupportedCertificateError(c)
}
supportedAlgs := signatureSchemesForPublicKey(vers, priv.Public())
if c.SupportedSignatureAlgorithms != nil {
supportedAlgs = slices.DeleteFunc(supportedAlgs, func(sigAlg SignatureScheme) bool {
return !isSupportedSignatureAlgorithm(sigAlg, c.SupportedSignatureAlgorithms)
})
}
// Filter out any unsupported signature algorithms, for example due to
// FIPS 140-3 policy, tlssha1=0, or protocol version.
supportedAlgs = slices.DeleteFunc(supportedAlgs, func(sigAlg SignatureScheme) bool {
return isDisabledSignatureAlgorithm(vers, sigAlg, false)
})
if len(supportedAlgs) == 0 {
return 0, unsupportedCertificateError(c)
}
@@ -285,6 +326,8 @@ func unsupportedCertificateError(cert *Certificate) error {
case *rsa.PublicKey:
return fmt.Errorf("tls: certificate RSA key size too small for supported signature algorithms")
case ed25519.PublicKey:
case *mldsa.PublicKey:
return errors.New("tls: ML-DSA certificates require TLS 1.3")
default:
return fmt.Errorf("tls: unsupported certificate key (%T)", pub)
}
+14 -16
View File
@@ -13,6 +13,7 @@ import (
"crypto/rc4"
"crypto/sha1"
"crypto/sha256"
_ "crypto/sha512" // for crypto.SHA384
"fmt"
"hash"
"runtime"
@@ -146,8 +147,8 @@ type cipherSuite struct {
}
var cipherSuites = []*cipherSuite{ // TODO: replace with a map, since the order doesn't matter.
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadAESGCM},
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
@@ -281,7 +282,7 @@ var cipherSuitesPreferenceOrder = []uint16{
// AEADs w/ ECDHE
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
// CBC w/ ECDHE
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
@@ -310,7 +311,7 @@ var cipherSuitesPreferenceOrder = []uint16{
var cipherSuitesPreferenceOrderNoAES = []uint16{
// ChaCha20Poly1305
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
// AES-GCM w/ ECDHE
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
@@ -342,25 +343,16 @@ var disabledCipherSuites = map[uint16]bool{
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: true,
TLS_ECDHE_RSA_WITH_RC4_128_SHA: true,
TLS_RSA_WITH_RC4_128_SHA: true,
}
// rsaKexCiphers contains the ciphers which use RSA based key exchange,
// which we also disable by default unless a GODEBUG is set.
var rsaKexCiphers = map[uint16]bool{
TLS_RSA_WITH_RC4_128_SHA: true,
// RSA key exchange
TLS_RSA_WITH_3DES_EDE_CBC_SHA: true,
TLS_RSA_WITH_AES_128_CBC_SHA: true,
TLS_RSA_WITH_AES_256_CBC_SHA: true,
TLS_RSA_WITH_AES_128_CBC_SHA256: true,
TLS_RSA_WITH_AES_128_GCM_SHA256: true,
TLS_RSA_WITH_AES_256_GCM_SHA384: true,
}
// tdesCiphers contains 3DES ciphers,
// which we also disable by default unless a GODEBUG is set.
var tdesCiphers = map[uint16]bool{
// 3DES
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: true,
TLS_RSA_WITH_3DES_EDE_CBC_SHA: true,
}
var (
@@ -425,7 +417,13 @@ func cipherAES(key, iv []byte, isRead bool) any {
// macSHA1 returns a SHA-1 based constant time MAC.
func macSHA1(key []byte) hash.Hash {
return hmac.New(sha1.New, key)
h := sha1.New
// The BoringCrypto SHA1 does not have a constant-time
// checksum function, so don't try to use it.
//if !boring.Enabled {
h = newConstantTimeHash(h)
//}
return hmac.New(h, key)
}
// macSHA256 returns a SHA-256 based MAC. This is only supported in TLS 1.2 and
+276 -86
View File
@@ -12,6 +12,8 @@ import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/fips140"
"crypto/mldsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha512"
@@ -20,6 +22,7 @@ import (
"fmt"
"io"
"net"
"runtime"
"slices"
"strings"
"sync"
@@ -67,7 +70,9 @@ const (
recordHeaderLen = 5 // record header length
maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
maxHandshakeCertificateMsg = 262144 // maximum certificate message size (256 KiB)
//////////////////////////////////// [REALITY] SECTION: change maxUselessRecords to match with OpenSSL
maxUselessRecords = 32 // maximum number of consecutive non-advancing records
//////////////////////////////////// [REALITY] SECTION END
)
// TLS record types.
@@ -145,19 +150,32 @@ const (
type CurveID uint16
const (
CurveP256 CurveID = 23
CurveP384 CurveID = 24
CurveP521 CurveID = 25
X25519 CurveID = 29
X25519MLKEM768 CurveID = 4588
CurveP256 CurveID = 23
CurveP384 CurveID = 24
CurveP521 CurveID = 25
X25519 CurveID = 29
X25519MLKEM768 CurveID = 4588
SecP256r1MLKEM768 CurveID = 4587
SecP384r1MLKEM1024 CurveID = 4589
MLKEM1024 CurveID = 514
)
func isTLS13OnlyKeyExchange(curve CurveID) bool {
return curve == X25519MLKEM768
switch curve {
case X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024, MLKEM1024:
return true
default:
return false
}
}
func isPQKeyExchange(curve CurveID) bool {
return curve == X25519MLKEM768
switch curve {
case X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024, MLKEM1024:
return true
default:
return false
}
}
// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8.
@@ -203,11 +221,12 @@ const (
signatureRSAPSS
signatureECDSA
signatureEd25519
signatureMLDSA
)
// directSigning is a standard Hash value that signals that no pre-hashing
// should be performed, and that the input should be signed directly. It is the
// hash function associated with the Ed25519 signature scheme.
// hash function associated with the Ed25519 and ML-DSA signature schemes.
var directSigning crypto.Hash = 0
// helloRetryRequestRandom is set as the Random value of a ServerHello
@@ -304,11 +323,21 @@ type ConnectionState struct {
// client side.
ECHAccepted bool
// HelloRetryRequest indicates whether we sent a HelloRetryRequest if we
// are a server, or if we received a HelloRetryRequest if we are a client.
HelloRetryRequest bool
// LocalCertificate is the certificate chain presented to the peer, if any,
// during the handshake. This field is only populated for connections which
// are not resumed (DidResume is false).
LocalCertificate [][]byte
// ekm is a closure exposed via ExportKeyingMaterial.
ekm func(label string, context []byte, length int) ([]byte, error)
// testingOnlyDidHRR is true if a HelloRetryRequest was sent/received.
testingOnlyDidHRR bool
// testingOnlyPeerSignatureAlgorithm is the signature algorithm used by the
// peer to sign the handshake. It is not set for resumed connections.
testingOnlyPeerSignatureAlgorithm SignatureScheme
}
// ExportKeyingMaterial returns length bytes of exported key material in a new
@@ -316,11 +345,6 @@ type ConnectionState struct {
// the seed. If the connection was set to allow renegotiation via
// Config.Renegotiation, or if the connections supports neither TLS 1.3 nor
// Extended Master Secret, this function will return an error.
//
// Exporting key material without Extended Master Secret or TLS 1.3 was disabled
// in Go 1.22 due to security issues (see the Security Considerations sections
// of RFC 5705 and RFC 7627), but can be re-enabled with the GODEBUG setting
// tlsunsafeekm=1.
func (cs *ConnectionState) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) {
return cs.ekm(label, context, length)
}
@@ -407,6 +431,11 @@ const (
// EdDSA algorithms.
Ed25519 SignatureScheme = 0x0807
// ML-DSA algorithms.
MLDSA44 SignatureScheme = 0x0904
MLDSA65 SignatureScheme = 0x0905
MLDSA87 SignatureScheme = 0x0906
// Legacy signature and hash algorithms for TLS 1.2.
PKCS1WithSHA1 SignatureScheme = 0x0201
ECDSAWithSHA1 SignatureScheme = 0x0203
@@ -465,10 +494,17 @@ type ClientHelloInfo struct {
// connection to fail.
Conn net.Conn
// HelloRetryRequest indicates whether the ClientHello was sent in response
// to a HelloRetryRequest message.
HelloRetryRequest bool
// config is embedded by the GetCertificate or GetConfigForClient caller,
// for use with SupportsCertificate.
config *Config
// isQUIC indicates whether the connection is a QUIC connection.
isQUIC bool
// ctx is the context of the handshake that is in progress.
ctx context.Context
}
@@ -537,6 +573,7 @@ const (
RenegotiateFreelyAsClient
)
//////////////////////////////////// [REALITY] SECTION: define var
type LimitFallback struct {
AfterBytes uint64
BytesPerSec uint64
@@ -566,11 +603,15 @@ type Config struct {
LimitFallbackUpload LimitFallback
LimitFallbackDownload LimitFallback
//////////////////////////////////// [REALITY] SECTION END
// Rand provides the source of entropy for nonces and RSA blinding.
// Rand provides the source of entropy for the connection.
// If Rand is nil, TLS uses the cryptographic random reader in package
// crypto/rand.
// The Reader must be safe for use by multiple goroutines.
// crypto/rand. The Reader must be safe for use by multiple goroutines.
//
// Deprecated: this should be left nil in production. Not all TLS
// configurations are guaranteed to use Rand. Test code can use
// [testing/cryptotest.SetGlobalRandom] instead.
Rand io.Reader
// Time returns the current time as the number of seconds since the epoch.
@@ -636,10 +677,13 @@ type Config struct {
// If GetConfigForClient is nil, the Config passed to Server() will be
// used for all connections.
//
// If SessionTicketKey was explicitly set on the returned Config, or if
// SetSessionTicketKeys was called on the returned Config, those keys will
// If SessionTicketKey is explicitly set on the returned Config, or if
// SetSessionTicketKeys is called on the returned Config, those keys will
// be used. Otherwise, the original Config keys will be used (and possibly
// rotated if they are automatically managed).
// rotated if they are automatically managed). WARNING: this allows session
// resumption of connections originally established with the parent (or a
// sibling) Config, which may bypass the [Config.VerifyPeerCertificate]
// value of the returned Config.
GetConfigForClient func(*ClientHelloInfo) (*Config, error)
// VerifyPeerCertificate, if not nil, is called after normal
@@ -657,8 +701,10 @@ type Config struct {
// rawCerts may be empty on the server if ClientAuth is RequestClientCert or
// VerifyClientCertIfGiven.
//
// This callback is not invoked on resumed connections, as certificates are
// not re-verified on resumption.
// This callback is not invoked on resumed connections. WARNING: this
// includes connections resumed across Configs returned by [Config.Clone] or
// [Config.GetConfigForClient] and their parents. If that is not intended,
// use [Config.VerifyConnection] instead, or set [Config.SessionTicketsDisabled].
//
// verifiedChains and its contents should not be modified.
VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
@@ -714,11 +760,7 @@ type Config struct {
// the list is ignored. Note that TLS 1.3 ciphersuites are not configurable.
//
// If CipherSuites is nil, a safe default list is used. The default cipher
// suites might change over time. In Go 1.22 RSA key exchange based cipher
// suites were removed from the default list, but can be re-added with the
// GODEBUG setting tlsrsakex=1. In Go 1.23 3DES cipher suites were removed
// from the default list, but can be re-added with the GODEBUG setting
// tls3des=1.
// suites might change over time.
CipherSuites []uint16
// PreferServerCipherSuites is a legacy field and has no effect.
@@ -783,9 +825,7 @@ type Config struct {
//
// By default, TLS 1.2 is currently used as the minimum. TLS 1.0 is the
// minimum supported by this package.
//
// The server-side default can be reverted to TLS 1.0 by including the value
// "tls10server=1" in the GODEBUG environment variable.
MinVersion uint16
// MaxVersion contains the maximum TLS version that is acceptable.
@@ -803,6 +843,11 @@ type Config struct {
// From Go 1.24, the default includes the [X25519MLKEM768] hybrid
// post-quantum key exchange. To disable it, set CurvePreferences explicitly
// or use the GODEBUG=tlsmlkem=0 environment variable.
//
// From Go 1.26, the default includes the [SecP256r1MLKEM768] and
// [SecP384r1MLKEM1024] hybrid post-quantum key exchanges, too. To disable
// them, set CurvePreferences explicitly or use either the
// GODEBUG=tlsmlkem=0 or the GODEBUG=tlssecpmlkem=0 environment variable.
CurvePreferences []CurveID
// DynamicRecordSizingDisabled disables adaptive sizing of TLS records.
@@ -818,7 +863,7 @@ type Config struct {
// KeyLogWriter optionally specifies a destination for TLS master secrets
// in NSS key log format that can be used to allow external programs
// such as Wireshark to decrypt TLS connections.
// See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format.
// See https://datatracker.ietf.org/doc/draft-ietf-tls-keylogfile/.
// Use of KeyLogWriter compromises security and should only be
// used for debugging.
KeyLogWriter io.Writer
@@ -910,13 +955,34 @@ type Config struct {
// with a specific ECH config known to a client.
type EncryptedClientHelloKey struct {
// Config should be a marshalled ECHConfig associated with PrivateKey. This
// must match the config provided to clients byte-for-byte. The config
// should only specify the DHKEM(X25519, HKDF-SHA256) KEM ID (0x0020), the
// HKDF-SHA256 KDF ID (0x0001), and a subset of the following AEAD IDs:
// AES-128-GCM (0x0001), AES-256-GCM (0x0002), ChaCha20Poly1305 (0x0003).
// must match the config provided to clients byte-for-byte. The config must
// use as KEM one of
//
// - DHKEM(P-256, HKDF-SHA256) (0x0010)
// - DHKEM(P-384, HKDF-SHA384) (0x0011)
// - DHKEM(P-521, HKDF-SHA512) (0x0012)
// - DHKEM(X25519, HKDF-SHA256) (0x0020)
// - ML-KEM-768 (0x0041)
// - ML-KEM-1024 (0x0042)
// - MLKEM768-P256 (0x0050)
// - MLKEM1024-P384 (0x0051)
// - MLKEM768-X25519 (0x647a)
//
// and as KDF one of
//
// - HKDF-SHA256 (0x0001)
// - HKDF-SHA384 (0x0002)
// - HKDF-SHA512 (0x0003)
//
// and as AEAD one of
//
// - AES-128-GCM (0x0001)
// - AES-256-GCM (0x0002)
// - ChaCha20Poly1305 (0x0003)
//
Config []byte
// PrivateKey should be a marshalled private key. Currently, we expect
// this to be the output of [ecdh.PrivateKey.Bytes].
// PrivateKey should be a marshalled private key, in the format expected by
// HPKE's DeserializePrivateKey (see RFC 9180), for the KEM used in Config.
PrivateKey []byte
// SendAsRetry indicates if Config should be sent as part of the list of
// retry configs when ECH is requested by the client but rejected by the
@@ -961,8 +1027,15 @@ func (c *Config) ticketKeyFromBytes(b [32]byte) (key ticketKey) {
// ticket, and the lifetime we set for all tickets we send.
const maxSessionTicketLifetime = 7 * 24 * time.Hour
// Clone returns a shallow clone of c or nil if c is nil. It is safe to clone a [Config] that is
// being used concurrently by a TLS client or server.
// Clone returns a shallow clone of c or nil if c is nil. It is safe to clone a
// [Config] that is being used concurrently by a TLS client or server.
//
// The returned Config can share session ticket keys with the original Config,
// which means connections could be resumed across the two Configs. WARNING:
// [Config.VerifyPeerCertificate] does not get called on resumed connections,
// including connections that were originally established on the parent Config.
// If that is not intended, use [Config.VerifyConnection] instead, or set
// [Config.SessionTicketsDisabled].
func (c *Config) Clone() *Config {
if c == nil {
return nil
@@ -970,6 +1043,7 @@ func (c *Config) Clone() *Config {
c.mutex.RLock()
defer c.mutex.RUnlock()
return &Config{
//////////////////////////////////// [REALITY] SECTION: define var
DialContext: c.DialContext,
Show: c.Show,
Type: c.Type,
@@ -983,6 +1057,7 @@ func (c *Config) Clone() *Config {
ShortIds: c.ShortIds,
LimitFallbackUpload: c.LimitFallbackUpload,
LimitFallbackDownload: c.LimitFallbackDownload,
//////////////////////////////////// [REALITY] SECTION END
Rand: c.Rand,
Time: c.Time,
Certificates: c.Certificates,
@@ -1201,7 +1276,7 @@ const roleServer = false
// supportedVersions returns the list of supported TLS versions, sorted from
// highest to lowest (and hence also in preference order).
func (c *Config) supportedVersions(isClient bool) []uint16 {
func (c *Config) supportedVersions(isClient, isQUIC bool) []uint16 {
versions := make([]uint16, 0, len(supportedVersions))
for _, v := range supportedVersions {
if fips140tls.Required() && !slices.Contains(allowedSupportedVersionsFIPS, v) {
@@ -1219,13 +1294,16 @@ func (c *Config) supportedVersions(isClient bool) []uint16 {
if c != nil && c.MaxVersion != 0 && v > c.MaxVersion {
continue
}
if isQUIC && v < VersionTLS13 {
continue
}
versions = append(versions, v)
}
return versions
}
func (c *Config) maxSupportedVersion(isClient bool) uint16 {
supportedVersions := c.supportedVersions(isClient)
func (c *Config) maxSupportedVersion(isClient, isQUIC bool) uint16 {
supportedVersions := c.supportedVersions(isClient, isQUIC)
if len(supportedVersions) == 0 {
return 0
}
@@ -1247,31 +1325,38 @@ func supportedVersionsFromMax(maxVersion uint16) []uint16 {
}
func (c *Config) curvePreferences(version uint16) []CurveID {
curvePreferences := defaultCurvePreferences()
if fips140tls.Required() {
curvePreferences = slices.DeleteFunc(curvePreferences, func(x CurveID) bool {
return !slices.Contains(allowedCurvePreferencesFIPS, x)
})
}
if c != nil && len(c.CurvePreferences) != 0 {
curvePreferences = slices.DeleteFunc(curvePreferences, func(x CurveID) bool {
return !slices.Contains(c.CurvePreferences, x)
})
}
if version < VersionTLS13 {
curvePreferences = slices.DeleteFunc(curvePreferences, isTLS13OnlyKeyExchange)
}
return curvePreferences
return slices.DeleteFunc(curvePreferenceOrder(), func(x CurveID) bool {
return !c.supportsCurve(version, x)
})
}
func (c *Config) supportsCurve(version uint16, curve CurveID) bool {
return slices.Contains(c.curvePreferences(version), curve)
func (c *Config) supportsCurve(version uint16, x CurveID) bool {
if c != nil && len(c.CurvePreferences) != 0 {
if !slices.Contains(c.CurvePreferences, x) {
return false
}
// Ignore unimplemented entries in c.CurvePreferences.
if !slices.Contains(curvePreferenceOrder(), x) {
return false
}
} else {
if !defaultCurveEnabled(x) {
return false
}
}
if fips140tls.Required() && !slices.Contains(allowedCurvePreferencesFIPS, x) {
return false
}
if version < VersionTLS13 && isTLS13OnlyKeyExchange(x) {
return false
}
return true
}
// mutualVersion returns the protocol version to use given the advertised
// versions of the peer. The highest supported version is preferred.
func (c *Config) mutualVersion(isClient bool, peerVersions []uint16) (uint16, bool) {
supportedVersions := c.supportedVersions(isClient)
func (c *Config) mutualVersion(isClient, isQUIC bool, peerVersions []uint16) (uint16, bool) {
supportedVersions := c.supportedVersions(isClient, isQUIC)
for _, v := range supportedVersions {
if slices.Contains(peerVersions, v) {
return v, true
@@ -1357,7 +1442,7 @@ func (chi *ClientHelloInfo) SupportsCertificate(c *Certificate) error {
if config == nil {
config = &Config{}
}
vers, ok := config.mutualVersion(roleServer, chi.SupportedVersions)
vers, ok := config.mutualVersion(roleServer, chi.isQUIC, chi.SupportedVersions)
if !ok {
return errors.New("no mutually supported protocol versions")
}
@@ -1465,6 +1550,9 @@ func (chi *ClientHelloInfo) SupportsCertificate(c *Certificate) error {
return errors.New("connection doesn't support Ed25519")
}
ecdsaCipherSuite = true
case *mldsa.PublicKey:
// ML-DSA requires TLS 1.3, which we already excluded above.
return errors.New("connection doesn't support ML-DSA")
case *rsa.PublicKey:
default:
return supportsRSAFallback(unsupportedCertificateError(c))
@@ -1578,7 +1666,10 @@ func (c *Config) writeKeyLog(label string, clientRandom, secret []byte) error {
_, err := c.KeyLogWriter.Write(logLine)
writerMutex.Unlock()
return err
if err != nil {
return fmt.Errorf("tls: KeyLogWriter: %w", err)
}
return nil
}
// writerMutex protects all KeyLogWriters globally. It is rarely enabled,
@@ -1589,9 +1680,14 @@ var writerMutex sync.Mutex
type Certificate struct {
Certificate [][]byte
// PrivateKey contains the private key corresponding to the public key in
// Leaf. This must implement crypto.Signer with an RSA, ECDSA or Ed25519 PublicKey.
// Leaf. This must implement [crypto.Signer] with an RSA, ECDSA, Ed25519
// (TLS 1.2+), or ML-DSA (TLS 1.3) PublicKey.
//
// For a server up to TLS 1.2, it can also implement crypto.Decrypter with
// an RSA PublicKey.
//
// If it implements [crypto.MessageSigner], SignMessage will be used instead
// of Sign for TLS 1.2 and later.
PrivateKey crypto.PrivateKey
// SupportedSignatureAlgorithms is an optional list restricting what
// signature algorithms the PrivateKey can be used for.
@@ -1680,6 +1776,10 @@ func (c *lruSessionCache) Put(sessionKey string, cs *ClientSessionState) {
return
}
if cs == nil {
return
}
if c.q.Len() < c.capacity {
entry := &lruSessionCacheEntry{sessionKey, cs}
c.m[sessionKey] = c.q.PushFront(entry)
@@ -1718,35 +1818,85 @@ func unexpectedMessageError(wanted, got any) error {
return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted)
}
var testingOnlySupportedSignatureAlgorithms []SignatureScheme
// supportedSignatureAlgorithms returns the supported signature algorithms for
// the given minimum TLS version, to advertise in ClientHello and
// CertificateRequest messages.
func supportedSignatureAlgorithms(minVers uint16) []SignatureScheme {
// the given range of TLS versions, to advertise in ClientHello and
// CertificateRequest messages. An algorithm is included if it is enabled at any
// version in the range.
func supportedSignatureAlgorithms(minVers, maxVers uint16) []SignatureScheme {
sigAlgs := defaultSupportedSignatureAlgorithms()
if fips140tls.Required() {
sigAlgs = slices.DeleteFunc(sigAlgs, func(s SignatureScheme) bool {
return !slices.Contains(allowedSignatureAlgorithmsFIPS, s)
})
if testingOnlySupportedSignatureAlgorithms != nil {
sigAlgs = slices.Clone(testingOnlySupportedSignatureAlgorithms)
}
if minVers > VersionTLS12 {
sigAlgs = slices.DeleteFunc(sigAlgs, func(s SignatureScheme) bool {
sigType, sigHash, _ := typeAndHashFromSignatureScheme(s)
return sigType == signaturePKCS1v15 || sigHash == crypto.SHA1
})
return slices.DeleteFunc(sigAlgs, func(s SignatureScheme) bool {
for v := minVers; v <= maxVers; v++ {
if !isDisabledSignatureAlgorithm(v, s, false) {
return false
}
}
return true
})
}
//var tlssha1 = godebug.New("tlssha1")
func isDisabledSignatureAlgorithm(version uint16, s SignatureScheme, isCert bool) bool {
if fips140tls.Required() && !slices.Contains(allowedSignatureAlgorithmsFIPS, s) {
return true
}
return sigAlgs
switch s {
case MLDSA44, MLDSA65, MLDSA87:
// ML-DSA is not available in FIPS 140-3 module v1.0.0.
if fips140.Version() == "v1.0.0" {
return true
}
// ML-DSA codepoints are only defined for TLS 1.3.
if version < VersionTLS13 {
return true
}
}
// For the _cert extension we include all algorithms, including SHA-1 and
// PKCS#1 v1.5, because it's more likely that something on our side will be
// willing to accept a *-with-SHA1 certificate (e.g. with a custom
// VerifyConnection or by a direct match with the CertPool), than that the
// peer would have a better certificate but is just choosing not to send it.
// crypto/x509 will refuse to verify important SHA-1 signatures anyway.
if isCert {
return false
}
// TLS 1.3 removed support for PKCS#1 v1.5 and SHA-1 signatures,
// and Go 1.25 removed support for SHA-1 signatures in TLS 1.2.
if version > VersionTLS12 {
sigType, sigHash, _ := typeAndHashFromSignatureScheme(s)
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
return true
}
} else { //if tlssha1.Value() != "1" {
_, sigHash, _ := typeAndHashFromSignatureScheme(s)
if sigHash == crypto.SHA1 {
return true
}
}
return false
}
// supportedSignatureAlgorithmsCert returns the supported algorithms for
// signatures in certificates.
func supportedSignatureAlgorithmsCert() []SignatureScheme {
sigAlgs := defaultSupportedSignatureAlgorithmsCert()
if fips140tls.Required() {
sigAlgs = slices.DeleteFunc(sigAlgs, func(s SignatureScheme) bool {
return !slices.Contains(allowedSignatureAlgorithmsFIPS, s)
})
}
return sigAlgs
func supportedSignatureAlgorithmsCert(minVers, maxVers uint16) []SignatureScheme {
sigAlgs := defaultSupportedSignatureAlgorithms()
return slices.DeleteFunc(sigAlgs, func(s SignatureScheme) bool {
for v := minVers; v <= maxVers; v++ {
if !isDisabledSignatureAlgorithm(v, s, true) {
return false
}
}
return true
})
}
func isSupportedSignatureAlgorithm(sigAlg SignatureScheme, supportedSignatureAlgorithms []SignatureScheme) bool {
@@ -1806,3 +1956,43 @@ func fipsAllowChain(chain []*x509.Certificate) bool {
return true
}
// anyValidVerifiedChain reports if at least one of the chains in verifiedChains
// is valid, as indicated by none of the certificates being expired and the root
// being in opts.Roots (or in the system root pool if opts.Roots is nil). If
// verifiedChains is empty, it returns false.
func anyValidVerifiedChain(verifiedChains [][]*x509.Certificate, opts x509.VerifyOptions) bool {
for _, chain := range verifiedChains {
if len(chain) == 0 {
continue
}
if slices.ContainsFunc(chain, func(cert *x509.Certificate) bool {
return opts.CurrentTime.Before(cert.NotBefore) || opts.CurrentTime.After(cert.NotAfter)
}) {
continue
}
// Since we already validated the chain, we only care that it is rooted
// in a CA in opts.Roots. On platforms where we control chain validation
// (e.g. not Windows or macOS) this is a simple lookup in the CertPool
// internal hash map, which we can simulate by running Verify on the
// root. On other platforms, we have to do full verification again,
// because EKU handling might differ. We will want to replace this with
// CertPool.Contains if/once that is available. See go.dev/issue/77376.
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
opts.Intermediates = x509.NewCertPool()
for _, cert := range chain[1:max(1, len(chain)-1)] {
opts.Intermediates.AddCert(cert)
}
leaf := chain[0]
if _, err := leaf.Verify(opts); err == nil {
return true
}
} else {
root := chain[len(chain)-1]
if _, err := root.Verify(opts); err == nil {
return true
}
}
}
return false
}
+21 -4
View File
@@ -18,6 +18,9 @@ func _() {
_ = x[ECDSAWithP384AndSHA384-1283]
_ = x[ECDSAWithP521AndSHA512-1539]
_ = x[Ed25519-2055]
_ = x[MLDSA44-2308]
_ = x[MLDSA65-2309]
_ = x[MLDSA87-2310]
_ = x[PKCS1WithSHA1-513]
_ = x[ECDSAWithSHA1-515]
}
@@ -32,10 +35,12 @@ const (
_SignatureScheme_name_6 = "PKCS1WithSHA512"
_SignatureScheme_name_7 = "ECDSAWithP521AndSHA512"
_SignatureScheme_name_8 = "PSSWithSHA256PSSWithSHA384PSSWithSHA512Ed25519"
_SignatureScheme_name_9 = "MLDSA44MLDSA65MLDSA87"
)
var (
_SignatureScheme_index_8 = [...]uint8{0, 13, 26, 39, 46}
_SignatureScheme_index_9 = [...]uint8{0, 7, 14, 21}
)
func (i SignatureScheme) String() string {
@@ -59,6 +64,9 @@ func (i SignatureScheme) String() string {
case 2052 <= i && i <= 2055:
i -= 2052
return _SignatureScheme_name_8[_SignatureScheme_index_8[i]:_SignatureScheme_index_8[i+1]]
case 2308 <= i && i <= 2310:
i -= 2308
return _SignatureScheme_name_9[_SignatureScheme_index_9[i]:_SignatureScheme_index_9[i+1]]
default:
return "SignatureScheme(" + strconv.FormatInt(int64(i), 10) + ")"
}
@@ -72,16 +80,21 @@ func _() {
_ = x[CurveP521-25]
_ = x[X25519-29]
_ = x[X25519MLKEM768-4588]
_ = x[SecP256r1MLKEM768-4587]
_ = x[SecP384r1MLKEM1024-4589]
_ = x[MLKEM1024-514]
}
const (
_CurveID_name_0 = "CurveP256CurveP384CurveP521"
_CurveID_name_1 = "X25519"
_CurveID_name_2 = "X25519MLKEM768"
_CurveID_name_2 = "MLKEM1024"
_CurveID_name_3 = "SecP256r1MLKEM768X25519MLKEM768SecP384r1MLKEM1024"
)
var (
_CurveID_index_0 = [...]uint8{0, 9, 18, 27}
_CurveID_index_3 = [...]uint8{0, 17, 31, 49}
)
func (i CurveID) String() string {
@@ -91,8 +104,11 @@ func (i CurveID) String() string {
return _CurveID_name_0[_CurveID_index_0[i]:_CurveID_index_0[i+1]]
case i == 29:
return _CurveID_name_1
case i == 4588:
case i == 514:
return _CurveID_name_2
case 4587 <= i && i <= 4589:
i -= 4587
return _CurveID_name_3[_CurveID_index_3[i]:_CurveID_index_3[i+1]]
default:
return "CurveID(" + strconv.FormatInt(int64(i), 10) + ")"
}
@@ -113,8 +129,9 @@ const _ClientAuthType_name = "NoClientCertRequestClientCertRequireAnyClientCertV
var _ClientAuthType_index = [...]uint8{0, 12, 29, 49, 72, 98}
func (i ClientAuthType) String() string {
if i < 0 || i >= ClientAuthType(len(_ClientAuthType_index)-1) {
idx := int(i) - 0
if i < 0 || idx >= len(_ClientAuthType_index)-1 {
return "ClientAuthType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ClientAuthType_name[_ClientAuthType_index[i]:_ClientAuthType_index[i+1]]
return _ClientAuthType_name[_ClientAuthType_index[idx]:_ClientAuthType_index[idx+1]]
}
+218 -75
View File
@@ -25,11 +25,13 @@ import (
// A Conn represents a secured connection.
// It implements the net.Conn interface.
type Conn struct {
//////////////////////////////////// [REALITY] SECTION: define var
AuthKey []byte
ClientVer [3]byte
ClientTime time.Time
ClientShortId [8]byte
MaxUselessRecords int
//////////////////////////////////// [REALITY] SECTION END
// constant
conn net.Conn
@@ -56,9 +58,11 @@ type Conn struct {
didHRR bool // whether a HelloRetryRequest was sent/received
cipherSuite uint16
curveID CurveID
peerSigAlg SignatureScheme
ocspResponse []byte // stapled OCSP response
scts [][]byte // signed certificate timestamps from server
peerCertificates []*x509.Certificate
localCertificate [][]byte
// verifiedChains contains the certificate chains that we built, as
// opposed to the ones presented by the server.
verifiedChains [][]*x509.Certificate
@@ -103,12 +107,27 @@ type Conn struct {
clientProtocol string
// input/output
in, out halfConn
rawInput bytes.Buffer // raw input, starting with a record header
input bytes.Reader // application data waiting to be read, from rawInput.Next
hand bytes.Buffer // handshake data waiting to be read
buffering bool // whether records are buffered in sendBuf
sendBuf []byte // a buffer of records waiting to be sent
in, out halfConn
// rawInput holds raw input, starting with a record header.
// It is nil when no input is buffered, in which case the buffer has
// been returned to rawInputPool so that connections idle in Read do
// not pin a record-sized buffer. It is lazily repopulated from the
// pool by readFromUntil.
rawInput *bytes.Buffer
// smallInput is a small buffer that serves as rawInput while
// waiting for a record header after rawInput has been returned to
// rawInputPool. It is lazily allocated by readFromUntil and then
// kept for the life of the connection.
smallInput *bytes.Buffer
// input holds application data waiting to be read, from rawInput.Next.
input bytes.Reader
// hand holds handshake data waiting to be read.
// It is nil when no handshake data is buffered, in which case the
// buffer has been returned to handPool. Use handBuf and handLen to
// access it.
hand *bytes.Buffer
buffering bool // whether records are buffered in sendBuf
sendBuf []byte // a buffer of records waiting to be sent
// bytesSent counts the bytes of application data sent.
// packetsSent counts packets.
@@ -171,8 +190,10 @@ func (c *Conn) NetConn() net.Conn {
// A halfConn represents one direction of the record layer
// connection, either sending or receiving.
type halfConn struct {
//////////////////////////////////// [REALITY] SECTION: define var
handshakeLen [7]int
handshakeBuf []byte
//////////////////////////////////// [REALITY] SECTION END
sync.Mutex
@@ -227,20 +248,19 @@ func (hc *halfConn) changeCipherSpec() error {
hc.mac = hc.nextMac
hc.nextCipher = nil
hc.nextMac = nil
for i := range hc.seq {
hc.seq[i] = 0
}
clear(hc.seq[:])
return nil
}
// setTrafficSecret sets the traffic secret for the given encryption level. setTrafficSecret
// should not be called directly, but rather through the Conn setWriteTrafficSecret and
// setReadTrafficSecret wrapper methods.
func (hc *halfConn) setTrafficSecret(suite *cipherSuiteTLS13, level QUICEncryptionLevel, secret []byte) {
hc.trafficSecret = secret
hc.level = level
key, iv := suite.trafficKey(secret)
hc.cipher = suite.aead(key, iv)
for i := range hc.seq {
hc.seq[i] = 0
}
clear(hc.seq[:])
}
// incSeq increments the sequence number.
@@ -528,6 +548,7 @@ func (hc *halfConn) encrypt(record, payload []byte, rand io.Reader) ([]byte, err
// Encrypt the actual ContentType and replace the plaintext one.
record = append(record, record[0])
//////////////////////////////////// [REALITY] SECTION: mimic recorded handshakeLen
padding := 0
if recordType(record[0]) == recordTypeHandshake && hc.handshakeLen[1] != 0 {
switch payload[0] {
@@ -558,6 +579,7 @@ func (hc *halfConn) encrypt(record, payload []byte, rand io.Reader) ([]byte, err
record[0] = byte(recordTypeApplicationData)
n := len(record) + c.Overhead() - recordHeaderLen
//////////////////////////////////// [REALITY] SECTION END
record[3] = byte(n >> 8)
record[4] = byte(n)
@@ -615,7 +637,9 @@ func (e RecordHeaderError) Error() string { return "tls: " + e.Msg }
func (c *Conn) newRecordHeaderError(conn net.Conn, msg string) (err RecordHeaderError) {
err.Msg = msg
err.Conn = conn
copy(err.RecordHeader[:], c.rawInput.Bytes())
if c.rawInput != nil {
copy(err.RecordHeader[:], c.rawInput.Bytes())
}
return err
}
@@ -657,6 +681,19 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
return c.in.setErrorLocked(errors.New("tls: internal error: attempted to read record with QUIC transport"))
}
// If rawInput is empty, we are about to block in a Read on the
// underlying connection waiting for the next record, possibly for a
// long time. A previous record may have grown rawInput to the maximum
// record size; don't pin that memory while idle. Return the buffer to
// the pool, and let readFromUntil read the header into a small buffer
// and switch back to a pooled record-sized buffer only once the
// payload length is known.
if c.rawInput != nil && c.rawInput.Len() == 0 && c.rawInput != c.smallInput && c.rawInput.Cap() > maxIdleInputCap {
c.rawInput.Reset()
rawInputPool.Put(c.rawInput)
c.rawInput = nil
}
// Read header, payload.
if err := c.readFromUntil(c.conn, recordHeaderLen); err != nil {
// RFC 8446, Section 6.1 suggests that EOF without an alertCloseNotify
@@ -731,13 +768,13 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
}
if typ != recordTypeAlert && typ != recordTypeChangeCipherSpec && len(data) > 0 {
if (typ == recordTypeApplicationData || (typ == recordTypeHandshake && !handshakeComplete)) && len(data) > 0 {
// This is a state-advancing message: reset the retry count.
c.retryCount = 0
}
// Handshake messages MUST NOT be interleaved with other record types in TLS 1.3.
if c.vers == VersionTLS13 && typ != recordTypeHandshake && c.hand.Len() > 0 {
if c.vers == VersionTLS13 && typ != recordTypeHandshake && c.handLen() > 0 {
return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
}
@@ -782,7 +819,7 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
return c.in.setErrorLocked(c.sendAlert(alertDecodeError))
}
// Handshake messages are not allowed to fragment across the CCS.
if c.hand.Len() > 0 {
if c.handLen() > 0 {
return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
}
// In TLS 1.3, change_cipher_spec records are ignored until the
@@ -790,7 +827,9 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
// 5, a server can send a ChangeCipherSpec before its ServerHello, when
// c.vers is still unset. That's not useful though and suspicious if the
// server then selects a lower protocol version, so don't allow that.
//////////////////////////////////// [REALITY] SECTION: reject change_cipher_spec record after handshake in TLS 1.3
if c.vers == VersionTLS13 && !handshakeComplete {
//////////////////////////////////// [REALITY] SECTION END
return c.retryReadRecord(expectChangeCipherSpec)
}
if !expectChangeCipherSpec {
@@ -818,7 +857,7 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
if len(data) == 0 || expectChangeCipherSpec {
return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
}
c.hand.Write(data)
c.handBuf().Write(data)
}
return nil
@@ -828,52 +867,125 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
// a warning alert, empty application_data, or a change_cipher_spec in TLS 1.3.
func (c *Conn) retryReadRecord(expectChangeCipherSpec bool) error {
c.retryCount++
//////////////////////////////////// [REALITY] SECTION: mimic recorded maxUselessRecords
if c.MaxUselessRecords <= 0 {
c.MaxUselessRecords = maxUselessRecords
}
if c.retryCount > c.MaxUselessRecords {
//////////////////////////////////// [REALITY] SECTION END
c.sendAlert(alertUnexpectedMessage)
return c.in.setErrorLocked(errors.New("tls: too many ignored records"))
}
return c.readRecordOrCCS(expectChangeCipherSpec)
}
// atLeastReader reads from R, stopping with EOF once at least N bytes have been
// read. It is different from an io.LimitedReader in that it doesn't cut short
// the last Read call, and in that it considers an early EOF an error.
type atLeastReader struct {
R io.Reader
N int64
// rawInputPool pools the record-sized buffers that back Conn.rawInput
// while records are being received. A connection returns its buffer to
// the pool before blocking to wait for a new record, often for a long
// time, so that idle connections do not each pin a record-sized buffer.
// Only buffers with capacity above maxIdleInputCap are pooled; smaller
// buffers stay attached to their connection.
var rawInputPool = sync.Pool{New: func() any { return new(bytes.Buffer) }}
// maxIdleInputCap is the largest rawInput capacity that a connection
// keeps while waiting for a new record to arrive. It is large enough to
// hold a record header and small records, so that only connections
// receiving larger records pay for the pooled buffer switch below.
const maxIdleInputCap = 1024
// handPool pools the buffers that back Conn.hand, which typically grow
// to hold the peer's largest flight of handshake messages. A connection
// returns its buffer to the pool once the handshake completes and after
// buffered post-handshake messages have been consumed, so that
// established connections do not pin it.
var handPool = sync.Pool{New: func() any { return new(bytes.Buffer) }}
// handBuf returns c.hand for writing, getting a buffer from handPool if
// c.hand is nil.
func (c *Conn) handBuf() *bytes.Buffer {
if c.hand == nil {
c.hand = handPool.Get().(*bytes.Buffer)
}
return c.hand
}
func (r *atLeastReader) Read(p []byte) (int, error) {
if r.N <= 0 {
return 0, io.EOF
// handLen returns the number of buffered handshake bytes.
func (c *Conn) handLen() int {
if c.hand == nil {
return 0
}
n, err := r.R.Read(p)
r.N -= int64(n) // won't underflow unless len(p) >= n > 9223372036854775809
if r.N > 0 && err == io.EOF {
return n, io.ErrUnexpectedEOF
return c.hand.Len()
}
// releaseHand returns c.hand to handPool if it is empty.
func (c *Conn) releaseHand() {
if c.hand != nil && c.hand.Len() == 0 {
c.hand.Reset()
handPool.Put(c.hand)
c.hand = nil
}
if r.N <= 0 && err == nil {
return n, io.EOF
}
return n, err
}
// readFromUntil reads from r into c.rawInput until c.rawInput contains
// at least n bytes or else returns an error.
func (c *Conn) readFromUntil(r io.Reader, n int) error {
if c.rawInput == nil {
// The record buffer was released while waiting for a new
// record. Block for the header using the connection's small
// buffer; the switch to a pooled record-sized buffer below
// happens only once the payload length is known and data is
// flowing.
if c.smallInput == nil {
c.smallInput = new(bytes.Buffer)
}
c.rawInput = c.smallInput
}
if c.rawInput.Len() >= n {
return nil
}
needs := n - c.rawInput.Len()
if want := c.rawInput.Len() + needs + bytes.MinRead; want > maxIdleInputCap && want > c.rawInput.Cap() {
// Growing past maxIdleInputCap: switch to a pooled buffer so
// that record-sized buffers are recycled across connections
// rather than allocated for every record.
b := rawInputPool.Get().(*bytes.Buffer)
b.Write(c.rawInput.Bytes())
if c.rawInput == c.smallInput {
c.smallInput.Reset()
} else if c.rawInput.Cap() > maxIdleInputCap {
c.rawInput.Reset()
rawInputPool.Put(c.rawInput)
}
c.rawInput = b
}
// There might be extra input waiting on the wire. Make a best effort
// attempt to fetch it so that it can be used in (*Conn).Read to
// "predict" closeNotify alerts.
// TODO(dmo): we use bytes.MinRead here because we used the buffer
// ReadFrom mechanism to avoid allocations, but we've hoisted this
// loop for performance. We really should use our own heuristic here
// for how much to read ahead.
c.rawInput.Grow(needs + bytes.MinRead)
_, err := c.rawInput.ReadFrom(&atLeastReader{r, int64(needs)})
return err
for {
buf := c.rawInput.AvailableBuffer()[:c.rawInput.Available()]
n, err := r.Read(buf)
// This write is just to update the internal state of the
// rawInput bytes.Buffer. It cannot fail.
c.rawInput.Write(buf[:n])
needs -= n
if needs <= 0 {
if err == io.EOF {
err = nil
}
return err
}
if err == io.EOF {
return io.ErrUnexpectedEOF
}
if err != nil {
return err
}
}
}
// sendAlertLocked sends a TLS alert message.
@@ -1100,6 +1212,7 @@ func (c *Conn) writeHandshakeRecord(msg handshakeMessage, transcript transcriptH
transcript.Write(data)
}
//////////////////////////////////// [REALITY] SECTION: mimic recorded handshakeBuf
if c.out.handshakeBuf != nil && len(data) > 0 && data[0] != typeServerHello {
c.out.handshakeBuf = append(c.out.handshakeBuf, data...)
if data[0] != typeFinished {
@@ -1131,6 +1244,7 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (int, error) {
return c.writeRecordLocked(typ, data)
}
//////////////////////////////////// [REALITY] SECTION END
// writeChangeCipherRecord writes a ChangeCipherSpec message to the connection and
// updates the record layer state.
@@ -1146,7 +1260,7 @@ func (c *Conn) readHandshakeBytes(n int) error {
if c.quic != nil {
return c.quicReadHandshakeBytes(n)
}
for c.hand.Len() < n {
for c.handLen() < n {
if err := c.readRecord(); err != nil {
return err
}
@@ -1379,7 +1493,9 @@ func (c *Conn) handlePostHandshakeMessage() error {
return err
}
c.retryCount++
//////////////////////////////////// [REALITY] SECTION: mimic recorded maxUselessRecords
if c.retryCount > c.MaxUselessRecords {
//////////////////////////////////// [REALITY] SECTION END
c.sendAlert(alertUnexpectedMessage)
return c.in.setErrorLocked(errors.New("tls: too many non-advancing records"))
}
@@ -1409,9 +1525,6 @@ func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error {
return c.in.setErrorLocked(c.sendAlert(alertInternalError))
}
newSecret := cipherSuite.nextTrafficSecret(c.in.trafficSecret)
c.in.setTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret)
if keyUpdate.updateRequested {
c.out.Lock()
defer c.out.Unlock()
@@ -1429,7 +1542,12 @@ func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error {
}
newSecret := cipherSuite.nextTrafficSecret(c.out.trafficSecret)
c.out.setTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret)
c.setWriteTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret)
}
newSecret := cipherSuite.nextTrafficSecret(c.in.trafficSecret)
if err := c.setReadTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret, keyUpdate.updateRequested); err != nil {
return err
}
return nil
@@ -1458,11 +1576,12 @@ func (c *Conn) Read(b []byte) (int, error) {
if err := c.readRecord(); err != nil {
return 0, err
}
for c.hand.Len() > 0 {
for c.handLen() > 0 {
if err := c.handlePostHandshakeMessage(); err != nil {
return 0, err
}
}
c.releaseHand()
}
n, _ := c.input.Read(b)
@@ -1590,37 +1709,23 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
}
handshakeCtx, cancel := context.WithCancel(ctx)
// Note: defer this before starting the "interrupter" goroutine
// Note: defer this before calling context.AfterFunc
// so that we can tell the difference between the input being canceled and
// this cancellation. In the former case, we need to close the connection.
defer cancel()
if c.quic != nil {
c.quic.cancelc = handshakeCtx.Done()
c.quic.ctx = handshakeCtx
c.quic.cancel = cancel
} else if ctx.Done() != nil {
// Start the "interrupter" goroutine, if this context might be canceled.
// (The background context cannot).
//
// The interrupter goroutine waits for the input context to be done and
// closes the connection if this happens before the function returns.
done := make(chan struct{})
interruptRes := make(chan error, 1)
// Close the connection if ctx is canceled before the function returns.
stop := context.AfterFunc(ctx, func() {
_ = c.conn.Close()
})
defer func() {
close(done)
if ctxErr := <-interruptRes; ctxErr != nil {
if !stop() {
// Return context error to user.
ret = ctxErr
}
}()
go func() {
select {
case <-handshakeCtx.Done():
// Close the connection, discarding the error
_ = c.conn.Close()
interruptRes <- handshakeCtx.Err()
case <-done:
interruptRes <- nil
ret = ctx.Err()
}
}()
}
@@ -1654,17 +1759,27 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
panic("tls: internal error: handshake returned an error but is marked successful")
}
// The handshake buffer typically grew to hold the peer's largest
// flight of handshake messages and is now empty. Post-handshake
// messages are rare and small, so release the buffer rather than
// pinning it for the life of the connection.
if c.handshakeErr == nil {
c.releaseHand()
}
if c.quic != nil {
if c.handshakeErr == nil {
c.quicHandshakeComplete()
// Provide the 1-RTT read secret now that the handshake is complete.
// The QUIC layer MUST NOT decrypt 1-RTT packets prior to completing
// the handshake (RFC 9001, Section 5.7).
c.quicSetReadSecret(QUICEncryptionLevelApplication, c.cipherSuite, c.in.trafficSecret)
if err := c.quicSetReadSecret(QUICEncryptionLevelApplication, c.cipherSuite, c.in.trafficSecret); err != nil {
return err
}
} else {
var a alert
c.out.Lock()
if !errors.As(c.out.err, &a) {
a, ok := errors.AsType[alert](c.out.err)
if !ok {
a = alertInternalError
}
c.out.Unlock()
@@ -1682,6 +1797,12 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
}
// ConnectionState returns basic TLS details about the connection.
//
// The returned [ConnectionState] is only meaningful after the handshake has
// completed, as reported by [ConnectionState.HandshakeComplete]; before then
// its fields are not populated. The handshake is run automatically by the
// first [Conn.Read] or [Conn.Write], or it can be triggered explicitly with
// [Conn.Handshake].
func (c *Conn) ConnectionState() ConnectionState {
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
@@ -1694,12 +1815,14 @@ func (c *Conn) connectionStateLocked() ConnectionState {
state.Version = c.vers
state.NegotiatedProtocol = c.clientProtocol
state.DidResume = c.didResume
state.testingOnlyDidHRR = c.didHRR
state.HelloRetryRequest = c.didHRR
state.testingOnlyPeerSignatureAlgorithm = c.peerSigAlg
state.CurveID = c.curveID
state.NegotiatedProtocolIsMutual = true
state.ServerName = c.serverName
state.CipherSuite = c.cipherSuite
state.PeerCertificates = c.peerCertificates
state.LocalCertificate = c.localCertificate
state.VerifiedChains = c.verifiedChains
state.SignedCertificateTimestamps = c.scts
state.OCSPResponse = c.ocspResponse
@@ -1713,13 +1836,7 @@ func (c *Conn) connectionStateLocked() ConnectionState {
if c.config.Renegotiation != RenegotiateNever {
state.ekm = noEKMBecauseRenegotiation
} else if c.vers != VersionTLS13 && !c.extMasterSecret {
state.ekm = func(label string, context []byte, length int) ([]byte, error) {
// if ekmgodebug.Value() == "1" {
// ekmgodebug.IncNonDefault()
// return c.ekm(label, context, length)
// }
return noEKMBecauseNoEMS(label, context, length)
}
state.ekm = noEKMBecauseNoEMS
} else {
state.ekm = c.ekm
}
@@ -1753,3 +1870,29 @@ func (c *Conn) VerifyHostname(host string) error {
}
return c.peerCertificates[0].VerifyHostname(host)
}
// setReadTrafficSecret sets the read traffic secret for the given encryption level. If
// being called at the same time as setWriteTrafficSecret, the caller must ensure the call
// to setWriteTrafficSecret happens first so any alerts are sent at the write level.
func (c *Conn) setReadTrafficSecret(suite *cipherSuiteTLS13, level QUICEncryptionLevel, secret []byte, locked bool) error {
// Ensure that there are no buffered handshake messages before changing the
// read keys, since that can cause messages to be parsed that were encrypted
// using old keys which are no longer appropriate.
if c.handLen() != 0 {
if locked {
c.sendAlertLocked(alertUnexpectedMessage)
} else {
c.sendAlert(alertUnexpectedMessage)
}
return errors.New("tls: handshake buffer not empty before setting read traffic secret")
}
c.in.setTrafficSecret(suite, level, secret)
return nil
}
// setWriteTrafficSecret sets the write traffic secret for the given encryption level. If
// being called at the same time as setReadTrafficSecret, the caller must ensure the call
// to setWriteTrafficSecret happens first so any alerts are sent at the write level.
func (c *Conn) setWriteTrafficSecret(suite *cipherSuiteTLS13, level QUICEncryptionLevel, secret []byte) {
c.out.setTrafficSecret(suite, level, secret)
}
+28 -37
View File
@@ -12,18 +12,35 @@ import (
// Defaults are collected in this file to allow distributions to more easily patch
// them to apply local policies.
// tlsmlkem=0 restores the pre-Go 1.24 default key exchanges.
//var tlsmlkem = godebug.New("tlsmlkem")
// defaultCurvePreferences is the default set of supported key exchanges, as
// well as the preference order.
func defaultCurvePreferences() []CurveID {
if false {
return []CurveID{X25519, CurveP256, CurveP384, CurveP521}
// tlssecpmlkem=0 restores the pre-Go 1.26 default key exchanges.
//var tlssecpmlkem = godebug.New("tlssecpmlkem")
// defaultCurveEnabled returns whether the key exchange c is enabled by default.
func defaultCurveEnabled(c CurveID) bool {
switch c {
case X25519, CurveP256, CurveP384, CurveP521:
return true
case X25519MLKEM768:
return true//tlsmlkem.Value() != "0"
case SecP256r1MLKEM768, SecP384r1MLKEM1024:
return true//tlsmlkem.Value() != "0" && tlssecpmlkem.Value() != "0"
default:
return false
}
return []CurveID{X25519MLKEM768, X25519, CurveP256, CurveP384, CurveP521}
}
//var tlssha1 = godebug.New("tlssha1")
// curvePreferenceOrder is the fixed preference order of key exchanges. It must
// include every supported key exchange.
func curvePreferenceOrder() []CurveID {
return []CurveID{
X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024, MLKEM1024,
X25519, CurveP256, CurveP384, CurveP521,
}
}
// defaultSupportedSignatureAlgorithms returns the signature and hash algorithms that
// the code advertises and supports in a TLS 1.2+ ClientHello and in a TLS 1.2+
@@ -31,30 +48,9 @@ func defaultCurvePreferences() []CurveID {
// Note that in TLS 1.2, the ECDSA algorithms are not constrained to P-256, etc.
func defaultSupportedSignatureAlgorithms() []SignatureScheme {
return []SignatureScheme{
PSSWithSHA256,
ECDSAWithP256AndSHA256,
Ed25519,
PSSWithSHA384,
PSSWithSHA512,
PKCS1WithSHA256,
PKCS1WithSHA384,
PKCS1WithSHA512,
ECDSAWithP384AndSHA384,
ECDSAWithP521AndSHA512,
}
}
// defaultSupportedSignatureAlgorithmsCert returns the signature algorithms that
// the code advertises as supported for signatures in certificates.
//
// We include all algorithms, including SHA-1 and PKCS#1 v1.5, because it's more
// likely that something on our side will be willing to accept a *-with-SHA1
// certificate (e.g. with a custom VerifyConnection or by a direct match with
// the CertPool), than that the peer would have a better certificate but is just
// choosing not to send it. crypto/x509 will refuse to verify important SHA-1
// signatures anyway.
func defaultSupportedSignatureAlgorithmsCert() []SignatureScheme {
return []SignatureScheme{
MLDSA44,
MLDSA65,
MLDSA87,
PSSWithSHA256,
ECDSAWithP256AndSHA256,
Ed25519,
@@ -70,9 +66,6 @@ func defaultSupportedSignatureAlgorithmsCert() []SignatureScheme {
}
}
//var tlsrsakex = godebug.New("tlsrsakex")
//var tls3des = godebug.New("tls3des")
func supportedCipherSuites(aesGCMPreferred bool) []uint16 {
if aesGCMPreferred {
return slices.Clone(cipherSuitesPreferenceOrder)
@@ -84,9 +77,7 @@ func supportedCipherSuites(aesGCMPreferred bool) []uint16 {
func defaultCipherSuites(aesGCMPreferred bool) []uint16 {
cipherSuites := supportedCipherSuites(aesGCMPreferred)
return slices.DeleteFunc(cipherSuites, func(c uint16) bool {
return disabledCipherSuites[c] ||
rsaKexCiphers[c] ||
tdesCiphers[c]
return disabledCipherSuites[c]
})
}
+10
View File
@@ -10,6 +10,7 @@ import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/mldsa"
"crypto/rsa"
"crypto/x509"
)
@@ -32,6 +33,9 @@ var (
}
allowedCurvePreferencesFIPS = []CurveID{
X25519MLKEM768,
SecP256r1MLKEM768,
SecP384r1MLKEM1024,
MLKEM1024,
CurveP256,
CurveP384,
CurveP521,
@@ -40,6 +44,9 @@ var (
PSSWithSHA256,
ECDSAWithP256AndSHA256,
Ed25519,
MLDSA44,
MLDSA65,
MLDSA87,
PSSWithSHA384,
PSSWithSHA512,
PKCS1WithSHA256,
@@ -70,6 +77,9 @@ func isCertificateAllowedFIPS(c *x509.Certificate) bool {
return k.Curve == elliptic.P256() || k.Curve == elliptic.P384() || k.Curve == elliptic.P521()
case ed25519.PublicKey:
return true
case *mldsa.PublicKey:
// Only for the native module.
return true //!boring.Enabled
default:
return false
}
+54 -68
View File
@@ -6,28 +6,14 @@ package reality
import (
"bytes"
"crypto/hpke"
"errors"
"fmt"
"slices"
"strings"
"golang.org/x/crypto/cryptobyte"
"github.com/xtls/reality/hpke"
)
// sortedSupportedAEADs is just a sorted version of hpke.SupportedAEADS.
// We need this so that when we insert them into ECHConfigs the ordering
// is stable.
var sortedSupportedAEADs []uint16
func init() {
for aeadID := range hpke.SupportedAEADs {
sortedSupportedAEADs = append(sortedSupportedAEADs, aeadID)
}
slices.Sort(sortedSupportedAEADs)
}
type EchCipher struct {
KDFID uint16
AEADID uint16
@@ -69,7 +55,7 @@ func (e *echConfigErr) Error() string {
func parseECHConfig(enc []byte) (skip bool, ec EchConfig, err error) {
s := cryptobyte.String(enc)
ec.raw = []byte(enc)
ec.raw = enc
if !s.ReadUint16(&ec.Version) {
return false, EchConfig{}, &echConfigErr{"version"}
}
@@ -79,7 +65,7 @@ func parseECHConfig(enc []byte) (skip bool, ec EchConfig, err error) {
if len(ec.raw) < int(ec.Length)+4 {
return false, EchConfig{}, &echConfigErr{"length"}
}
ec.raw = ec.raw[:ec.Length+4]
ec.raw = ec.raw[:int(ec.Length)+4]
if ec.Version != extensionEncryptedClientHello {
s.Skip(int(ec.Length))
return true, EchConfig{}, nil
@@ -133,7 +119,7 @@ func parseECHConfig(enc []byte) (skip bool, ec EchConfig, err error) {
return false, ec, nil
}
// parseECHConfigList parses a draft-ietf-tls-esni-18 ECHConfigList, returning a
// parseECHConfigList parses a RFC 9849 ECHConfigList, returning a
// slice of parsed ECHConfigs, in the same order they were parsed, or an error
// if the list is malformed.
func parseECHConfigList(data []byte) ([]EchConfig, error) {
@@ -142,7 +128,7 @@ func parseECHConfigList(data []byte) ([]EchConfig, error) {
if !s.ReadUint16(&length) {
return nil, errMalformedECHConfigList
}
if length != uint16(len(data)-2) {
if int(length) != len(data)-2 {
return nil, errMalformedECHConfigList
}
var configs []EchConfig
@@ -150,7 +136,7 @@ func parseECHConfigList(data []byte) ([]EchConfig, error) {
if len(s) < 4 {
return nil, errors.New("tls: malformed ECHConfig")
}
configLen := uint16(s[2])<<8 | uint16(s[3])
configLen := int(s[2])<<8 | int(s[3])
skip, ec, err := parseECHConfig(s)
if err != nil {
return nil, err
@@ -163,25 +149,8 @@ func parseECHConfigList(data []byte) ([]EchConfig, error) {
return configs, nil
}
func pickECHConfig(list []EchConfig) *EchConfig {
func pickECHConfig(list []EchConfig) (*EchConfig, hpke.PublicKey, hpke.KDF, hpke.AEAD) {
for _, ec := range list {
if _, ok := hpke.SupportedKEMs[ec.KemID]; !ok {
continue
}
var validSCS bool
for _, cs := range ec.SymmetricCipherSuite {
if _, ok := hpke.SupportedAEADs[cs.AEADID]; !ok {
continue
}
if _, ok := hpke.SupportedKDFs[cs.KDFID]; !ok {
continue
}
validSCS = true
break
}
if !validSCS {
continue
}
if !validDNSName(string(ec.PublicName)) {
continue
}
@@ -197,25 +166,37 @@ func pickECHConfig(list []EchConfig) *EchConfig {
if unsupportedExt {
continue
}
return &ec
}
return nil
}
func pickECHCipherSuite(suites []EchCipher) (EchCipher, error) {
for _, s := range suites {
// NOTE: all of the supported AEADs and KDFs are fine, rather than
// imposing some sort of preference here, we just pick the first valid
// suite.
if _, ok := hpke.SupportedAEADs[s.AEADID]; !ok {
kem, err := hpke.NewKEM(ec.KemID)
if err != nil {
continue
}
if _, ok := hpke.SupportedKDFs[s.KDFID]; !ok {
pub, err := kem.NewPublicKey(ec.PublicKey)
if err != nil {
// This is an error in the config, but killing the connection feels
// excessive.
continue
}
return s, nil
for _, cs := range ec.SymmetricCipherSuite {
// All of the supported AEADs and KDFs are fine, rather than
// imposing some sort of preference here, we just pick the first
// valid suite.
kdf, err := hpke.NewKDF(cs.KDFID)
if err != nil {
continue
}
// 0xFFFF is an export-only AEAD that cannot seal/open, making
// it an invalid choice for encrypting ClientHelloInner.
if cs.AEADID == 0xFFFF {
continue
}
aead, err := hpke.NewAEAD(cs.AEADID)
if err != nil {
continue
}
return &ec, pub, kdf, aead
}
}
return EchCipher{}, errors.New("tls: no supported symmetric ciphersuites for ECH")
return nil, nil, nil, nil
}
func encodeInnerClientHello(inner *clientHelloMsg, maxNameLength int) ([]byte, error) {
@@ -231,7 +212,7 @@ func encodeInnerClientHello(inner *clientHelloMsg, maxNameLength int) ([]byte, e
} else {
paddingLen = maxNameLength + 9
}
paddingLen = 31 - ((len(h) + paddingLen - 1) % 32)
paddingLen += 31 - ((len(h) + paddingLen - 1) % 32)
return append(h, make([]byte, paddingLen)...), nil
}
@@ -569,16 +550,6 @@ func parseECHExt(ext []byte) (echType echExtType, cs EchCipher, configID uint8,
return echType, cs, configID, bytes.Clone(encap), bytes.Clone(payload), nil
}
func marshalEncryptedClientHelloConfigList(configs []EncryptedClientHelloKey) ([]byte, error) {
builder := cryptobyte.NewBuilder(nil)
builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {
for _, c := range configs {
builder.AddBytes(c.Config)
}
})
return builder.Bytes()
}
func (c *Conn) processECHClientHello(outer *clientHelloMsg, echKeys []EncryptedClientHelloKey) (*clientHelloMsg, *echServerContext, error) {
echType, echCiphersuite, configID, encap, payload, err := parseECHExt(outer.encryptedClientHello)
if err != nil {
@@ -601,20 +572,35 @@ func (c *Conn) processECHClientHello(outer *clientHelloMsg, echKeys []EncryptedC
for _, echKey := range echKeys {
skip, config, err := parseECHConfig(echKey.Config)
if err != nil || skip {
if err != nil {
c.sendAlert(alertInternalError)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKeys Config: %s", err)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config: %s", err)
}
if skip {
continue
}
echPriv, err := hpke.ParseHPKEPrivateKey(config.KemID, echKey.PrivateKey)
kem, err := hpke.NewKEM(config.KemID)
if err != nil {
c.sendAlert(alertInternalError)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKeys PrivateKey: %s", err)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config KEM: %s", err)
}
echPriv, err := kem.NewPrivateKey(echKey.PrivateKey)
if err != nil {
c.sendAlert(alertInternalError)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey PrivateKey: %s", err)
}
kdf, err := hpke.NewKDF(echCiphersuite.KDFID)
if err != nil {
c.sendAlert(alertInternalError)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config KDF: %s", err)
}
aead, err := hpke.NewAEAD(echCiphersuite.AEADID)
if err != nil {
c.sendAlert(alertInternalError)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config AEAD: %s", err)
}
info := append([]byte("tls ech\x00"), echKey.Config...)
hpkeContext, err := hpke.SetupRecipient(hpke.DHKEM_X25519_HKDF_SHA256, echCiphersuite.KDFID, echCiphersuite.AEADID, echPriv, info, encap)
hpkeContext, err := hpke.NewRecipient(encap, echPriv, kdf, aead, info)
if err != nil {
// attempt next trial decryption
continue
+8 -2
View File
@@ -13,6 +13,7 @@ import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/mldsa"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
@@ -35,6 +36,7 @@ var (
rsaBits = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set")
ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521")
ed25519Key = flag.Bool("ed25519", false, "Generate an Ed25519 key")
mldsaKey = flag.Bool("mldsa", false, "Generate an ML-DSA-44 key")
)
func publicKey(priv any) any {
@@ -45,6 +47,8 @@ func publicKey(priv any) any {
return &k.PublicKey
case ed25519.PrivateKey:
return k.Public().(ed25519.PublicKey)
case *mldsa.PrivateKey:
return k.PublicKey()
default:
return nil
}
@@ -63,6 +67,8 @@ func main() {
case "":
if *ed25519Key {
_, priv, err = ed25519.GenerateKey(rand.Reader)
} else if *mldsaKey {
priv, err = mldsa.GenerateKey(mldsa.MLDSA44())
} else {
priv, err = rsa.GenerateKey(rand.Reader, *rsaBits)
}
@@ -81,8 +87,8 @@ func main() {
log.Fatalf("Failed to generate private key: %v", err)
}
// ECDSA, ED25519 and RSA subject keys should have the DigitalSignature
// KeyUsage bits set in the x509.Certificate template
// ECDSA, ED25519, ML-DSA, and RSA subject keys should have the
// DigitalSignature KeyUsage bits set in the x509.Certificate template
keyUsage := x509.KeyUsageDigitalSignature
// Only RSA subject keys should have the KeyEncipherment KeyUsage bits set. In
// the context of TLS this KeyUsage is particular to RSA key exchange and
+7 -7
View File
@@ -1,18 +1,18 @@
module github.com/xtls/reality
go 1.24.0
go 1.27
require (
github.com/cloudflare/circl v1.6.3
github.com/cloudflare/circl v1.6.5
github.com/juju/ratelimit v1.0.2
github.com/pires/go-proxyproto v0.11.0
github.com/pires/go-proxyproto v0.15.0
github.com/refraction-networking/utls v1.8.2
golang.org/x/crypto v0.48.0
golang.org/x/sys v0.41.0
golang.org/x/crypto v0.57.0
golang.org/x/sys v0.48.0
)
require (
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/andybalholm/brotli v1.2.3 // indirect
github.com/klauspost/compress v1.20.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)
+14 -12
View File
@@ -1,23 +1,25 @@
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/andybalholm/brotli v1.2.3 h1:8H1qwOkl2LPfjf3YezB90JnCliZb6SInJ/OJkEbA5NQ=
github.com/andybalholm/brotli v1.2.3/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/cloudflare/circl v1.6.5 h1:O64F26HEqNhznd/hrC5KZXVKYuKM2rx4deZDTc4ihQA=
github.com/cloudflare/circl v1.6.5/go.mod h1:h5LNyxAc5nTue9DS5jT+48en2PSDYt3zdGnz5OstK6c=
github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pires/go-proxyproto v0.11.0 h1:gUQpS85X/VJMdUsYyEgyn59uLJvGqPhJV5YvG68wXH4=
github.com/pires/go-proxyproto v0.11.0/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
github.com/pires/go-proxyproto v0.15.0 h1:dTshmNbFm/D+0+sbrxUuddPOZ5Y0B7c5NhtsBkm6LqI=
github.com/pires/go-proxyproto v0.15.0/go.mod h1:OXsCrKwrK2tXS9YrI5tkHx5xaQlO8FH3lFW76orFh24=
github.com/refraction-networking/utls v1.8.2 h1:j4Q1gJj0xngdeH+Ox/qND11aEfhpgoEvV+S9iJ2IdQo=
github.com/refraction-networking/utls v1.8.2/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+82 -86
View File
@@ -10,11 +10,11 @@ import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/mlkem"
"crypto/hpke"
"crypto/mldsa"
"crypto/rsa"
"crypto/subtle"
"crypto/x509"
"encoding/binary"
"errors"
"fmt"
"hash"
@@ -25,7 +25,6 @@ import (
"time"
"github.com/xtls/reality/fips140tls"
"github.com/xtls/reality/hpke"
"github.com/xtls/reality/tls13"
)
@@ -41,8 +40,6 @@ type clientHandshakeState struct {
ticket []byte // a fresh ticket received during this handshake
}
var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme
func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echClientContext, error) {
config := c.config
if len(config.ServerName) == 0 && !config.InsecureSkipVerify {
@@ -61,7 +58,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCli
return nil, nil, nil, errors.New("tls: NextProtos values too large")
}
supportedVersions := config.supportedVersions(roleClient)
supportedVersions := config.supportedVersions(roleClient, c.quic != nil)
if len(supportedVersions) == 0 {
return nil, nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion")
}
@@ -123,11 +120,8 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCli
}
if maxVersion >= VersionTLS12 {
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms(minVersion)
hello.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithmsCert()
}
if testingOnlyForceClientHelloSignatureAlgorithms != nil {
hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms(minVersion, maxVersion)
hello.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithmsCert(minVersion, maxVersion)
}
var keyShareKeys *keySharePrivateKeys
@@ -146,45 +140,23 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCli
}
if len(hello.supportedCurves) == 0 {
return nil, nil, nil, errors.New("tls: no supported elliptic curves for ECDHE")
return nil, nil, nil, errors.New("tls: no supported key exchange methods (CurveIDs)")
}
// Since the order is fixed, the first one is always the one to send a
// key share for. All the PQ hybrids sort first, and produce a fallback
// ECDH share.
curveID := hello.supportedCurves[0]
keyShareKeys = &keySharePrivateKeys{curveID: curveID}
// Note that if X25519MLKEM768 is supported, it will be first because
// the preference order is fixed.
if curveID == X25519MLKEM768 {
keyShareKeys.ecdhe, err = generateECDHEKey(config.rand(), X25519)
if err != nil {
return nil, nil, nil, err
}
seed := make([]byte, mlkem.SeedSize)
if _, err := io.ReadFull(config.rand(), seed); err != nil {
return nil, nil, nil, err
}
keyShareKeys.mlkem, err = mlkem.NewDecapsulationKey768(seed)
if err != nil {
return nil, nil, nil, err
}
mlkemEncapsulationKey := keyShareKeys.mlkem.EncapsulationKey().Bytes()
x25519EphemeralKey := keyShareKeys.ecdhe.PublicKey().Bytes()
hello.keyShares = []keyShare{
{group: X25519MLKEM768, data: append(mlkemEncapsulationKey, x25519EphemeralKey...)},
}
// If both X25519MLKEM768 and X25519 are supported, we send both key
// shares (as a fallback) and we reuse the same X25519 ephemeral
// key, as allowed by draft-ietf-tls-hybrid-design-09, Section 3.2.
if slices.Contains(hello.supportedCurves, X25519) {
hello.keyShares = append(hello.keyShares, keyShare{group: X25519, data: x25519EphemeralKey})
}
} else {
if _, ok := curveForCurveID(curveID); !ok {
return nil, nil, nil, errors.New("tls: CurvePreferences includes unsupported curve")
}
keyShareKeys.ecdhe, err = generateECDHEKey(config.rand(), curveID)
if err != nil {
return nil, nil, nil, err
}
hello.keyShares = []keyShare{{group: curveID, data: keyShareKeys.ecdhe.PublicKey().Bytes()}}
ke, err := keyExchangeForCurveID(curveID)
if err != nil {
return nil, nil, nil, errors.New("tls: internal error: supportsCurve accepted unimplemented curve")
}
keyShareKeys, hello.keyShares, err = ke.keyShares(config.rand())
if err != nil {
return nil, nil, nil, err
}
// Only send the fallback ECDH share if the corresponding CurveID is enabled.
if len(hello.keyShares) == 2 && !slices.Contains(hello.supportedCurves, hello.keyShares[1].group) {
hello.keyShares = hello.keyShares[:1]
}
}
@@ -211,11 +183,11 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCli
if err != nil {
return nil, nil, nil, err
}
echConfig := pickECHConfig(echConfigs)
echConfig, echPK, kdf, aead := pickECHConfig(echConfigs)
if echConfig == nil {
return nil, nil, nil, errors.New("tls: EncryptedClientHelloConfigList contains no valid configs")
}
ech = &echClientContext{config: echConfig}
ech = &echClientContext{config: echConfig, kdfID: kdf.ID(), aeadID: aead.ID()}
hello.encryptedClientHello = []byte{1} // indicate inner hello
// We need to explicitly set these 1.2 fields to nil, as we do not
// marshal them when encoding the inner hello, otherwise transcripts
@@ -225,17 +197,8 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCli
hello.secureRenegotiationSupported = false
hello.extendedMasterSecret = false
echPK, err := hpke.ParseHPKEPublicKey(ech.config.KemID, ech.config.PublicKey)
if err != nil {
return nil, nil, nil, err
}
suite, err := pickECHCipherSuite(ech.config.SymmetricCipherSuite)
if err != nil {
return nil, nil, nil, err
}
ech.kdfID, ech.aeadID = suite.KDFID, suite.AEADID
info := append([]byte("tls ech\x00"), ech.config.raw...)
ech.encapsulatedKey, ech.hpkeContext, err = hpke.SetupSender(ech.config.KemID, suite.KDFID, suite.AEADID, echPK, info)
ech.encapsulatedKey, ech.hpkeContext, err = hpke.NewSender(echPK, kdf, aead, info)
if err != nil {
return nil, nil, nil, err
}
@@ -270,7 +233,6 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
if err != nil {
return err
}
c.serverName = hello.serverName
session, earlySecret, binderKey, err := c.loadSession(hello)
if err != nil {
@@ -324,7 +286,11 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
if hello.earlyData {
suite := cipherSuiteTLS13ByID(session.cipherSuite)
transcript := suite.hash.New()
if err := transcriptMsg(hello, transcript); err != nil {
transcriptHello := hello
if ech != nil {
transcriptHello = ech.innerHello
}
if err := transcriptMsg(transcriptHello, transcript); err != nil {
return err
}
earlyTrafficSecret := earlySecret.ClientEarlyTrafficSecret(transcript)
@@ -350,7 +316,7 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
// If we are negotiating a protocol version that's lower than what we
// support, check for the server downgrade canaries.
// See RFC 8446, Section 4.1.3.
maxVers := c.config.maxSupportedVersion(roleClient)
maxVers := c.config.maxSupportedVersion(roleClient, c.quic != nil)
tls12Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS12
tls11Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS11
if maxVers == VersionTLS13 && c.vers <= VersionTLS12 && (tls12Downgrade || tls11Downgrade) ||
@@ -433,9 +399,6 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (
return nil, nil, nil, nil
}
// Check that the cached server certificate is not expired, and that it's
// valid for the ServerName. This should be ensured by the cache key, but
// protect the application from a faulty ClientSessionCache implementation.
if c.config.time().After(session.peerCertificates[0].NotAfter) {
// Expired certificate, delete the entry.
c.config.ClientSessionCache.Put(cacheKey, nil)
@@ -447,6 +410,18 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (
return nil, nil, nil, nil
}
if err := session.peerCertificates[0].VerifyHostname(c.config.ServerName); err != nil {
// This should be ensured by the cache key, but protect the
// application from a faulty ClientSessionCache implementation.
return nil, nil, nil, nil
}
opts := x509.VerifyOptions{
CurrentTime: c.config.time(),
Roots: c.config.RootCAs,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
if !anyValidVerifiedChain(session.verifiedChains, opts) {
// No valid chains, delete the entry.
c.config.ClientSessionCache.Put(cacheKey, nil)
return nil, nil, nil, nil
}
}
@@ -534,7 +509,7 @@ func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error {
peerVersion = serverHello.supportedVersion
}
vers, ok := c.config.mutualVersion(roleClient, []uint16{peerVersion})
vers, ok := c.config.mutualVersion(roleClient, c.quic != nil, []uint16{peerVersion})
if !ok {
c.sendAlert(alertProtocolVersion)
return fmt.Errorf("tls: server selected unsupported protocol version %x", peerVersion)
@@ -725,8 +700,9 @@ func (hs *clientHandshakeState) doFullHandshake() error {
c.sendAlert(alertIllegalParameter)
return err
}
if len(skx.key) >= 3 && skx.key[0] == 3 /* named curve */ {
c.curveID = CurveID(binary.BigEndian.Uint16(skx.key[1:]))
if keyAgreement, ok := keyAgreement.(*ecdheKeyAgreement); ok {
c.curveID = keyAgreement.curveID
c.peerSigAlg = keyAgreement.signatureAlgorithm
}
msg, err = c.readHandshake(&hs.finishedHash)
@@ -753,6 +729,10 @@ func (hs *clientHandshakeState) doFullHandshake() error {
}
}
if chainToSend != nil {
hs.c.localCertificate = chainToSend.Certificate
}
shd, ok := msg.(*serverHelloDoneMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
@@ -807,37 +787,43 @@ func (hs *clientHandshakeState) doFullHandshake() error {
return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey)
}
var sigType uint8
var sigHash crypto.Hash
if c.vers >= VersionTLS12 {
signatureAlgorithm, err := selectSignatureScheme(c.vers, chainToSend, certReq.supportedSignatureAlgorithms)
if err != nil {
c.sendAlert(alertIllegalParameter)
c.sendAlert(alertHandshakeFailure)
return err
}
sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
sigType, sigHash, err := typeAndHashFromSignatureScheme(signatureAlgorithm)
if err != nil {
return c.sendAlert(alertInternalError)
}
certVerify.hasSignatureAlgorithm = true
certVerify.signatureAlgorithm = signatureAlgorithm
if hs.finishedHash.buffer == nil {
c.sendAlert(alertInternalError)
return errors.New("tls: internal error: did not keep handshake transcript for TLS 1.2")
}
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
certVerify.signature, err = crypto.SignMessage(key, c.config.rand(), hs.finishedHash.buffer, signOpts)
if err != nil {
c.sendAlert(alertInternalError)
return err
}
} else {
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(key.Public())
sigType, sigHash, err := legacyTypeAndHashFromPublicKey(key.Public())
if err != nil {
c.sendAlert(alertIllegalParameter)
return err
}
}
signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash)
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
certVerify.signature, err = key.Sign(c.config.rand(), signed, signOpts)
if err != nil {
c.sendAlert(alertInternalError)
return err
signed := hs.finishedHash.hashForClientCertificate(sigType)
certVerify.signature, err = key.Sign(c.config.rand(), signed, sigHash)
if err != nil {
c.sendAlert(alertInternalError)
return err
}
}
if _, err := hs.c.writeHandshakeRecord(certVerify, &hs.finishedHash); err != nil {
@@ -1175,9 +1161,19 @@ func (c *Conn) verifyServerCertificate(certificates [][]byte) error {
}
}
if fips140tls.Required() && !isCertificateAllowedFIPS(certs[0]) {
c.sendAlert(alertBadCertificate)
err := errors.New("server's certificate is not allowed in FIPS 140-3 mode")
return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}
}
switch certs[0].PublicKey.(type) {
case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
break
case *mldsa.PublicKey:
if c.vers < VersionTLS13 {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server's certificate uses ML-DSA, which requires TLS 1.3")
}
default:
c.sendAlert(alertUnsupportedCertificate)
return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey)
+34 -52
View File
@@ -10,7 +10,6 @@ import (
"crypto"
"crypto/hkdf"
"crypto/hmac"
"crypto/mlkem"
"crypto/rsa"
"crypto/subtle"
"errors"
@@ -55,7 +54,8 @@ func (hs *clientHandshakeStateTLS13) handshake() error {
}
// Consistency check on the presence of a keyShare and its parameters.
if hs.keyShareKeys == nil || hs.keyShareKeys.ecdhe == nil || len(hs.hello.keyShares) == 0 {
if hs.keyShareKeys == nil || (hs.keyShareKeys.ecdhe == nil && hs.keyShareKeys.mlkem == nil) ||
len(hs.hello.keyShares) == 0 {
return c.sendAlert(alertInternalError)
}
@@ -320,22 +320,18 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
}
// Note: we don't support selecting X25519MLKEM768 in a HRR, because it
// is currently first in preference order, so if it's enabled we'll
// always send a key share for it.
//
// This will have to change once we support multiple hybrid KEMs.
if _, ok := curveForCurveID(curveID); !ok {
ke, err := keyExchangeForCurveID(curveID)
if err != nil {
c.sendAlert(alertInternalError)
return errors.New("tls: CurvePreferences includes unsupported curve")
return errors.New("tls: internal error: supportsCurve accepted unimplemented curve")
}
key, err := generateECDHEKey(c.config.rand(), curveID)
hs.keyShareKeys, hello.keyShares, err = ke.keyShares(c.config.rand())
if err != nil {
c.sendAlert(alertInternalError)
return err
}
hs.keyShareKeys = &keySharePrivateKeys{curveID: curveID, ecdhe: key}
hello.keyShares = []keyShare{{group: curveID, data: key.PublicKey().Bytes()}}
// Do not send the fallback ECDH key share in a HRR response.
hello.keyShares = hello.keyShares[:1]
}
if len(hello.pskIdentities) > 0 {
@@ -475,36 +471,16 @@ func (hs *clientHandshakeStateTLS13) processServerHello() error {
func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
c := hs.c
ecdhePeerData := hs.serverHello.serverShare.data
if hs.serverHello.serverShare.group == X25519MLKEM768 {
if len(ecdhePeerData) != mlkem.CiphertextSize768+x25519PublicKeySize {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: invalid server X25519MLKEM768 key share")
}
ecdhePeerData = hs.serverHello.serverShare.data[mlkem.CiphertextSize768:]
ke, err := keyExchangeForCurveID(hs.serverHello.serverShare.group)
if err != nil {
c.sendAlert(alertInternalError)
return err
}
peerKey, err := hs.keyShareKeys.ecdhe.Curve().NewPublicKey(ecdhePeerData)
sharedKey, err := ke.clientSharedSecret(hs.keyShareKeys, hs.serverHello.serverShare.data)
if err != nil {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: invalid server key share")
}
sharedKey, err := hs.keyShareKeys.ecdhe.ECDH(peerKey)
if err != nil {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: invalid server key share")
}
if hs.serverHello.serverShare.group == X25519MLKEM768 {
if hs.keyShareKeys.mlkem == nil {
return c.sendAlert(alertInternalError)
}
ciphertext := hs.serverHello.serverShare.data[:mlkem.CiphertextSize768]
mlkemShared, err := hs.keyShareKeys.mlkem.Decapsulate(ciphertext)
if err != nil {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: invalid X25519MLKEM768 server key share")
}
sharedKey = append(mlkemShared, sharedKey...)
}
c.curveID = hs.serverHello.serverShare.group
earlySecret := hs.earlySecret
@@ -515,16 +491,17 @@ func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
handshakeSecret := earlySecret.HandshakeSecret(sharedKey)
clientSecret := handshakeSecret.ClientHandshakeTrafficSecret(hs.transcript)
c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret)
c.setWriteTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret)
serverSecret := handshakeSecret.ServerHandshakeTrafficSecret(hs.transcript)
c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret)
if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret, false); err != nil {
return err
}
if c.quic != nil {
if c.hand.Len() != 0 {
c.sendAlert(alertUnexpectedMessage)
}
c.quicSetWriteSecret(QUICEncryptionLevelHandshake, hs.suite.id, clientSecret)
c.quicSetReadSecret(QUICEncryptionLevelHandshake, hs.suite.id, serverSecret)
if err := c.quicSetReadSecret(QUICEncryptionLevelHandshake, hs.suite.id, serverSecret); err != nil {
return err
}
}
err = c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret)
@@ -677,7 +654,8 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
// See RFC 8446, Section 4.4.3.
// We don't use hs.hello.supportedSignatureAlgorithms because it might
// include PKCS#1 v1.5 and SHA-1 if the ClientHello also supported TLS 1.2.
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms(c.vers)) {
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms(c.vers, c.vers)) ||
!isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, signatureSchemesForPublicKey(c.vers, c.peerCertificates[0].PublicKey)) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: certificate used with invalid signature algorithm")
}
@@ -688,12 +666,13 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
return c.sendAlert(alertInternalError)
}
signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
signed := signedMessage(serverSignatureContext, hs.transcript)
if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
sigHash, signed, certVerify.signature); err != nil {
c.sendAlert(alertDecryptError)
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
}
c.peerSigAlg = certVerify.signatureAlgorithm
if err := transcriptMsg(certVerify, hs.transcript); err != nil {
return err
@@ -733,7 +712,9 @@ func (hs *clientHandshakeStateTLS13) readServerFinished() error {
hs.trafficSecret = hs.masterSecret.ClientApplicationTrafficSecret(hs.transcript)
serverSecret := hs.masterSecret.ServerApplicationTrafficSecret(hs.transcript)
c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret)
if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret, false); err != nil {
return err
}
err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret)
if err != nil {
@@ -775,6 +756,10 @@ func (hs *clientHandshakeStateTLS13) sendClientCertificate() error {
return err
}
if cert != nil {
hs.c.localCertificate = cert.Certificate
}
certMsg := new(certificateMsgTLS13)
certMsg.certificate = *cert
@@ -806,12 +791,12 @@ func (hs *clientHandshakeStateTLS13) sendClientCertificate() error {
return c.sendAlert(alertInternalError)
}
signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
signed := signedMessage(clientSignatureContext, hs.transcript)
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
sig, err := crypto.SignMessage(cert.PrivateKey.(crypto.Signer), c.config.rand(), signed, signOpts)
if err != nil {
c.sendAlert(alertInternalError)
return errors.New("tls: failed to sign handshake: " + err.Error())
@@ -836,16 +821,13 @@ func (hs *clientHandshakeStateTLS13) sendClientFinished() error {
return err
}
c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret)
c.setWriteTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret)
if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil {
c.resumptionSecret = hs.masterSecret.ResumptionMasterSecret(hs.transcript)
}
if c.quic != nil {
if c.hand.Len() != 0 {
c.sendAlert(alertUnexpectedMessage)
}
c.quicSetWriteSecret(QUICEncryptionLevelApplication, hs.suite.id, hs.trafficSecret)
}
+56 -1
View File
@@ -5,6 +5,7 @@
package reality
import (
"bytes"
"errors"
"fmt"
"slices"
@@ -317,7 +318,8 @@ func (m *clientHelloMsg) marshalMsg(echInner bool) ([]byte, error) {
})
})
}
if len(m.pskIdentities) > 0 { // pre_shared_key must be the last extension
// pre_shared_key must be the last extension
if len(m.pskIdentities) > 0 && (echInner || len(m.encryptedClientHello) == 0 || bytes.Equal(m.encryptedClientHello, []byte{byte(innerECHExt)})) {
// RFC 8446, Section 4.2.11
exts.AddUint16(extensionPreSharedKey)
exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) {
@@ -1005,6 +1007,7 @@ type encryptedExtensionsMsg struct {
quicTransportParameters []byte
earlyData bool
echRetryConfigs []byte
serverNameAck bool
}
func (m *encryptedExtensionsMsg) marshal() ([]byte, error) {
@@ -1040,6 +1043,10 @@ func (m *encryptedExtensionsMsg) marshal() ([]byte, error) {
b.AddBytes(m.echRetryConfigs)
})
}
if m.serverNameAck {
b.AddUint16(extensionServerName)
b.AddUint16(0) // empty extension_data
}
})
})
@@ -1095,6 +1102,21 @@ func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool {
if !extData.CopyBytes(m.echRetryConfigs) {
return false
}
case extensionServerName:
if len(extData) != 0 {
return false
}
m.serverNameAck = true
case extensionStatusRequest, extensionSupportedPoints,
extensionSignatureAlgorithms, extensionSCT,
extensionExtendedMasterSecret, extensionSessionTicket,
extensionPreSharedKey, extensionSupportedVersions,
extensionCookie, extensionPSKModes,
extensionCertificateAuthorities, extensionSignatureAlgorithmsCert,
extensionKeyShare, extensionRenegotiationInfo,
extensionECHOuterExtensions:
// Not allowed in EncryptedExtensions.
return false
default:
// Ignore unknown extensions.
continue
@@ -1219,6 +1241,18 @@ func (m *newSessionTicketMsgTLS13) unmarshal(data []byte) bool {
if !extData.ReadUint32(&m.maxEarlyData) {
return false
}
case extensionServerName, extensionStatusRequest,
extensionSupportedCurves, extensionSupportedPoints,
extensionSignatureAlgorithms, extensionALPN, extensionSCT,
extensionExtendedMasterSecret, extensionSessionTicket,
extensionPreSharedKey, extensionSupportedVersions,
extensionCookie, extensionPSKModes,
extensionCertificateAuthorities, extensionSignatureAlgorithmsCert,
extensionKeyShare, extensionQUICTransportParameters,
extensionRenegotiationInfo, extensionECHOuterExtensions,
extensionEncryptedClientHello:
// Not allowed in TLS 1.3 NewSessionTicket.
return false
default:
// Ignore unknown extensions.
continue
@@ -1363,6 +1397,15 @@ func (m *certificateRequestMsgTLS13) unmarshal(data []byte) bool {
}
m.certificateAuthorities = append(m.certificateAuthorities, ca)
}
case extensionSupportedCurves, extensionSupportedPoints,
extensionALPN, extensionExtendedMasterSecret,
extensionSessionTicket, extensionPreSharedKey,
extensionEarlyData, extensionSupportedVersions,
extensionCookie, extensionPSKModes, extensionKeyShare,
extensionQUICTransportParameters, extensionRenegotiationInfo,
extensionECHOuterExtensions, extensionEncryptedClientHello:
// Not allowed in TLS 1.3 CertificateRequest.
return false
default:
// Ignore unknown extensions.
continue
@@ -1573,6 +1616,18 @@ func unmarshalCertificate(s *cryptobyte.String, certificate *Certificate) bool {
certificate.SignedCertificateTimestamps = append(
certificate.SignedCertificateTimestamps, sct)
}
case extensionServerName, extensionSupportedCurves,
extensionSupportedPoints, extensionSignatureAlgorithms,
extensionALPN, extensionExtendedMasterSecret,
extensionSessionTicket, extensionPreSharedKey,
extensionEarlyData, extensionSupportedVersions,
extensionCookie, extensionPSKModes,
extensionCertificateAuthorities, extensionSignatureAlgorithmsCert,
extensionKeyShare, extensionQUICTransportParameters,
extensionRenegotiationInfo, extensionECHOuterExtensions,
extensionEncryptedClientHello:
// Not allowed in Certificate.
return false
default:
// Ignore unknown extensions.
continue
+63 -18
View File
@@ -9,10 +9,10 @@ import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/mldsa"
"crypto/rsa"
"crypto/subtle"
"crypto/x509"
"encoding/binary"
"errors"
"fmt"
"hash"
@@ -189,7 +189,7 @@ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, *echServer
} else if len(clientVersions) == 0 {
clientVersions = supportedVersionsFromMax(clientHello.vers)
}
c.vers, ok = c.config.mutualVersion(roleServer, clientVersions)
c.vers, ok = c.config.mutualVersion(roleServer, c.quic != nil, clientVersions)
if !ok {
c.sendAlert(alertProtocolVersion)
return nil, nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
@@ -236,7 +236,7 @@ func (hs *serverHandshakeState) processClientHello() error {
hs.hello.random = make([]byte, 32)
serverRandom := hs.hello.random
// Downgrade protection canaries. See RFC 8446, Section 4.1.3.
maxVers := c.config.maxSupportedVersion(roleServer)
maxVers := c.config.maxSupportedVersion(roleServer, c.quic != nil)
if maxVers >= VersionTLS12 && c.vers < maxVers || testingOnlyForceDowngradeCanary {
if c.vers == VersionTLS12 {
copy(serverRandom[24:], downgradeCanaryTLS12)
@@ -280,6 +280,7 @@ func (hs *serverHandshakeState) processClientHello() error {
}
return err
}
if hs.clientHello.scts {
hs.hello.scts = hs.cert.SignedCertificateTimestamps
}
@@ -307,6 +308,11 @@ func (hs *serverHandshakeState) processClientHello() error {
hs.ecSignOk = true
case *rsa.PublicKey:
hs.rsaSignOk = true
case *mldsa.PublicKey:
// ML-DSA can only be used with TLS 1.3.
c.sendAlert(alertInternalError)
return fmt.Errorf("tls: ML-DSA certificates require TLS 1.3, but client negotiated %s",
VersionName(c.vers))
default:
c.sendAlert(alertInternalError)
return fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public())
@@ -354,7 +360,7 @@ func negotiateALPN(serverProtos, clientProtos []string, quic bool) (string, erro
if http11fallback {
return "", nil
}
return "", fmt.Errorf("tls: client requested unsupported application protocols (%s)", clientProtos)
return "", fmt.Errorf("tls: client requested unsupported application protocols (%q)", clientProtos)
}
// supportsECDHE returns whether ECDHE key exchanges can be used with this
@@ -406,7 +412,7 @@ func (hs *serverHandshakeState) pickCipherSuite() error {
for _, id := range hs.clientHello.cipherSuites {
if id == TLS_FALLBACK_SCSV {
// The client is doing a fallback connection. See RFC 7507.
if hs.clientHello.vers < c.config.maxSupportedVersion(roleServer) {
if hs.clientHello.vers < c.config.maxSupportedVersion(roleServer, c.quic != nil) {
c.sendAlert(alertInappropriateFallback)
return errors.New("tls: client using inappropriate protocol fallback")
}
@@ -511,8 +517,13 @@ func (hs *serverHandshakeState) checkForResumption() error {
if sessionHasClientCerts && c.config.time().After(sessionState.peerCertificates[0].NotAfter) {
return nil
}
opts := x509.VerifyOptions{
CurrentTime: c.config.time(),
Roots: c.config.ClientCAs,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
if sessionHasClientCerts && c.config.ClientAuth >= VerifyClientCertIfGiven &&
len(sessionState.verifiedChains) == 0 {
!anyValidVerifiedChain(sessionState.verifiedChains, opts) {
return nil
}
@@ -582,6 +593,10 @@ func (hs *serverHandshakeState) doFullHandshake() error {
hs.hello.ocspStapling = true
}
if hs.clientHello.serverName != "" {
hs.hello.serverNameAck = true
}
hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled
hs.hello.cipherSuite = hs.suite.id
@@ -600,6 +615,10 @@ func (hs *serverHandshakeState) doFullHandshake() error {
certMsg := new(certificateMsg)
certMsg.certificates = hs.cert.Certificate
// Set localCertificate here, rather than at certificate selection time, so
// that it is only populated when a certificate is actually presented to the
// peer, and not on resumed connections.
c.localCertificate = hs.cert.Certificate
if _, err := hs.c.writeHandshakeRecord(certMsg, &hs.finishedHash); err != nil {
return err
}
@@ -619,8 +638,9 @@ func (hs *serverHandshakeState) doFullHandshake() error {
return err
}
if skx != nil {
if len(skx.key) >= 3 && skx.key[0] == 3 /* named curve */ {
c.curveID = CurveID(binary.BigEndian.Uint16(skx.key[1:]))
if keyAgreement, ok := keyAgreement.(*ecdheKeyAgreement); ok {
c.curveID = keyAgreement.curveID
c.peerSigAlg = keyAgreement.signatureAlgorithm
}
if _, err := hs.c.writeHandshakeRecord(skx, &hs.finishedHash); err != nil {
return err
@@ -637,7 +657,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
}
if c.vers >= VersionTLS12 {
certReq.hasSignatureAlgorithm = true
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms(c.vers)
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms(c.vers, c.vers)
}
// An empty list of certificateAuthorities signals to
@@ -759,19 +779,28 @@ func (hs *serverHandshakeState) doFullHandshake() error {
if err != nil {
return c.sendAlert(alertInternalError)
}
if hs.finishedHash.buffer == nil {
c.sendAlert(alertInternalError)
return errors.New("tls: internal error: did not keep handshake transcript for TLS 1.2")
}
if err := verifyHandshakeSignature(sigType, pub, sigHash, hs.finishedHash.buffer, certVerify.signature); err != nil {
c.sendAlert(alertDecryptError)
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
}
} else {
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(pub)
if err != nil {
c.sendAlert(alertIllegalParameter)
return err
}
signed := hs.finishedHash.hashForClientCertificate(sigType)
if err := verifyLegacyHandshakeSignature(sigType, pub, sigHash, signed, certVerify.signature); err != nil {
c.sendAlert(alertDecryptError)
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
}
}
signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash)
if err := verifyHandshakeSignature(sigType, pub, sigHash, signed, certVerify.signature); err != nil {
c.sendAlert(alertDecryptError)
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
}
c.peerSigAlg = certVerify.signatureAlgorithm
if err := transcriptMsg(certVerify, &hs.finishedHash); err != nil {
return err
@@ -943,10 +972,9 @@ func (c *Conn) processCertsFromClient(certificate Certificate) error {
chains, err := certs[0].Verify(opts)
if err != nil {
var errCertificateInvalid x509.CertificateInvalidError
if errors.As(err, &x509.UnknownAuthorityError{}) {
if _, ok := errors.AsType[x509.UnknownAuthorityError](err); ok {
c.sendAlert(alertUnknownCA)
} else if errors.As(err, &errCertificateInvalid) && errCertificateInvalid.Reason == x509.Expired {
} else if errCertificateInvalid, ok := errors.AsType[x509.CertificateInvalidError](err); ok && errCertificateInvalid.Reason == x509.Expired {
c.sendAlert(alertCertificateExpired)
} else {
c.sendAlert(alertBadCertificate)
@@ -966,8 +994,19 @@ func (c *Conn) processCertsFromClient(certificate Certificate) error {
c.scts = certificate.SignedCertificateTimestamps
if len(certs) > 0 {
if fips140tls.Required() && !isCertificateAllowedFIPS(certs[0]) {
c.sendAlert(alertBadCertificate)
err := errors.New("client's certificate is not allowed in FIPS 140-3 mode")
return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}
}
switch certs[0].PublicKey.(type) {
case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey:
case *mldsa.PublicKey:
if c.vers < VersionTLS13 {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: client certificate uses ML-DSA, which requires TLS 1.3")
}
default:
c.sendAlert(alertUnsupportedCertificate)
return fmt.Errorf("tls: client certificate contains an unsupported public key of type %T", certs[0].PublicKey)
@@ -990,6 +1029,10 @@ func clientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg)
supportedVersions = supportedVersionsFromMax(clientHello.vers)
}
conn := c.conn
if c.quic != nil {
conn = c.quic.clientHelloInfoConn
}
return &ClientHelloInfo{
CipherSuites: clientHello.cipherSuites,
ServerName: clientHello.serverName,
@@ -999,8 +1042,10 @@ func clientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg)
SupportedProtos: clientHello.alpnProtocols,
SupportedVersions: supportedVersions,
Extensions: clientHello.extensions,
Conn: c.conn,
Conn: conn,
HelloRetryRequest: c.didHRR,
config: c.config,
isQUIC: c.quic != nil,
ctx: ctx,
}
}
+62 -67
View File
@@ -11,6 +11,7 @@ import (
"crypto/ed25519"
"crypto/hkdf"
"crypto/hmac"
"crypto/hpke"
"crypto/mlkem"
"crypto/rand"
"crypto/rsa"
@@ -29,7 +30,6 @@ import (
"github.com/cloudflare/circl/sign/mldsa/mldsa65"
"github.com/xtls/reality/fips140tls"
"github.com/xtls/reality/hpke"
"github.com/xtls/reality/tls13"
)
@@ -43,7 +43,7 @@ type echServerContext struct {
configID uint8
ciphersuite EchCipher
transcript hash.Hash
// inner indicates that the initial client_hello we recieved contained an
// inner indicates that the initial client_hello we received contained an
// encrypted_client_hello extension that indicated it was an "inner" hello.
// We don't do any additional processing of the hello in this case, so all
// fields above are unset.
@@ -71,6 +71,7 @@ type serverHandshakeStateTLS13 struct {
echContext *echServerContext
}
//////////////////////////////////// [REALITY] SECTION: do handshake
var (
ed25519Priv ed25519.PrivateKey
signedCert []byte
@@ -197,6 +198,7 @@ func (hs *serverHandshakeStateTLS13) handshake() error {
return nil
}
//////////////////////////////////// [REALITY] SECTION END
func (hs *serverHandshakeStateTLS13) processClientHello() error {
c := hs.c
@@ -226,7 +228,7 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error {
if id == TLS_FALLBACK_SCSV {
// Use c.vers instead of max(supported_versions) because an attacker
// could defeat this by adding an arbitrary high version otherwise.
if c.vers < c.config.maxSupportedVersion(roleServer) {
if c.vers < c.config.maxSupportedVersion(roleServer, c.quic != nil) {
c.sendAlert(alertInappropriateFallback)
return errors.New("tls: client using inappropriate protocol fallback")
}
@@ -340,55 +342,16 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error {
}
c.curveID = selectedGroup
ecdhGroup := selectedGroup
ecdhData := clientKeyShare.data
if selectedGroup == X25519MLKEM768 {
ecdhGroup = X25519
if len(ecdhData) != mlkem.EncapsulationKeySize768+x25519PublicKeySize {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: invalid X25519MLKEM768 client key share")
}
ecdhData = ecdhData[mlkem.EncapsulationKeySize768:]
}
if _, ok := curveForCurveID(ecdhGroup); !ok {
c.sendAlert(alertInternalError)
return errors.New("tls: CurvePreferences includes unsupported curve")
}
key, err := generateECDHEKey(c.config.rand(), ecdhGroup)
ke, err := keyExchangeForCurveID(selectedGroup)
if err != nil {
c.sendAlert(alertInternalError)
return err
return errors.New("tls: internal error: supportsCurve accepted unimplemented curve")
}
hs.hello.serverShare = keyShare{group: selectedGroup, data: key.PublicKey().Bytes()}
peerKey, err := key.Curve().NewPublicKey(ecdhData)
hs.sharedKey, hs.hello.serverShare, err = ke.serverSharedSecret(c.config.rand(), clientKeyShare.data)
if err != nil {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: invalid client key share")
}
hs.sharedKey, err = key.ECDH(peerKey)
if err != nil {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: invalid client key share")
}
if selectedGroup == X25519MLKEM768 {
k, err := mlkem.NewEncapsulationKey768(clientKeyShare.data[:mlkem.EncapsulationKeySize768])
if err != nil {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: invalid X25519MLKEM768 client key share")
}
mlkemSharedSecret, ciphertext := k.Encapsulate()
// draft-kwiatkowski-tls-ecdhe-mlkem-02, Section 3.1.3: "For
// X25519MLKEM768, the shared secret is the concatenation of the ML-KEM
// shared secret and the X25519 shared secret. The shared secret is 64
// bytes (32 bytes for each part)."
hs.sharedKey = append(mlkemSharedSecret, hs.sharedKey...)
// draft-kwiatkowski-tls-ecdhe-mlkem-02, Section 3.1.2: "When the
// X25519MLKEM768 group is negotiated, the server's key exchange value
// is the concatenation of an ML-KEM ciphertext returned from
// encapsulation to the client's encapsulation key, and the server's
// ephemeral X25519 share."
hs.hello.serverShare.data = append(ciphertext, hs.hello.serverShare.data...)
}
selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols, c.quic != nil)
if err != nil {
@@ -503,8 +466,13 @@ func (hs *serverHandshakeStateTLS13) checkForResumption() error {
if sessionHasClientCerts && c.config.time().After(sessionState.peerCertificates[0].NotAfter) {
continue
}
opts := x509.VerifyOptions{
CurrentTime: c.config.time(),
Roots: c.config.ClientCAs,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
if sessionHasClientCerts && c.config.ClientAuth >= VerifyClientCertIfGiven &&
len(sessionState.verifiedChains) == 0 {
!anyValidVerifiedChain(sessionState.verifiedChains, opts) {
continue
}
@@ -544,7 +512,9 @@ func (hs *serverHandshakeStateTLS13) checkForResumption() error {
return err
}
earlyTrafficSecret := hs.earlySecret.ClientEarlyTrafficSecret(transcript)
c.quicSetReadSecret(QUICEncryptionLevelEarly, hs.suite.id, earlyTrafficSecret)
if err := c.quicSetReadSecret(QUICEncryptionLevelEarly, hs.suite.id, earlyTrafficSecret); err != nil {
return err
}
}
c.didResume = true
@@ -562,10 +532,17 @@ func (hs *serverHandshakeStateTLS13) checkForResumption() error {
return nil
}
// cloneHash uses the encoding.BinaryMarshaler and encoding.BinaryUnmarshaler
// cloneHash uses [hash.Cloner] to clone in. If [hash.Cloner]
// is not implemented or not supported, then it falls back to the
// [encoding.BinaryMarshaler] and [encoding.BinaryUnmarshaler]
// interfaces implemented by standard library hashes to clone the state of in
// to a new instance of h. It returns nil if the operation fails.
func cloneHash(in hash.Hash, h crypto.Hash) hash.Hash {
if cloner, ok := in.(hash.Cloner); ok {
if out, err := cloner.Clone(); err == nil {
return out
}
}
// Recreate the interface to avoid importing encoding.
type binaryMarshaler interface {
MarshalBinary() (data []byte, err error)
@@ -612,6 +589,9 @@ func (hs *serverHandshakeStateTLS13) pickCertificate() error {
}
return err
}
if certificate != nil {
hs.c.localCertificate = certificate.Certificate
}
hs.sigAlg, err = selectSignatureScheme(c.vers, certificate, hs.clientHello.supportedSignatureAlgorithms)
if err != nil {
// getCertificate returned a certificate that is unsupported or
@@ -641,6 +621,14 @@ func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) (*keyShare, error) {
c := hs.c
// Make sure the client didn't send extra handshake messages alongside
// their initial client_hello. If they sent two client_hello messages,
// we will consume the second before they respond to the server_hello.
if c.handLen() != 0 {
c.sendAlert(alertUnexpectedMessage)
return nil, errors.New("tls: handshake buffer not empty before HelloRetryRequest")
}
// 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 {
@@ -846,6 +834,7 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
return err
}
//////////////////////////////////// [REALITY] SECTION: do handshake
/*
if _, err := hs.c.writeHandshakeRecord(hs.hello, hs.transcript); err != nil {
return err
@@ -857,6 +846,7 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
return err
}
}
//////////////////////////////////// [REALITY] SECTION END
if err := hs.sendDummyChangeCipherSpec(); err != nil {
return err
@@ -868,17 +858,18 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
}
hs.handshakeSecret = earlySecret.HandshakeSecret(hs.sharedKey)
clientSecret := hs.handshakeSecret.ClientHandshakeTrafficSecret(hs.transcript)
c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret)
serverSecret := hs.handshakeSecret.ServerHandshakeTrafficSecret(hs.transcript)
c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret)
c.setWriteTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret)
clientSecret := hs.handshakeSecret.ClientHandshakeTrafficSecret(hs.transcript)
if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret, false); err != nil {
return err
}
if c.quic != nil {
if c.hand.Len() != 0 {
c.sendAlert(alertUnexpectedMessage)
}
c.quicSetWriteSecret(QUICEncryptionLevelHandshake, hs.suite.id, serverSecret)
c.quicSetReadSecret(QUICEncryptionLevelHandshake, hs.suite.id, clientSecret)
if err := c.quicSetReadSecret(QUICEncryptionLevelHandshake, hs.suite.id, clientSecret); err != nil {
return err
}
}
err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.clientHello.random, clientSecret)
@@ -904,6 +895,10 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
encryptedExtensions.earlyData = hs.earlyData
}
if !hs.c.didResume && hs.clientHello.serverName != "" {
encryptedExtensions.serverNameAck = true
}
// If client sent ECH extension, but we didn't accept it,
// send retry configs, if available.
echKeys := hs.c.config.EncryptedClientHelloKeys
@@ -946,8 +941,8 @@ func (hs *serverHandshakeStateTLS13) sendServerCertificate() error {
certReq := new(certificateRequestMsgTLS13)
certReq.ocspStapling = true
certReq.scts = true
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms(c.vers)
certReq.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithmsCert()
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms(c.vers, c.vers)
certReq.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithmsCert(c.vers, c.vers)
if c.config.ClientCAs != nil {
certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
}
@@ -976,12 +971,12 @@ func (hs *serverHandshakeStateTLS13) sendServerCertificate() error {
return c.sendAlert(alertInternalError)
}
signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
signed := signedMessage(serverSignatureContext, hs.transcript)
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
sig, err := hs.cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
sig, err := crypto.SignMessage(hs.cert.PrivateKey.(crypto.Signer), c.config.rand(), signed, signOpts)
if err != nil {
public := hs.cert.PrivateKey.(crypto.Signer).Public()
if rsaKey, ok := public.(*rsa.PublicKey); ok && sigType == signatureRSAPSS &&
@@ -1018,13 +1013,9 @@ func (hs *serverHandshakeStateTLS13) sendServerFinished() error {
hs.trafficSecret = hs.masterSecret.ClientApplicationTrafficSecret(hs.transcript)
serverSecret := hs.masterSecret.ServerApplicationTrafficSecret(hs.transcript)
c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret)
c.setWriteTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret)
if c.quic != nil {
if c.hand.Len() != 0 {
// TODO: Handle this in setTrafficSecret?
c.sendAlert(alertUnexpectedMessage)
}
c.quicSetWriteSecret(QUICEncryptionLevelApplication, hs.suite.id, serverSecret)
}
@@ -1200,7 +1191,8 @@ func (hs *serverHandshakeStateTLS13) readClientCertificate() error {
// See RFC 8446, Section 4.4.3.
// We don't use certReq.supportedSignatureAlgorithms because it would
// require keeping the certificateRequestMsgTLS13 around in the hs.
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms(c.vers)) {
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms(c.vers, c.vers)) ||
!isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, signatureSchemesForPublicKey(c.vers, c.peerCertificates[0].PublicKey)) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: client certificate used with invalid signature algorithm")
}
@@ -1211,12 +1203,13 @@ func (hs *serverHandshakeStateTLS13) readClientCertificate() error {
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
return c.sendAlert(alertInternalError)
}
signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
signed := signedMessage(clientSignatureContext, hs.transcript)
if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
sigHash, signed, certVerify.signature); err != nil {
c.sendAlert(alertDecryptError)
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
}
c.peerSigAlg = certVerify.signatureAlgorithm
if err := transcriptMsg(certVerify, hs.transcript); err != nil {
return err
@@ -1252,7 +1245,9 @@ func (hs *serverHandshakeStateTLS13) readClientFinished() error {
return errors.New("tls: invalid client finished hash")
}
c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret)
if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret, false); err != nil {
return err
}
return nil
}
-355
View File
@@ -1,355 +0,0 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package hpke
import (
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/ecdh"
"crypto/hkdf"
"crypto/rand"
"encoding/binary"
"errors"
"math/bits"
"golang.org/x/crypto/chacha20poly1305"
)
// testingOnlyGenerateKey is only used during testing, to provide
// a fixed test key to use when checking the RFC 9180 vectors.
var testingOnlyGenerateKey func() (*ecdh.PrivateKey, error)
type hkdfKDF struct {
hash crypto.Hash
}
func (kdf *hkdfKDF) LabeledExtract(sid []byte, salt []byte, label string, inputKey []byte) ([]byte, error) {
labeledIKM := make([]byte, 0, 7+len(sid)+len(label)+len(inputKey))
labeledIKM = append(labeledIKM, []byte("HPKE-v1")...)
labeledIKM = append(labeledIKM, sid...)
labeledIKM = append(labeledIKM, label...)
labeledIKM = append(labeledIKM, inputKey...)
return hkdf.Extract(kdf.hash.New, labeledIKM, salt)
}
func (kdf *hkdfKDF) LabeledExpand(suiteID []byte, randomKey []byte, label string, info []byte, length uint16) ([]byte, error) {
labeledInfo := make([]byte, 0, 2+7+len(suiteID)+len(label)+len(info))
labeledInfo = binary.BigEndian.AppendUint16(labeledInfo, length)
labeledInfo = append(labeledInfo, []byte("HPKE-v1")...)
labeledInfo = append(labeledInfo, suiteID...)
labeledInfo = append(labeledInfo, label...)
labeledInfo = append(labeledInfo, info...)
return hkdf.Expand(kdf.hash.New, randomKey, string(labeledInfo), int(length))
}
// dhKEM implements the KEM specified in RFC 9180, Section 4.1.
type dhKEM struct {
dh ecdh.Curve
kdf hkdfKDF
suiteID []byte
nSecret uint16
}
type KemID uint16
const DHKEM_X25519_HKDF_SHA256 = 0x0020
var SupportedKEMs = map[uint16]struct {
curve ecdh.Curve
hash crypto.Hash
nSecret uint16
}{
// RFC 9180 Section 7.1
DHKEM_X25519_HKDF_SHA256: {ecdh.X25519(), crypto.SHA256, 32},
}
func newDHKem(kemID uint16) (*dhKEM, error) {
suite, ok := SupportedKEMs[kemID]
if !ok {
return nil, errors.New("unsupported suite ID")
}
return &dhKEM{
dh: suite.curve,
kdf: hkdfKDF{suite.hash},
suiteID: binary.BigEndian.AppendUint16([]byte("KEM"), kemID),
nSecret: suite.nSecret,
}, nil
}
func (dh *dhKEM) ExtractAndExpand(dhKey, kemContext []byte) ([]byte, error) {
eaePRK, err := dh.kdf.LabeledExtract(dh.suiteID[:], nil, "eae_prk", dhKey)
if err != nil {
return nil, err
}
return dh.kdf.LabeledExpand(dh.suiteID[:], eaePRK, "shared_secret", kemContext, dh.nSecret)
}
func (dh *dhKEM) Encap(pubRecipient *ecdh.PublicKey) (sharedSecret []byte, encapPub []byte, err error) {
var privEph *ecdh.PrivateKey
if testingOnlyGenerateKey != nil {
privEph, err = testingOnlyGenerateKey()
} else {
privEph, err = dh.dh.GenerateKey(rand.Reader)
}
if err != nil {
return nil, nil, err
}
dhVal, err := privEph.ECDH(pubRecipient)
if err != nil {
return nil, nil, err
}
encPubEph := privEph.PublicKey().Bytes()
encPubRecip := pubRecipient.Bytes()
kemContext := append(encPubEph, encPubRecip...)
sharedSecret, err = dh.ExtractAndExpand(dhVal, kemContext)
if err != nil {
return nil, nil, err
}
return sharedSecret, encPubEph, nil
}
func (dh *dhKEM) Decap(encPubEph []byte, secRecipient *ecdh.PrivateKey) ([]byte, error) {
pubEph, err := dh.dh.NewPublicKey(encPubEph)
if err != nil {
return nil, err
}
dhVal, err := secRecipient.ECDH(pubEph)
if err != nil {
return nil, err
}
kemContext := append(encPubEph, secRecipient.PublicKey().Bytes()...)
return dh.ExtractAndExpand(dhVal, kemContext)
}
type context struct {
aead cipher.AEAD
sharedSecret []byte
suiteID []byte
key []byte
baseNonce []byte
exporterSecret []byte
seqNum uint128
}
type Sender struct {
*context
}
type Recipient struct {
*context
}
var aesGCMNew = func(key []byte) (cipher.AEAD, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
return cipher.NewGCM(block)
}
type AEADID uint16
const (
AEAD_AES_128_GCM = 0x0001
AEAD_AES_256_GCM = 0x0002
AEAD_ChaCha20Poly1305 = 0x0003
)
var SupportedAEADs = map[uint16]struct {
keySize int
nonceSize int
aead func([]byte) (cipher.AEAD, error)
}{
// RFC 9180, Section 7.3
AEAD_AES_128_GCM: {keySize: 16, nonceSize: 12, aead: aesGCMNew},
AEAD_AES_256_GCM: {keySize: 32, nonceSize: 12, aead: aesGCMNew},
AEAD_ChaCha20Poly1305: {keySize: chacha20poly1305.KeySize, nonceSize: chacha20poly1305.NonceSize, aead: chacha20poly1305.New},
}
type KDFID uint16
const KDF_HKDF_SHA256 = 0x0001
var SupportedKDFs = map[uint16]func() *hkdfKDF{
// RFC 9180, Section 7.2
KDF_HKDF_SHA256: func() *hkdfKDF { return &hkdfKDF{crypto.SHA256} },
}
func newContext(sharedSecret []byte, kemID, kdfID, aeadID uint16, info []byte) (*context, error) {
sid := suiteID(kemID, kdfID, aeadID)
kdfInit, ok := SupportedKDFs[kdfID]
if !ok {
return nil, errors.New("unsupported KDF id")
}
kdf := kdfInit()
aeadInfo, ok := SupportedAEADs[aeadID]
if !ok {
return nil, errors.New("unsupported AEAD id")
}
pskIDHash, err := kdf.LabeledExtract(sid, nil, "psk_id_hash", nil)
if err != nil {
return nil, err
}
infoHash, err := kdf.LabeledExtract(sid, nil, "info_hash", info)
if err != nil {
return nil, err
}
ksContext := append([]byte{0}, pskIDHash...)
ksContext = append(ksContext, infoHash...)
secret, err := kdf.LabeledExtract(sid, sharedSecret, "secret", nil)
if err != nil {
return nil, err
}
key, err := kdf.LabeledExpand(sid, secret, "key", ksContext, uint16(aeadInfo.keySize) /* Nk - key size for AEAD */)
if err != nil {
return nil, err
}
baseNonce, err := kdf.LabeledExpand(sid, secret, "base_nonce", ksContext, uint16(aeadInfo.nonceSize) /* Nn - nonce size for AEAD */)
if err != nil {
return nil, err
}
exporterSecret, err := kdf.LabeledExpand(sid, secret, "exp", ksContext, uint16(kdf.hash.Size()) /* Nh - hash output size of the kdf*/)
if err != nil {
return nil, err
}
aead, err := aeadInfo.aead(key)
if err != nil {
return nil, err
}
return &context{
aead: aead,
sharedSecret: sharedSecret,
suiteID: sid,
key: key,
baseNonce: baseNonce,
exporterSecret: exporterSecret,
}, nil
}
func SetupSender(kemID, kdfID, aeadID uint16, pub *ecdh.PublicKey, info []byte) ([]byte, *Sender, error) {
kem, err := newDHKem(kemID)
if err != nil {
return nil, nil, err
}
sharedSecret, encapsulatedKey, err := kem.Encap(pub)
if err != nil {
return nil, nil, err
}
context, err := newContext(sharedSecret, kemID, kdfID, aeadID, info)
if err != nil {
return nil, nil, err
}
return encapsulatedKey, &Sender{context}, nil
}
func SetupRecipient(kemID, kdfID, aeadID uint16, priv *ecdh.PrivateKey, info, encPubEph []byte) (*Recipient, error) {
kem, err := newDHKem(kemID)
if err != nil {
return nil, err
}
sharedSecret, err := kem.Decap(encPubEph, priv)
if err != nil {
return nil, err
}
context, err := newContext(sharedSecret, kemID, kdfID, aeadID, info)
if err != nil {
return nil, err
}
return &Recipient{context}, nil
}
func (ctx *context) nextNonce() []byte {
nonce := ctx.seqNum.bytes()[16-ctx.aead.NonceSize():]
for i := range ctx.baseNonce {
nonce[i] ^= ctx.baseNonce[i]
}
return nonce
}
func (ctx *context) incrementNonce() {
// Message limit is, according to the RFC, 2^95+1, which
// is somewhat confusing, but we do as we're told.
if ctx.seqNum.bitLen() >= (ctx.aead.NonceSize()*8)-1 {
panic("message limit reached")
}
ctx.seqNum = ctx.seqNum.addOne()
}
func (s *Sender) Seal(aad, plaintext []byte) ([]byte, error) {
ciphertext := s.aead.Seal(nil, s.nextNonce(), plaintext, aad)
s.incrementNonce()
return ciphertext, nil
}
func (r *Recipient) Open(aad, ciphertext []byte) ([]byte, error) {
plaintext, err := r.aead.Open(nil, r.nextNonce(), ciphertext, aad)
if err != nil {
return nil, err
}
r.incrementNonce()
return plaintext, nil
}
func suiteID(kemID, kdfID, aeadID uint16) []byte {
suiteID := make([]byte, 0, 4+2+2+2)
suiteID = append(suiteID, []byte("HPKE")...)
suiteID = binary.BigEndian.AppendUint16(suiteID, kemID)
suiteID = binary.BigEndian.AppendUint16(suiteID, kdfID)
suiteID = binary.BigEndian.AppendUint16(suiteID, aeadID)
return suiteID
}
func ParseHPKEPublicKey(kemID uint16, bytes []byte) (*ecdh.PublicKey, error) {
kemInfo, ok := SupportedKEMs[kemID]
if !ok {
return nil, errors.New("unsupported KEM id")
}
return kemInfo.curve.NewPublicKey(bytes)
}
func ParseHPKEPrivateKey(kemID uint16, bytes []byte) (*ecdh.PrivateKey, error) {
kemInfo, ok := SupportedKEMs[kemID]
if !ok {
return nil, errors.New("unsupported KEM id")
}
return kemInfo.curve.NewPrivateKey(bytes)
}
type uint128 struct {
hi, lo uint64
}
func (u uint128) addOne() uint128 {
lo, carry := bits.Add64(u.lo, 1, 0)
return uint128{u.hi + carry, lo}
}
func (u uint128) bitLen() int {
return bits.Len64(u.hi) + bits.Len64(u.lo)
}
func (u uint128) bytes() []byte {
b := make([]byte, 16)
binary.BigEndian.PutUint64(b[0:], u.hi)
binary.BigEndian.PutUint64(b[8:], u.lo)
return b
}
+103 -76
View File
@@ -127,25 +127,8 @@ func md5SHA1Hash(slices [][]byte) []byte {
}
// hashForServerKeyExchange hashes the given slices and returns their digest
// using the given hash function (for TLS 1.2) or using a default based on
// the sigType (for earlier TLS versions). For Ed25519 signatures, which don't
// do pre-hashing, it returns the concatenation of the slices.
func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint16, slices ...[]byte) []byte {
if sigType == signatureEd25519 {
var signed []byte
for _, slice := range slices {
signed = append(signed, slice...)
}
return signed
}
if version >= VersionTLS12 {
h := hashFunc.New()
for _, slice := range slices {
h.Write(slice)
}
digest := h.Sum(nil)
return digest
}
// using a hash based on the sigType. It can only be used for TLS 1.0 and 1.1.
func hashForServerKeyExchange(sigType uint8, slices ...[]byte) []byte {
if sigType == signatureECDSA {
return sha1Hash(slices)
}
@@ -159,31 +142,35 @@ func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint1
type ecdheKeyAgreement struct {
version uint16
isRSA bool
key *ecdh.PrivateKey
// ckx and preMasterSecret are generated in processServerKeyExchange
// and returned in generateClientKeyExchange.
ckx *clientKeyExchangeMsg
preMasterSecret []byte
// curveID, signatureAlgorithm, and key are set by processServerKeyExchange
// and generateServerKeyExchange.
curveID CurveID
signatureAlgorithm SignatureScheme
key *ecdh.PrivateKey
}
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(ka.version, c) {
curveID = c
ka.curveID = c
break
}
}
if curveID == 0 {
if ka.curveID == 0 {
return nil, errors.New("tls: no supported elliptic curves offered")
}
if _, ok := curveForCurveID(curveID); !ok {
return nil, errors.New("tls: CurvePreferences includes unsupported curve")
if _, ok := curveForCurveID(ka.curveID); !ok {
return nil, errors.New("tls: internal error: supportsCurve accepted unimplemented curve")
}
key, err := generateECDHEKey(config.rand(), curveID)
key, err := generateECDHEKey(config.rand(), ka.curveID)
if err != nil {
return nil, err
}
@@ -193,8 +180,8 @@ func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Cer
ecdhePublic := key.PublicKey().Bytes()
serverECDHEParams := make([]byte, 1+2+1+len(ecdhePublic))
serverECDHEParams[0] = 3 // named curve
serverECDHEParams[1] = byte(curveID >> 8)
serverECDHEParams[2] = byte(curveID)
serverECDHEParams[1] = byte(ka.curveID >> 8)
serverECDHEParams[2] = byte(ka.curveID)
serverECDHEParams[3] = byte(len(ecdhePublic))
copy(serverECDHEParams[4:], ecdhePublic)
@@ -203,37 +190,41 @@ func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Cer
return nil, fmt.Errorf("tls: certificate private key of type %T does not implement crypto.Signer", cert.PrivateKey)
}
var signatureAlgorithm SignatureScheme
var sigType uint8
var sigHash crypto.Hash
var sig []byte
if ka.version >= VersionTLS12 {
signatureAlgorithm, err = selectSignatureScheme(ka.version, cert, clientHello.supportedSignatureAlgorithms)
ka.signatureAlgorithm, err = selectSignatureScheme(ka.version, cert, clientHello.supportedSignatureAlgorithms)
if err != nil {
return nil, err
}
sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
sigType, sigHash, err := typeAndHashFromSignatureScheme(ka.signatureAlgorithm)
if err != nil {
return nil, err
}
signed := slices.Concat(clientHello.random, hello.random, serverECDHEParams)
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
return nil, errors.New("tls: certificate cannot be used with the selected cipher suite")
}
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
sig, err = crypto.SignMessage(priv, config.rand(), signed, signOpts)
if err != nil {
return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
}
} else {
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(priv.Public())
sigType, sigHash, err := legacyTypeAndHashFromPublicKey(priv.Public())
if err != nil {
return nil, err
}
}
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
return nil, errors.New("tls: certificate cannot be used with the selected cipher suite")
}
signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, hello.random, serverECDHEParams)
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
sig, err := priv.Sign(config.rand(), signed, signOpts)
if err != nil {
return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
signed := hashForServerKeyExchange(sigType, clientHello.random, hello.random, serverECDHEParams)
if (sigType == signaturePKCS1v15) != ka.isRSA {
return nil, errors.New("tls: certificate cannot be used with the selected cipher suite")
}
sig, err = priv.Sign(config.rand(), signed, sigHash)
if err != nil {
return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
}
}
skx := new(serverKeyExchangeMsg)
@@ -245,8 +236,8 @@ func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Cer
copy(skx.key, serverECDHEParams)
k := skx.key[len(serverECDHEParams):]
if ka.version >= VersionTLS12 {
k[0] = byte(signatureAlgorithm >> 8)
k[1] = byte(signatureAlgorithm)
k[0] = byte(ka.signatureAlgorithm >> 8)
k[1] = byte(ka.signatureAlgorithm)
k = k[2:]
}
k[0] = byte(len(sig) >> 8)
@@ -280,7 +271,7 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
if skx.key[0] != 3 { // named curve
return errors.New("tls: server selected unsupported curve")
}
curveID := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
ka.curveID = CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
publicLen := int(skx.key[3])
if publicLen+4 > len(skx.key) {
@@ -293,16 +284,32 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
if len(sig) < 2 {
return errServerKeyExchange
}
if ka.version >= VersionTLS12 {
ka.signatureAlgorithm = SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1])
sig = sig[2:]
if len(sig) < 2 {
return errServerKeyExchange
}
switch ka.signatureAlgorithm {
case MLDSA44, MLDSA65, MLDSA87:
return errors.New("tls: server selected ML-DSA with TLS version < 1.3")
}
}
sigLen := int(sig[0])<<8 | int(sig[1])
if sigLen+2 != len(sig) {
return errServerKeyExchange
}
sig = sig[2:]
if !slices.Contains(clientHello.supportedCurves, curveID) {
if !slices.Contains(clientHello.supportedCurves, ka.curveID) {
return errors.New("tls: server selected unoffered curve")
}
if _, ok := curveForCurveID(curveID); !ok {
if _, ok := curveForCurveID(ka.curveID); !ok {
return errors.New("tls: server selected unsupported curve")
}
key, err := generateECDHEKey(config.rand(), curveID)
key, err := generateECDHEKey(config.rand(), ka.curveID)
if err != nil {
return err
}
@@ -326,38 +333,32 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
var sigType uint8
var sigHash crypto.Hash
if ka.version >= VersionTLS12 {
signatureAlgorithm := SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1])
sig = sig[2:]
if len(sig) < 2 {
return errServerKeyExchange
}
if !isSupportedSignatureAlgorithm(signatureAlgorithm, clientHello.supportedSignatureAlgorithms) {
if !isSupportedSignatureAlgorithm(ka.signatureAlgorithm, clientHello.supportedSignatureAlgorithms) {
return errors.New("tls: certificate used with invalid signature algorithm")
}
sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
sigType, sigHash, err = typeAndHashFromSignatureScheme(ka.signatureAlgorithm)
if err != nil {
return err
}
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
return errServerKeyExchange
}
signed := slices.Concat(clientHello.random, serverHello.random, serverECDHEParams)
if err := verifyHandshakeSignature(sigType, cert.PublicKey, sigHash, signed, sig); err != nil {
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
}
} else {
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(cert.PublicKey)
if err != nil {
return err
}
}
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
return errServerKeyExchange
}
sigLen := int(sig[0])<<8 | int(sig[1])
if sigLen+2 != len(sig) {
return errServerKeyExchange
}
sig = sig[2:]
signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, serverHello.random, serverECDHEParams)
if err := verifyHandshakeSignature(sigType, cert.PublicKey, sigHash, signed, sig); err != nil {
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
if (sigType == signaturePKCS1v15) != ka.isRSA {
return errServerKeyExchange
}
signed := hashForServerKeyExchange(sigType, clientHello.random, serverHello.random, serverECDHEParams)
if err := verifyLegacyHandshakeSignature(sigType, cert.PublicKey, sigHash, signed, sig); err != nil {
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
}
}
return nil
}
@@ -369,3 +370,29 @@ func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHel
return ka.preMasterSecret, ka.ckx, nil
}
// generateECDHEKey returns a PrivateKey that implements Diffie-Hellman
// according to RFC 8446, Section 4.2.8.2.
func generateECDHEKey(rand io.Reader, curveID CurveID) (*ecdh.PrivateKey, error) {
curve, ok := curveForCurveID(curveID)
if !ok {
return nil, errors.New("tls: internal error: unsupported curve")
}
return curve.GenerateKey(rand)
}
func curveForCurveID(id CurveID) (ecdh.Curve, bool) {
switch id {
case X25519:
return ecdh.X25519(), true
case CurveP256:
return ecdh.P256(), true
case CurveP384:
return ecdh.P384(), true
case CurveP521:
return ecdh.P521(), true
default:
return nil, false
}
}
+235 -21
View File
@@ -5,7 +5,9 @@
package reality
import (
"crypto"
"crypto/ecdh"
"crypto/fips140"
"crypto/hmac"
"crypto/mlkem"
"errors"
@@ -51,35 +53,247 @@ func (c *cipherSuiteTLS13) exportKeyingMaterial(s *tls13.MasterSecret, transcrip
}
type keySharePrivateKeys struct {
curveID CurveID
ecdhe *ecdh.PrivateKey
mlkem *mlkem.DecapsulationKey768
ecdhe *ecdh.PrivateKey
mlkem crypto.Decapsulator
}
const x25519PublicKeySize = 32
// A keyExchange implements a TLS 1.3 KEM.
type keyExchange interface {
// keyShares generates one or two key shares.
//
// The first one will match the id, the second (if present) reuses the
// traditional component of the requested hybrid, as allowed by
// draft-ietf-tls-hybrid-design-09, Section 3.2.
keyShares(rand io.Reader) (*keySharePrivateKeys, []keyShare, error)
// generateECDHEKey returns a PrivateKey that implements Diffie-Hellman
// according to RFC 8446, Section 4.2.8.2.
func generateECDHEKey(rand io.Reader, curveID CurveID) (*ecdh.PrivateKey, error) {
curve, ok := curveForCurveID(curveID)
if !ok {
return nil, errors.New("tls: internal error: unsupported curve")
// serverSharedSecret computes the shared secret and the server's key share.
serverSharedSecret(rand io.Reader, clientKeyShare []byte) ([]byte, keyShare, error)
// clientSharedSecret computes the shared secret given the server's key
// share and the keys generated by keyShares.
clientSharedSecret(priv *keySharePrivateKeys, serverKeyShare []byte) ([]byte, error)
}
func keyExchangeForCurveID(id CurveID) (keyExchange, error) {
mlkemGenerateKey768 := func() (crypto.Decapsulator, error) {
return mlkem.GenerateKey768()
}
mlkemGenerateKey1024 := func() (crypto.Decapsulator, error) {
return mlkem.GenerateKey1024()
}
mlkemNewPublicKey768 := func(b []byte) (crypto.Encapsulator, error) {
return mlkem.NewEncapsulationKey768(b)
}
mlkemNewPublicKey1024 := func(b []byte) (crypto.Encapsulator, error) {
return mlkem.NewEncapsulationKey1024(b)
}
return curve.GenerateKey(rand)
}
func curveForCurveID(id CurveID) (ecdh.Curve, bool) {
switch id {
case X25519:
return ecdh.X25519(), true
return &ecdhKeyExchange{id, ecdh.X25519()}, nil
case CurveP256:
return ecdh.P256(), true
return &ecdhKeyExchange{id, ecdh.P256()}, nil
case CurveP384:
return ecdh.P384(), true
return &ecdhKeyExchange{id, ecdh.P384()}, nil
case CurveP521:
return ecdh.P521(), true
return &ecdhKeyExchange{id, ecdh.P521()}, nil
case X25519MLKEM768:
return &hybridKeyExchange{id, ecdhKeyExchange{X25519, ecdh.X25519()},
32, mlkem.EncapsulationKeySize768, mlkem.CiphertextSize768,
mlkemGenerateKey768, mlkemNewPublicKey768}, nil
case SecP256r1MLKEM768:
return &hybridKeyExchange{id, ecdhKeyExchange{CurveP256, ecdh.P256()},
65, mlkem.EncapsulationKeySize768, mlkem.CiphertextSize768,
mlkemGenerateKey768, mlkemNewPublicKey768}, nil
case SecP384r1MLKEM1024:
return &hybridKeyExchange{id, ecdhKeyExchange{CurveP384, ecdh.P384()},
97, mlkem.EncapsulationKeySize1024, mlkem.CiphertextSize1024,
mlkemGenerateKey1024, mlkemNewPublicKey1024}, nil
case MLKEM1024:
return &mlkem1024KeyExchange{}, nil
default:
return nil, false
return nil, errors.New("tls: unsupported key exchange")
}
}
}
type mlkem1024KeyExchange struct{}
func (ke *mlkem1024KeyExchange) keyShares(_ io.Reader) (*keySharePrivateKeys, []keyShare, error) {
priv, err := mlkem.GenerateKey1024()
if err != nil {
return nil, nil, err
}
return &keySharePrivateKeys{mlkem: priv}, []keyShare{{MLKEM1024, priv.EncapsulationKey().Bytes()}}, nil
}
func (ke *mlkem1024KeyExchange) serverSharedSecret(_ io.Reader, clientKeyShare []byte) ([]byte, keyShare, error) {
peerKey, err := mlkem.NewEncapsulationKey1024(clientKeyShare)
if err != nil {
return nil, keyShare{}, err
}
sharedKey, keyShareData := peerKey.Encapsulate()
return sharedKey, keyShare{MLKEM1024, keyShareData}, nil
}
func (ke *mlkem1024KeyExchange) clientSharedSecret(priv *keySharePrivateKeys, serverKeyShare []byte) ([]byte, error) {
sharedKey, err := priv.mlkem.Decapsulate(serverKeyShare)
if err != nil {
return nil, err
}
return sharedKey, nil
}
type ecdhKeyExchange struct {
id CurveID
curve ecdh.Curve
}
func (ke *ecdhKeyExchange) keyShares(rand io.Reader) (*keySharePrivateKeys, []keyShare, error) {
priv, err := ke.curve.GenerateKey(rand)
if err != nil {
return nil, nil, err
}
return &keySharePrivateKeys{ecdhe: priv}, []keyShare{{ke.id, priv.PublicKey().Bytes()}}, nil
}
func (ke *ecdhKeyExchange) serverSharedSecret(rand io.Reader, clientKeyShare []byte) ([]byte, keyShare, error) {
key, err := ke.curve.GenerateKey(rand)
if err != nil {
return nil, keyShare{}, err
}
peerKey, err := ke.curve.NewPublicKey(clientKeyShare)
if err != nil {
return nil, keyShare{}, err
}
sharedKey, err := key.ECDH(peerKey)
if err != nil {
return nil, keyShare{}, err
}
return sharedKey, keyShare{ke.id, key.PublicKey().Bytes()}, nil
}
func (ke *ecdhKeyExchange) clientSharedSecret(priv *keySharePrivateKeys, serverKeyShare []byte) ([]byte, error) {
peerKey, err := ke.curve.NewPublicKey(serverKeyShare)
if err != nil {
return nil, err
}
sharedKey, err := priv.ecdhe.ECDH(peerKey)
if err != nil {
return nil, err
}
return sharedKey, nil
}
type hybridKeyExchange struct {
id CurveID
ecdh ecdhKeyExchange
ecdhElementSize int
mlkemPublicKeySize int
mlkemCiphertextSize int
mlkemGenerateKey func() (crypto.Decapsulator, error)
mlkemNewPublicKey func([]byte) (crypto.Encapsulator, error)
}
func (ke *hybridKeyExchange) keyShares(rand io.Reader) (*keySharePrivateKeys, []keyShare, error) {
var (
priv *keySharePrivateKeys
ecdhShares []keyShare
err error
)
fips140.WithoutEnforcement(func() { // Hybrid of ML-KEM, which is Approved.
priv, ecdhShares, err = ke.ecdh.keyShares(rand)
})
if err != nil {
return nil, nil, err
}
priv.mlkem, err = ke.mlkemGenerateKey()
if err != nil {
return nil, nil, err
}
var shareData []byte
// For X25519MLKEM768, the ML-KEM-768 encapsulation key comes first.
// For SecP256r1MLKEM768 and SecP384r1MLKEM1024, the ECDH share comes first.
// See draft-ietf-tls-ecdhe-mlkem-02, Section 4.1.
if ke.id == X25519MLKEM768 {
shareData = append(priv.mlkem.Encapsulator().Bytes(), ecdhShares[0].data...)
} else {
shareData = append(ecdhShares[0].data, priv.mlkem.Encapsulator().Bytes()...)
}
return priv, []keyShare{{ke.id, shareData}, ecdhShares[0]}, nil
}
func (ke *hybridKeyExchange) serverSharedSecret(rand io.Reader, clientKeyShare []byte) ([]byte, keyShare, error) {
if len(clientKeyShare) != ke.ecdhElementSize+ke.mlkemPublicKeySize {
return nil, keyShare{}, errors.New("tls: invalid client key share length for hybrid key exchange")
}
var ecdhShareData, mlkemShareData []byte
if ke.id == X25519MLKEM768 {
mlkemShareData = clientKeyShare[:ke.mlkemPublicKeySize]
ecdhShareData = clientKeyShare[ke.mlkemPublicKeySize:]
} else {
ecdhShareData = clientKeyShare[:ke.ecdhElementSize]
mlkemShareData = clientKeyShare[ke.ecdhElementSize:]
}
var (
ecdhSharedSecret []byte
ks keyShare
err error
)
fips140.WithoutEnforcement(func() { // Hybrid of ML-KEM, which is Approved.
ecdhSharedSecret, ks, err = ke.ecdh.serverSharedSecret(rand, ecdhShareData)
})
if err != nil {
return nil, keyShare{}, err
}
mlkemPeerKey, err := ke.mlkemNewPublicKey(mlkemShareData)
if err != nil {
return nil, keyShare{}, err
}
mlkemSharedSecret, mlkemKeyShare := mlkemPeerKey.Encapsulate()
var sharedKey []byte
if ke.id == X25519MLKEM768 {
sharedKey = append(mlkemSharedSecret, ecdhSharedSecret...)
ks.data = append(mlkemKeyShare, ks.data...)
} else {
sharedKey = append(ecdhSharedSecret, mlkemSharedSecret...)
ks.data = append(ks.data, mlkemKeyShare...)
}
ks.group = ke.id
return sharedKey, ks, nil
}
func (ke *hybridKeyExchange) clientSharedSecret(priv *keySharePrivateKeys, serverKeyShare []byte) ([]byte, error) {
if len(serverKeyShare) != ke.ecdhElementSize+ke.mlkemCiphertextSize {
return nil, errors.New("tls: invalid server key share length for hybrid key exchange")
}
var ecdhShareData, mlkemShareData []byte
if ke.id == X25519MLKEM768 {
mlkemShareData = serverKeyShare[:ke.mlkemCiphertextSize]
ecdhShareData = serverKeyShare[ke.mlkemCiphertextSize:]
} else {
ecdhShareData = serverKeyShare[:ke.ecdhElementSize]
mlkemShareData = serverKeyShare[ke.ecdhElementSize:]
}
var (
ecdhSharedSecret []byte
err error
)
fips140.WithoutEnforcement(func() { // Hybrid of ML-KEM, which is Approved.
ecdhSharedSecret, err = ke.ecdh.clientSharedSecret(priv, ecdhShareData)
})
if err != nil {
return nil, err
}
mlkemSharedSecret, err := priv.mlkem.Decapsulate(mlkemShareData)
if err != nil {
return nil, err
}
var sharedKey []byte
if ke.id == X25519MLKEM768 {
sharedKey = append(mlkemSharedSecret, ecdhSharedSecret...)
} else {
sharedKey = append(ecdhSharedSecret, mlkemSharedSecret...)
}
return sharedKey, nil
}
+4 -17
View File
@@ -222,22 +222,9 @@ func (h finishedHash) serverSum(masterSecret []byte) []byte {
return h.prf(masterSecret, serverFinishedLabel, h.Sum(), finishedVerifyLength)
}
// hashForClientCertificate returns the handshake messages so far, pre-hashed if
// necessary, suitable for signing by a TLS client certificate.
func (h finishedHash) hashForClientCertificate(sigType uint8, hashAlg crypto.Hash) []byte {
if (h.version >= VersionTLS12 || sigType == signatureEd25519) && h.buffer == nil {
panic("tls: handshake hash for a client certificate requested after discarding the handshake buffer")
}
if sigType == signatureEd25519 {
return h.buffer
}
if h.version >= VersionTLS12 {
hash := hashAlg.New()
hash.Write(h.buffer)
return hash.Sum(nil)
}
// hashForClientCertificate returns the handshake messages so far, pre-hashed,
// suitable for signing by a TLS 1.0 and 1.1 client certificate.
func (h finishedHash) hashForClientCertificate(sigType uint8) []byte {
if sigType == signatureECDSA {
return h.server.Sum(nil)
@@ -263,7 +250,7 @@ func noEKMBecauseRenegotiation(label string, context []byte, length int) ([]byte
// Master Secret is not negotiated and thus we wish to fail all key-material
// export requests.
func noEKMBecauseNoEMS(label string, context []byte, length int) ([]byte, error) {
return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when neither TLS 1.3 nor Extended Master Secret are negotiated; override with GODEBUG=tlsunsafeekm=1")
return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when neither TLS 1.3 nor Extended Master Secret are negotiated")
}
// ekmFromMasterSecret generates exported keying material as defined in RFC 5705.
+57 -28
View File
@@ -8,6 +8,7 @@ import (
"context"
"errors"
"fmt"
"net"
)
// QUICEncryptionLevel represents a QUIC encryption level used to transmit
@@ -56,6 +57,9 @@ type QUICConfig struct {
// stored in the client session cache.
// The application should use [QUICConn.StoreSession] to store sessions.
EnableSessionEvents bool
// ClientHelloInfoConn is the net.Conn to use for the ClientHelloInfo.Conn field.
ClientHelloInfoConn net.Conn
}
// A QUICEventKind is a type of operation on a QUIC connection.
@@ -117,6 +121,11 @@ const (
// The application may modify the [SessionState] before storing it.
// This event only occurs on client connections.
QUICStoreSession
// QUICErrorEvent indicates that a fatal error has occurred.
// The handshake cannot proceed and the connection must be closed.
// QUICEvent.Err is set.
QUICErrorEvent
)
// A QUICEvent is an event occurring on a QUIC connection.
@@ -138,6 +147,10 @@ type QUICEvent struct {
// Set for QUICResumeSession and QUICStoreSession.
SessionState *SessionState
// Set for QUICErrorEvent.
// The error will wrap AlertError.
Err error
}
type quicState struct {
@@ -153,10 +166,11 @@ type quicState struct {
started bool
signalc chan struct{} // handshake data is available to be read
blockedc chan struct{} // handshake is waiting for data, closed when done
cancelc <-chan struct{} // handshake has been canceled
ctx context.Context // handshake context
cancel context.CancelFunc
waitingForDrain bool
errorReturned bool
// readbuf is shared between HandleData and the handshake goroutine.
// HandshakeCryptoData passes ownership to the handshake goroutine by
@@ -166,23 +180,22 @@ type quicState struct {
transportParams []byte // to send to the peer
enableSessionEvents bool
clientHelloInfoConn net.Conn
}
// QUICClient returns a new TLS client side connection using QUICTransport as the
// underlying transport. The config cannot be nil.
//
// The config's MinVersion must be at least TLS 1.3.
func QUICClient(config *QUICConfig) *QUICConn {
return newQUICConn(Client(nil, config.TLSConfig), config)
}
// QUICServer returns a new TLS server side connection using QUICTransport as the
// underlying transport. The config cannot be nil.
//
// The config's MinVersion must be at least TLS 1.3.
func QUICServer(config *QUICConfig) *QUICConn {
//////////////////////////////////// [REALITY] SECTION: create Reality server
c, _ := Server(context.Background(), nil, config.TLSConfig)
return newQUICConn(c, config)
//////////////////////////////////// [REALITY] SECTION END
}
func newQUICConn(conn *Conn, config *QUICConfig) *QUICConn {
@@ -190,6 +203,7 @@ func newQUICConn(conn *Conn, config *QUICConfig) *QUICConn {
signalc: make(chan struct{}),
blockedc: make(chan struct{}),
enableSessionEvents: config.EnableSessionEvents,
clientHelloInfoConn: config.ClientHelloInfoConn,
}
conn.quic.events = conn.quic.eventArr[:0]
return &QUICConn{
@@ -206,9 +220,6 @@ func (q *QUICConn) Start(ctx context.Context) error {
return quicError(errors.New("tls: Start called more than once"))
}
q.conn.quic.started = true
if q.conn.config.MinVersion < VersionTLS13 {
return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.3"))
}
go q.conn.HandshakeContext(ctx)
if _, ok := <-q.conn.quic.blockedc; !ok {
return q.conn.handshakeErr
@@ -222,7 +233,7 @@ func (q *QUICConn) NextEvent() QUICEvent {
qs := q.conn.quic
if last := qs.nextEvent - 1; last >= 0 && len(qs.events[last].Data) > 0 {
// Write over some of the previous event's data,
// to catch callers erroniously retaining it.
// to catch callers erroneously retaining it.
qs.events[last].Data[0] = 0
}
if qs.nextEvent >= len(qs.events) && qs.waitingForDrain {
@@ -230,6 +241,15 @@ func (q *QUICConn) NextEvent() QUICEvent {
<-qs.signalc
<-qs.blockedc
}
if err := q.conn.handshakeErr; err != nil {
if qs.errorReturned {
return QUICEvent{Kind: QUICNoEvent}
}
qs.errorReturned = true
qs.events = nil
qs.nextEvent = 0
return QUICEvent{Kind: QUICErrorEvent, Err: q.conn.handshakeErr}
}
if qs.nextEvent >= len(qs.events) {
qs.events = qs.events[:0]
qs.nextEvent = 0
@@ -243,10 +263,11 @@ func (q *QUICConn) NextEvent() QUICEvent {
// Close closes the connection and stops any in-progress handshake.
func (q *QUICConn) Close() error {
if q.conn.quic.cancel == nil {
if q.conn.quic.ctx == nil {
return nil // never started
}
q.conn.quic.cancel()
<-q.conn.quic.signalc
for range q.conn.quic.blockedc {
// Wait for the handshake goroutine to return.
}
@@ -270,9 +291,9 @@ func (q *QUICConn) HandleData(level QUICEncryptionLevel, data []byte) error {
// The handshake goroutine has exited.
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
c.hand.Write(c.quic.readbuf)
c.handBuf().Write(c.quic.readbuf)
c.quic.readbuf = nil
for q.conn.hand.Len() >= 4 && q.conn.handshakeErr == nil {
for q.conn.handLen() >= 4 && q.conn.handshakeErr == nil {
b := q.conn.hand.Bytes()
n := int(b[1])<<16 | int(b[2])<<8 | int(b[3])
if n > maxHandshake {
@@ -286,6 +307,7 @@ func (q *QUICConn) HandleData(level QUICEncryptionLevel, data []byte) error {
q.conn.handshakeErr = err
}
}
q.conn.releaseHand()
if q.conn.handshakeErr != nil {
return quicError(q.conn.handshakeErr)
}
@@ -303,6 +325,9 @@ type QUICSessionTicketOptions struct {
// Currently, it can only be called once.
func (q *QUICConn) SendSessionTicket(opts QUICSessionTicketOptions) error {
c := q.conn
if c.config.SessionTicketsDisabled {
return nil
}
if !c.isHandshakeComplete.Load() {
return quicError(errors.New("tls: SendSessionTicket called before handshake completed"))
}
@@ -360,12 +385,11 @@ func quicError(err error) error {
if err == nil {
return nil
}
var ae AlertError
if errors.As(err, &ae) {
if _, ok := errors.AsType[AlertError](err); ok {
return err
}
var a alert
if !errors.As(err, &a) {
a, ok := errors.AsType[alert](err)
if !ok {
a = alertInternalError
}
// Return an error wrapping the original error and an AlertError.
@@ -374,7 +398,7 @@ func quicError(err error) error {
}
func (c *Conn) quicReadHandshakeBytes(n int) error {
for c.hand.Len() < n {
for c.handLen() < n {
if err := c.quicWaitForSignal(); err != nil {
return err
}
@@ -382,13 +406,22 @@ func (c *Conn) quicReadHandshakeBytes(n int) error {
return nil
}
func (c *Conn) quicSetReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
func (c *Conn) quicSetReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) error {
// Ensure that there are no buffered handshake messages before changing the
// read keys, since that can cause messages to be parsed that were encrypted
// using old keys which are no longer appropriate.
// TODO(roland): we should merge this check with the similar one in setReadTrafficSecret.
if c.handLen() != 0 {
c.sendAlert(alertUnexpectedMessage)
return errors.New("tls: handshake buffer not empty before setting read traffic secret")
}
c.quic.events = append(c.quic.events, QUICEvent{
Kind: QUICSetReadSecret,
Level: level,
Suite: suite,
Data: secret,
})
return nil
}
func (c *Conn) quicSetWriteSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
@@ -482,20 +515,16 @@ func (c *Conn) quicWaitForSignal() error {
// Send on blockedc to notify the QUICConn that the handshake is blocked.
// Exported methods of QUICConn wait for the handshake to become blocked
// before returning to the user.
select {
case c.quic.blockedc <- struct{}{}:
case <-c.quic.cancelc:
return c.sendAlertLocked(alertCloseNotify)
}
c.quic.blockedc <- struct{}{}
// The QUICConn reads from signalc to notify us that the handshake may
// be able to proceed. (The QUICConn reads, because we close signalc to
// indicate that the handshake has completed.)
select {
case c.quic.signalc <- struct{}{}:
c.hand.Write(c.quic.readbuf)
c.quic.readbuf = nil
case <-c.quic.cancelc:
c.quic.signalc <- struct{}{}
if c.quic.ctx.Err() != nil {
// The connection has been canceled.
return c.sendAlertLocked(alertCloseNotify)
}
c.handBuf().Write(c.quic.readbuf)
c.quic.readbuf = nil
return nil
}
+2
View File
@@ -15,6 +15,8 @@ import (
utls "github.com/refraction-networking/utls"
)
// Reality specifc file, used for target detection
var GlobalPostHandshakeRecordsLens sync.Map
var GlobalMaxCSSMsgCount sync.Map
+1 -1
View File
@@ -81,7 +81,7 @@ type SessionState struct {
version uint16
isClient bool
cipherSuite uint16
// createdAt is the generation time of the secret on the sever (which for
// createdAt is the generation time of the secret on the server (which for
// TLS 1.01.2 might be earlier than the current session) and the time at
// which the ticket was received on the client.
createdAt uint64 // seconds since UNIX epoch
+34 -28
View File
@@ -27,13 +27,13 @@ package reality
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
import (
"bytes"
"context"
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/mldsa"
"crypto/mlkem"
"crypto/rsa"
"crypto/sha256"
@@ -56,6 +56,7 @@ import (
"golang.org/x/crypto/hkdf"
)
//////////////////////////////////// [REALITY] SECTION: Reality server
type CloseWriteConn interface {
net.Conn
CloseWrite() error
@@ -493,6 +494,7 @@ func Server(ctx context.Context, conn net.Conn, config *Config) (*Conn, error) {
return c
*/
}
//////////////////////////////////// [REALITY] SECTION END
// Client returns a new TLS client side connection
// using conn as the underlying transport.
@@ -512,6 +514,7 @@ func Client(conn net.Conn, config *Config) *Conn {
type listener struct {
net.Listener
config *Config
//////////////////////////////////// [REALITY] SECTION: listener
conns chan net.Conn
err error
}
@@ -563,6 +566,7 @@ func NewListener(inner net.Listener, config *Config) net.Listener {
}
return l
}
//////////////////////////////////// [REALITY] SECTION END
// Listen creates a TLS listener accepting connections on the
// given network address using net.Listen.
@@ -583,6 +587,8 @@ func Listen(network, laddr string, config *Config) (net.Listener, error) {
type timeoutError struct{}
var _ error = timeoutError{}
func (timeoutError) Error() string { return "tls: DialWithDialer timed out" }
func (timeoutError) Timeout() bool { return true }
func (timeoutError) Temporary() bool { return true }
@@ -710,10 +716,6 @@ func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Con
// files. The files must contain PEM encoded data. The certificate file may
// contain intermediate certificates following the leaf certificate to form a
// certificate chain. On successful return, Certificate.Leaf will be populated.
//
// Before Go 1.23 Certificate.Leaf was left nil, and the parsed certificate was
// discarded. This behavior can be re-enabled by setting "x509keypairleaf=0"
// in the GODEBUG environment variable.
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
certPEMBlock, err := os.ReadFile(certFile)
if err != nil {
@@ -728,10 +730,6 @@ func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
// X509KeyPair parses a public/private key pair from a pair of
// PEM encoded data. On successful return, Certificate.Leaf will be populated.
//
// Before Go 1.23 Certificate.Leaf was left nil, and the parsed certificate was
// discarded. This behavior can be re-enabled by setting "x509keypairleaf=0"
// in the GODEBUG environment variable.
func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
fail := func(err error) (Certificate, error) { return Certificate{}, err }
@@ -785,7 +783,6 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
if err != nil {
return fail(err)
}
cert.Leaf = x509Cert
cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
@@ -799,7 +796,7 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
if !ok {
return fail(errors.New("tls: private key type does not match public key type"))
}
if pub.N.Cmp(priv.N) != 0 {
if !priv.PublicKey.Equal(pub) {
return fail(errors.New("tls: private key does not match public key"))
}
case *ecdsa.PublicKey:
@@ -807,7 +804,7 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
if !ok {
return fail(errors.New("tls: private key type does not match public key type"))
}
if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
if !priv.PublicKey.Equal(pub) {
return fail(errors.New("tls: private key does not match public key"))
}
case ed25519.PublicKey:
@@ -815,7 +812,15 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
if !ok {
return fail(errors.New("tls: private key type does not match public key type"))
}
if !bytes.Equal(priv.Public().(ed25519.PublicKey), pub) {
if !priv.Public().(ed25519.PublicKey).Equal(pub) {
return fail(errors.New("tls: private key does not match public key"))
}
case *mldsa.PublicKey:
priv, ok := cert.PrivateKey.(*mldsa.PrivateKey)
if !ok {
return fail(errors.New("tls: private key type does not match public key type"))
}
if !priv.PublicKey().Equal(pub) {
return fail(errors.New("tls: private key does not match public key"))
}
default:
@@ -829,20 +834,21 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
// PKCS #1 private keys by default, while OpenSSL 1.0.0 generates PKCS #8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
key, err := x509.ParsePKCS8PrivateKey(der)
pkcs8Err := err // Return the PKCS#8 error if all parsing attempts fail.
if err != nil {
key, err = x509.ParsePKCS1PrivateKey(der)
}
if err != nil {
key, err = x509.ParseECPrivateKey(der)
}
if err != nil {
return nil, fmt.Errorf("tls: failed to parse private key: %w", pkcs8Err)
}
switch key := key.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey, *mldsa.PrivateKey:
return key, nil
default:
return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping")
}
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
switch key := key.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
return key, nil
default:
return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping")
}
}
if key, err := x509.ParseECPrivateKey(der); err == nil {
return key, nil
}
return nil, errors.New("tls: failed to parse private key")
}
}