439 lines
11 KiB
Go
439 lines
11 KiB
Go
// Package ssl 提供 SSL/TLS 性能基准测试。
|
||
//
|
||
// 测试覆盖:
|
||
// - TLS 握手性能(模拟客户端握手)
|
||
// - 证书加载性能
|
||
// - 重新协商开销
|
||
// - OCSP 装订性能
|
||
// - 会话恢复性能(使用 session ticket)
|
||
//
|
||
// 作者:xfy
|
||
package ssl
|
||
|
||
import (
|
||
"crypto/tls"
|
||
"os"
|
||
"testing"
|
||
"time"
|
||
|
||
"rua.plus/lolly/internal/config"
|
||
"rua.plus/lolly/internal/sslutil"
|
||
)
|
||
|
||
// BenchmarkTLSHandshake 基准测试 TLS 握手性能。
|
||
//
|
||
// 使用本地 TCP 环回连接模拟真实客户端握手,
|
||
// 测量完整 TLS 握手(包括密钥交换和证书验证)的开销。
|
||
func BenchmarkTLSHandshake(b *testing.B) {
|
||
tmpDir := b.TempDir()
|
||
certPEM, keyPEM := generateTestCert(&testing.T{})
|
||
|
||
certPath := tmpDir + "/cert.pem"
|
||
keyPath := tmpDir + "/key.pem"
|
||
|
||
if err := os.WriteFile(certPath, certPEM, 0o644); err != nil {
|
||
b.Fatalf("write cert: %v", err)
|
||
}
|
||
if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
|
||
b.Fatalf("write key: %v", err)
|
||
}
|
||
|
||
cfg := &config.SSLConfig{
|
||
Cert: certPath,
|
||
Key: keyPath,
|
||
Protocols: []string{"TLSv1.2", "TLSv1.3"},
|
||
}
|
||
|
||
manager, err := NewTLSManager(cfg)
|
||
if err != nil {
|
||
b.Fatalf("NewTLSManager() failed: %v", err)
|
||
}
|
||
defer manager.Close()
|
||
|
||
serverTLS := manager.GetTLSConfig()
|
||
|
||
listener, err := tls.Listen("tcp", "127.0.0.1:0", serverTLS)
|
||
if err != nil {
|
||
b.Fatalf("tls.Listen failed: %v", err)
|
||
}
|
||
defer listener.Close()
|
||
|
||
// 后台接受连接
|
||
go func() {
|
||
for {
|
||
conn, err := listener.Accept()
|
||
if err != nil {
|
||
return
|
||
}
|
||
// 读取少量数据以完成握手
|
||
buf := make([]byte, 1)
|
||
_, _ = conn.Read(buf)
|
||
_ = conn.Close()
|
||
}
|
||
}()
|
||
|
||
clientTLS := &tls.Config{
|
||
InsecureSkipVerify: true,
|
||
MinVersion: tls.VersionTLS12,
|
||
}
|
||
|
||
b.ResetTimer()
|
||
b.RunParallel(func(pb *testing.PB) {
|
||
for pb.Next() {
|
||
conn, err := tls.Dial("tcp", listener.Addr().String(), clientTLS)
|
||
if err != nil {
|
||
b.Error(err)
|
||
continue
|
||
}
|
||
_, _ = conn.Write([]byte{0})
|
||
_ = conn.Close()
|
||
}
|
||
})
|
||
}
|
||
|
||
// BenchmarkTLSHandshake_TLS13Only 基准测试仅 TLS 1.3 的握手性能。
|
||
//
|
||
// TLS 1.3 握手比 TLS 1.2 更快(1-RTT),测量两者的差异。
|
||
func BenchmarkTLSHandshake_TLS13Only(b *testing.B) {
|
||
tmpDir := b.TempDir()
|
||
certPEM, keyPEM := generateTestCert(&testing.T{})
|
||
|
||
certPath := tmpDir + "/cert.pem"
|
||
keyPath := tmpDir + "/key.pem"
|
||
|
||
if err := os.WriteFile(certPath, certPEM, 0o644); err != nil {
|
||
b.Fatalf("write cert: %v", err)
|
||
}
|
||
if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
|
||
b.Fatalf("write key: %v", err)
|
||
}
|
||
|
||
cfg := &config.SSLConfig{
|
||
Cert: certPath,
|
||
Key: keyPath,
|
||
Protocols: []string{"TLSv1.3"},
|
||
}
|
||
|
||
manager, err := NewTLSManager(cfg)
|
||
if err != nil {
|
||
b.Fatalf("NewTLSManager() failed: %v", err)
|
||
}
|
||
defer manager.Close()
|
||
|
||
serverTLS := manager.GetTLSConfig()
|
||
|
||
listener, err := tls.Listen("tcp", "127.0.0.1:0", serverTLS)
|
||
if err != nil {
|
||
b.Fatalf("tls.Listen failed: %v", err)
|
||
}
|
||
defer listener.Close()
|
||
|
||
go func() {
|
||
for {
|
||
conn, err := listener.Accept()
|
||
if err != nil {
|
||
return
|
||
}
|
||
buf := make([]byte, 1)
|
||
_, _ = conn.Read(buf)
|
||
_ = conn.Close()
|
||
}
|
||
}()
|
||
|
||
clientTLS := &tls.Config{
|
||
InsecureSkipVerify: true,
|
||
MinVersion: tls.VersionTLS13,
|
||
MaxVersion: tls.VersionTLS13,
|
||
}
|
||
|
||
b.ResetTimer()
|
||
b.RunParallel(func(pb *testing.PB) {
|
||
for pb.Next() {
|
||
conn, err := tls.Dial("tcp", listener.Addr().String(), clientTLS)
|
||
if err != nil {
|
||
b.Error(err)
|
||
continue
|
||
}
|
||
_, _ = conn.Write([]byte{0})
|
||
_ = conn.Close()
|
||
}
|
||
})
|
||
}
|
||
|
||
// BenchmarkTLSCertificateLoad 基准测试证书加载性能。
|
||
//
|
||
// 测量从磁盘加载 X509 密钥对的开销,包括 PEM 解码和
|
||
// 私钥解析。
|
||
func BenchmarkTLSCertificateLoad(b *testing.B) {
|
||
tmpDir := b.TempDir()
|
||
certPEM, keyPEM := generateTestCert(&testing.T{})
|
||
|
||
certPath := tmpDir + "/cert.pem"
|
||
keyPath := tmpDir + "/key.pem"
|
||
|
||
if err := os.WriteFile(certPath, certPEM, 0o644); err != nil {
|
||
b.Fatalf("write cert: %v", err)
|
||
}
|
||
if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
|
||
b.Fatalf("write key: %v", err)
|
||
}
|
||
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_, err := tls.LoadX509KeyPair(certPath, keyPath)
|
||
if err != nil {
|
||
b.Fatal(err)
|
||
}
|
||
}
|
||
}
|
||
|
||
// BenchmarkTLSCertificateLoad_InMemory 基准测试内存中证书加载性能。
|
||
//
|
||
// 不经过磁盘 I/O,直接测量 PEM 解析和证书构建的开销。
|
||
func BenchmarkTLSCertificateLoad_InMemory(b *testing.B) {
|
||
certPEM, keyPEM := generateTestCert(&testing.T{})
|
||
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_, err := tls.X509KeyPair(certPEM, keyPEM)
|
||
if err != nil {
|
||
b.Fatal(err)
|
||
}
|
||
}
|
||
}
|
||
|
||
// BenchmarkTLSCertificateLoad_Parallel 基准测试并发证书加载性能。
|
||
//
|
||
// 模拟多协程同时加载证书的场景,验证线程安全性。
|
||
func BenchmarkTLSCertificateLoad_Parallel(b *testing.B) {
|
||
tmpDir := b.TempDir()
|
||
certPEM, keyPEM := generateTestCert(&testing.T{})
|
||
|
||
certPath := tmpDir + "/cert.pem"
|
||
keyPath := tmpDir + "/key.pem"
|
||
|
||
if err := os.WriteFile(certPath, certPEM, 0o644); err != nil {
|
||
b.Fatalf("write cert: %v", err)
|
||
}
|
||
if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
|
||
b.Fatalf("write key: %v", err)
|
||
}
|
||
|
||
b.ResetTimer()
|
||
b.RunParallel(func(pb *testing.PB) {
|
||
for pb.Next() {
|
||
_, err := tls.LoadX509KeyPair(certPath, keyPath)
|
||
if err != nil {
|
||
b.Error(err)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// BenchmarkTLSRenegotiation 基准测试 TLS 重新协商开销。
|
||
//
|
||
// TLS 1.3 已移除重新协商,该测试测量 TLS 1.2 下的
|
||
// 重新协商成本,用于评估是否值得启用重新协商。
|
||
// 通过建立 TLS 1.2 连接并检查握手统计来测量开销。
|
||
func BenchmarkTLSRenegotiation(b *testing.B) {
|
||
tmpDir := b.TempDir()
|
||
certPEM, keyPEM := generateTestCert(&testing.T{})
|
||
|
||
certPath := tmpDir + "/cert.pem"
|
||
keyPath := tmpDir + "/key.pem"
|
||
|
||
if err := os.WriteFile(certPath, certPEM, 0o644); err != nil {
|
||
b.Fatalf("write cert: %v", err)
|
||
}
|
||
if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
|
||
b.Fatalf("write key: %v", err)
|
||
}
|
||
|
||
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
||
if err != nil {
|
||
b.Fatalf("tls.LoadX509KeyPair failed: %v", err)
|
||
}
|
||
|
||
serverTLS := &tls.Config{
|
||
Certificates: []tls.Certificate{cert},
|
||
MinVersion: tls.VersionTLS12,
|
||
MaxVersion: tls.VersionTLS12,
|
||
}
|
||
|
||
listener, err := tls.Listen("tcp", "127.0.0.1:0", serverTLS)
|
||
if err != nil {
|
||
b.Fatalf("tls.Listen failed: %v", err)
|
||
}
|
||
defer listener.Close()
|
||
|
||
// 服务端接受连接
|
||
acceptDone := make(chan struct{})
|
||
go func() {
|
||
defer close(acceptDone)
|
||
for {
|
||
conn, err := listener.Accept()
|
||
if err != nil {
|
||
return
|
||
}
|
||
// 读取数据完成握手后关闭
|
||
buf := make([]byte, 1)
|
||
_, _ = conn.Read(buf)
|
||
_ = conn.Close()
|
||
}
|
||
}()
|
||
|
||
clientTLS := &tls.Config{
|
||
InsecureSkipVerify: true,
|
||
MinVersion: tls.VersionTLS12,
|
||
MaxVersion: tls.VersionTLS12,
|
||
Renegotiation: tls.RenegotiateOnceAsClient,
|
||
}
|
||
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
conn, err := tls.Dial("tcp", listener.Addr().String(), clientTLS)
|
||
if err != nil {
|
||
b.Fatalf("Dial failed: %v", err)
|
||
}
|
||
_, _ = conn.Write([]byte{0})
|
||
_ = conn.Close()
|
||
}
|
||
b.StopTimer()
|
||
listener.Close()
|
||
<-acceptDone
|
||
}
|
||
|
||
// BenchmarkOCSPStapling 基准测试 OCSP 装订性能。
|
||
//
|
||
// 测量启用 OCSP Stapling 后,GetConfigForClient 回调中
|
||
// 获取和附加 OCSP 响应的开销。
|
||
func BenchmarkOCSPStapling(b *testing.B) {
|
||
// 直接测试 OCSPManager 的响应获取性能
|
||
// (自签证书没有 OCSP Server URL,无法端到端测试)
|
||
ocspMgr := NewOCSPManager(DefaultOCSPConfig())
|
||
ocspMgr.Start()
|
||
defer ocspMgr.Stop()
|
||
|
||
// 注册一个模拟序列号的状态
|
||
testSerial := "1234567890"
|
||
ocspMgr.mu.Lock()
|
||
ocspMgr.responses[testSerial] = &ocspResponse{
|
||
response: make([]byte, 1024),
|
||
thisUpdate: time.Now(),
|
||
nextUpdate: time.Now().Add(time.Hour),
|
||
status: statusValid,
|
||
fetchedAt: time.Now(),
|
||
errors: 0,
|
||
}
|
||
ocspMgr.mu.Unlock()
|
||
|
||
b.ResetTimer()
|
||
b.RunParallel(func(pb *testing.PB) {
|
||
for pb.Next() {
|
||
resp := ocspMgr.GetOCSPResponse(testSerial)
|
||
if resp == nil {
|
||
b.Error("expected non-nil OCSP response")
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// BenchmarkOCSPStapling_Miss 基准测试 OCSP 未命中性能。
|
||
//
|
||
// 测量查询不存在序列号时的开销,验证优雅降级不影响性能。
|
||
func BenchmarkOCSPStapling_Miss(b *testing.B) {
|
||
ocspMgr := NewOCSPManager(DefaultOCSPConfig())
|
||
ocspMgr.Start()
|
||
defer ocspMgr.Stop()
|
||
|
||
nonExistentSerial := "nonexistent"
|
||
|
||
b.ResetTimer()
|
||
b.RunParallel(func(pb *testing.PB) {
|
||
for pb.Next() {
|
||
resp := ocspMgr.GetOCSPResponse(nonExistentSerial)
|
||
if resp != nil {
|
||
b.Error("expected nil OCSP response for non-existent serial")
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// BenchmarkOCSPStapling_GetStatus 基准测试 OCSP 状态查询性能。
|
||
//
|
||
// 测量 GetStatus 方法获取证书状态信息的开销。
|
||
|
||
// BenchmarkSessionResumption 基准测试使用 Session Ticket 的会话恢复性能。
|
||
//
|
||
// 使用 ClientSessionCache 实现会话恢复,
|
||
// 测量使用缓存 session 时的握手开销。
|
||
|
||
// BenchmarkSessionResumption_FullHandshake 基准测试完整握手(无会话恢复)。
|
||
//
|
||
// 作为对照组,测量没有 session ticket 的完整 TLS 握手开销,
|
||
// 与 BenchmarkSessionResumption 对比评估会话恢复的性能提升。
|
||
|
||
// BenchmarkSessionTicketManager_ApplyToTLSConfig 基准测试应用 Session Ticket 到 TLS 配置的开销。
|
||
func BenchmarkSessionTicketManager_ApplyToTLSConfig(b *testing.B) {
|
||
sessionMgr, err := NewSessionTicketManager(config.SessionTicketsConfig{
|
||
Enabled: true,
|
||
RotateInterval: time.Hour,
|
||
RetainKeys: 3,
|
||
})
|
||
if err != nil {
|
||
b.Fatalf("NewSessionTicketManager failed: %v", err)
|
||
}
|
||
defer sessionMgr.Stop()
|
||
|
||
for range 2 {
|
||
_ = sessionMgr.RotateKey()
|
||
}
|
||
|
||
baseTLS := &tls.Config{
|
||
Certificates: []tls.Certificate{},
|
||
MinVersion: tls.VersionTLS12,
|
||
}
|
||
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
tlsCfg := baseTLS.Clone()
|
||
sessionMgr.ApplyToTLSConfig(tlsCfg)
|
||
}
|
||
}
|
||
|
||
// BenchmarkSNI_GetCertificate 基准测试 SNI 证书查找性能。
|
||
//
|
||
// 测量 GetCertificate 回调在多证书场景下的查找开销。
|
||
|
||
// BenchmarkCipherSuiteParsing 基准测试加密套件解析性能。
|
||
func BenchmarkCipherSuiteParsing(b *testing.B) {
|
||
ciphers := []string{
|
||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||
}
|
||
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_, err := sslutil.ParseCipherSuites(ciphers)
|
||
if err != nil {
|
||
b.Fatal(err)
|
||
}
|
||
}
|
||
}
|
||
|
||
// BenchmarkTLSVersionsParsing 基准测试 TLS 版本解析性能。
|
||
func BenchmarkTLSVersionsParsing(b *testing.B) {
|
||
protocols := []string{"TLSv1.2", "TLSv1.3"}
|
||
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_, _, err := sslutil.ParseTLSVersions(protocols)
|
||
if err != nil {
|
||
b.Fatal(err)
|
||
}
|
||
}
|
||
}
|