From 69793c864044ae04455c3e4aac9925c0650d9901 Mon Sep 17 00:00:00 2001 From: RPRX <63339210+RPRX@users.noreply.github.com> Date: Mon, 21 Jul 2025 01:51:33 +0000 Subject: [PATCH] REALITY protocol: Add optional Post-Quantum ML-DSA-65 signature to cert's ExtraExtensions --- common.go | 2 ++ go.mod | 5 +++-- go.sum | 10 ++++++---- handshake_server_tls13.go | 35 ++++++++++++++++++++++++++++++----- 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/common.go b/common.go index bd14b98..8b19ccf 100644 --- a/common.go +++ b/common.go @@ -562,6 +562,8 @@ type Config struct { MaxTimeDiff time.Duration ShortIds map[[8]byte]bool + Mldsa65Key []byte + LimitFallbackUpload LimitFallback LimitFallbackDownload LimitFallback diff --git a/go.mod b/go.mod index 9588881..ada78b1 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,10 @@ module github.com/xtls/reality go 1.24 require ( + github.com/cloudflare/circl v1.6.1 github.com/juju/ratelimit v1.0.2 github.com/pires/go-proxyproto v0.8.1 github.com/refraction-networking/utls v1.7.3 - golang.org/x/crypto v0.39.0 - golang.org/x/sys v0.33.0 + golang.org/x/crypto v0.40.0 + golang.org/x/sys v0.34.0 ) diff --git a/go.sum b/go.sum index ea33dbf..97773f2 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,12 @@ +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= 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/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0= github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= github.com/refraction-networking/utls v1.7.3 h1:L0WRhHY7Oq1T0zkdzVZMR6zWZv+sXbHB9zcuvsAEqCo= github.com/refraction-networking/utls v1.7.3/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= diff --git a/handshake_server_tls13.go b/handshake_server_tls13.go index 0fad34a..2f55176 100644 --- a/handshake_server_tls13.go +++ b/handshake_server_tls13.go @@ -16,6 +16,7 @@ import ( "crypto/rsa" "crypto/sha512" "crypto/x509" + "crypto/x509/pkix" "encoding/binary" "errors" "fmt" @@ -26,6 +27,7 @@ import ( "sort" "time" + "github.com/cloudflare/circl/sign/mldsa/mldsa65" "github.com/xtls/reality/fips140tls" "github.com/xtls/reality/hpke" "github.com/xtls/reality/tls13" @@ -70,14 +72,17 @@ type serverHandshakeStateTLS13 struct { } var ( - ed25519Priv ed25519.PrivateKey - signedCert []byte + ed25519Priv ed25519.PrivateKey + signedCert []byte + signedCertMldsa65 []byte ) func init() { certificate := x509.Certificate{SerialNumber: &big.Int{}} + certificateMldsa65 := x509.Certificate{SerialNumber: &big.Int{}, ExtraExtensions: []pkix.Extension{{Id: []int{0, 0}, Value: empty[:3309]}}} _, ed25519Priv, _ = ed25519.GenerateKey(rand.Reader) signedCert, _ = x509.CreateCertificate(rand.Reader, &certificate, &certificate, ed25519.PublicKey(ed25519Priv[32:]), ed25519Priv) + signedCertMldsa65, _ = x509.CreateCertificate(rand.Reader, &certificateMldsa65, &certificateMldsa65, ed25519.PublicKey(ed25519Priv[32:]), ed25519Priv) } func (hs *serverHandshakeStateTLS13) handshake() error { @@ -90,6 +95,10 @@ func (hs *serverHandshakeStateTLS13) handshake() error { } */ { + if c.config.Show { + fmt.Printf("REALITY remoteAddr: %v\tusing X25519MLKEM768: %v\n", c.RemoteAddr().String(), hs.hello.serverShare.group == X25519MLKEM768) + } + hs.suite = cipherSuiteTLS13ByID(hs.hello.cipherSuite) c.cipherSuite = hs.suite.id hs.transcript = hs.suite.hash.New() @@ -130,14 +139,30 @@ func (hs *serverHandshakeStateTLS13) handshake() error { } */ { - signedCert := append([]byte{}, signedCert...) + if c.config.Show { + fmt.Printf("REALITY remoteAddr: %v\tusing ML-DSA-65: %v\n", c.RemoteAddr().String(), len(c.config.Mldsa65Key) > 0) + } + + var cert []byte + if len(c.config.Mldsa65Key) > 0 { + cert = bytes.Clone(signedCertMldsa65) + } else { + cert = bytes.Clone(signedCert) + } h := hmac.New(sha512.New, c.AuthKey) h.Write(ed25519Priv[32:]) - h.Sum(signedCert[:len(signedCert)-64]) + h.Sum(cert[:len(cert)-64]) + + if len(c.config.Mldsa65Key) > 0 { + h.Write(hs.clientHello.original) + h.Write(hs.hello.original) + key, _ := mldsa65.Scheme().UnmarshalBinaryPrivateKey(c.config.Mldsa65Key) + mldsa65.SignTo(key.(*mldsa65.PrivateKey), h.Sum(nil), nil, false, cert[126:]) // fixed location + } hs.cert = &Certificate{ - Certificate: [][]byte{signedCert}, + Certificate: [][]byte{cert}, PrivateKey: ed25519Priv, } hs.sigAlg = Ed25519