Compare commits

...
Author SHA1 Message Date
Fangliding 50162ff81d Safe resume with pinCA & support session resume in uTLS 2026-08-21 17:35:12 +08:00
3 changed files with 32 additions and 15 deletions
+20 -8
View File
@@ -13,6 +13,7 @@ import (
"sync"
"time"
utls "github.com/refraction-networking/utls"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/ocsp"
@@ -22,6 +23,7 @@ import (
)
var globalSessionCache = tls.NewLRUClientSessionCache(128)
var uGlobalSessionCache = utls.NewLRUClientSessionCache(128)
// ParseCertificate converts a cert.Certificate to Certificate.
func ParseCertificate(c *cert.Certificate) *Certificate {
@@ -280,12 +282,10 @@ func (c *Config) parseServerName() string {
return c.ServerName
}
func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) (err error) {
// extract x509 certificates from rawCerts (verifiedChains will be nil if InsecureSkipVerify is true)
certs := make([]*x509.Certificate, len(rawCerts))
for i, asn1Data := range rawCerts {
certs[i], _ = x509.ParseCertificate(asn1Data)
}
// Note: Remember to update uVerifyConnectionAdapter if this function needs more fields in the future.
func (r *RandCarrier) verifyConnection(cs tls.ConnectionState) error {
certs := cs.PeerCertificates
// extract x509 certificates from cs.PeerCertificates
if len(certs) == 0 {
return errors.New("unexpected certs")
}
@@ -325,7 +325,7 @@ func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509
}
}
if verifyResult == foundCA {
errors.New("peer cert is invalid (against pinned CA and verifyPeerCertByName)")
return errors.New("peer cert is invalid (against pinned CA and verifyPeerCertByName)")
}
return errors.New("peer cert is invalid (against root CAs and verifyPeerCertByName)")
}
@@ -352,6 +352,18 @@ func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509
return nil // r.PinnedPeerCertSha256==nil && r.verifyPeerCertByName==nil
}
func uVerifyConnectionAdapter(f func(tls.ConnectionState) error) func(utls.ConnectionState) error {
if f == nil {
return nil
}
return func(cs utls.ConnectionState) error {
standardCS := tls.ConnectionState{
PeerCertificates: cs.PeerCertificates,
}
return f(standardCS)
}
}
type RandCarrier struct {
Config *tls.Config
RootCAs *x509.CertPool
@@ -389,7 +401,7 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
RootCAs: root,
NextProtos: slices.Clone(c.NextProtocol),
SessionTicketsDisabled: !c.EnableSessionResumption,
VerifyPeerCertificate: randCarrier.verifyPeerCert,
VerifyConnection: randCarrier.verifyConnection,
}
randCarrier.Config = config
if len(c.VerifyPeerCertByName) > 0 {
+6 -6
View File
@@ -110,15 +110,15 @@ func TestVerifyPeerLeafCert(t *testing.T) {
PinnedPeerCertSha256: [][]byte{leafHash[:]},
}
rawCerts := [][]byte{leaf.Raw}
err := r.verifyPeerCert(rawCerts, nil)
cs := tls.ConnectionState{PeerCertificates: []*x509.Certificate{leaf}}
err := r.verifyConnection(cs)
if err != nil {
t.Fatal("expected to verify leaf cert signed by pinned CA, but got error:", err)
}
// make the pinned hash incorrect
r.PinnedPeerCertSha256[0][0] += 1
err = r.verifyPeerCert(rawCerts, nil)
err = r.verifyConnection(cs)
if err == nil {
t.Fatal("expected to fail verifying leaf cert with incorrect pinned CA hash, but got no error")
}
@@ -138,15 +138,15 @@ func TestVerifyPeerCACert(t *testing.T) {
PinnedPeerCertSha256: [][]byte{caHash[:]},
}
rawCerts := [][]byte{leaf.Raw, ca.Raw}
err := r.verifyPeerCert(rawCerts, nil)
cs := tls.ConnectionState{PeerCertificates: []*x509.Certificate{leaf, ca}}
err := r.verifyConnection(cs)
if err != nil {
t.Fatal("expected to verify leaf cert signed by pinned CA, but got error:", err)
}
// make the pinned hash incorrect
r.PinnedPeerCertSha256[0][0] += 1
err = r.verifyPeerCert(rawCerts, nil)
err = r.verifyConnection(cs)
if err == nil {
t.Fatal("expected to fail verifying leaf cert with incorrect pinned CA hash, but got no error")
}
+6 -1
View File
@@ -152,11 +152,16 @@ func copyConfig(c *tls.Config) *utls.Config {
RootCAs: c.RootCAs,
ServerName: c.ServerName,
InsecureSkipVerify: c.InsecureSkipVerify,
VerifyPeerCertificate: c.VerifyPeerCertificate,
VerifyConnection: uVerifyConnectionAdapter(c.VerifyConnection),
SessionTicketsDisabled: c.SessionTicketsDisabled,
KeyLogWriter: c.KeyLogWriter,
EncryptedClientHelloConfigList: c.EncryptedClientHelloConfigList,
NextProtos: c.NextProtos,
}
if c.ClientSessionCache != nil {
config.ClientSessionCache = uGlobalSessionCache
}
return config
}