mirror of
https://github.com/XTLS/REALITY.git
synced 2026-09-22 23:08:01 +03:00
Sync upstream Go 1.27+; Add "[REALITY] SECTION" comments (#30)
https://github.com/XTLS/REALITY/pull/30#issuecomment-5579024460 https://github.com/XTLS/REALITY/pull/30#issuecomment-5594872257 https://github.com/XTLS/REALITY/pull/38#discussion_r4058349112
This commit is contained in:
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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]]
|
||||
}
|
||||
|
||||
@@ -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
@@ -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,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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.0–1.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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user