feat(transport): 完成安全架构拆分并收口 stream/bulk 传输优化

- 新增 managed/external/nested 三种传输保护模式
  - 新增 peer attach 显式认证、抗重放、channel binding 和可选前向保密协商
  - 明确单连接注入与可重拨连接源的语义边界
  - 禁止 ConnectByConn 场景下 dedicated bulk 走 sidecar,auto 模式自动回退 shared
  - 修正 dedicated attach 在 bootstrap/steady profile 切换下的处理逻辑
  - 优化 shared bulk super-batch 与批量 framed write 路径
  - 降低 stream/bulk fast path 的复制和分发损耗
  - 补齐 benchmark、回归测试、运行时快照和 README 文档
This commit is contained in:
2026-04-20 16:35:44 +08:00
parent f038a89771
commit 98ef9e7fcc
52 changed files with 4069 additions and 445 deletions
+157 -44
View File
@@ -15,9 +15,10 @@ import (
)
var (
errModernPSKSecretEmpty = errors.New("modern psk secret must be non-empty")
errModernPSKPayload = errors.New("invalid modern psk payload")
errModernPSKRequired = errors.New("modern psk is required: call UseModernPSKClient/UseModernPSKServer or set a transport key before Connect/Listen")
errModernPSKSecretEmpty = errors.New("modern psk secret must be non-empty")
errModernPSKPayload = errors.New("invalid modern psk payload")
errModernPSKRequired = errors.New("transport security is required: call UseModernPSKClient/UseModernPSKServer, UsePSKOverExternalTransportClient/Server, or set a transport key before Connect/Listen")
errModernPSKForwardSecrecyUnsupported = errors.New("forward secrecy is unsupported for external transport protection")
)
var (
@@ -47,9 +48,10 @@ var modernPSKPayloadPool sync.Pool
// The current profile derives a 32-byte transport key with Argon2id and uses
// AES-GCM with a per-codec nonce prefix plus a per-message counter.
type ModernPSKOptions struct {
Salt []byte
AAD []byte
Argon2Params starcrypto.Argon2Params
Salt []byte
AAD []byte
Argon2Params starcrypto.Argon2Params
RequireForwardSecrecy bool
}
// DefaultModernPSKOptions returns the recommended settings for the current
@@ -78,24 +80,17 @@ func defaultModernPSKTransportBundle() modernPSKTransportBundle {
// Argon2id, and switches message protection to AES-GCM. Configure it before
// calling Connect/ConnectTimeout.
func UseModernPSKClient(c Client, sharedSecret []byte, opts *ModernPSKOptions) error {
key, aad, err := deriveModernPSKKey(sharedSecret, opts)
managed, err := deriveModernPSKProtectionProfile(sharedSecret, opts, ProtectionManaged)
if err != nil {
return err
}
transport := buildModernPSKTransportBundle(aad)
runtime, err := newModernPSKCodecRuntime(key, aad)
if err != nil {
return err
}
c.SetSecretKey(key)
c.SetMsgEn(transport.msgEn)
c.SetMsgDe(transport.msgDe)
if client, ok := c.(*ClientCommon); ok {
client.fastStreamEncode = transport.fastStreamEncode
client.fastBulkEncode = transport.fastBulkEncode
client.fastPlainEncode = transport.fastPlainEncode
client.modernPSKRuntime = runtime
client.configureClientSecurityProfiles(AuthPSK, ProtectionManaged, managed, managed, opts != nil && opts.RequireForwardSecrecy)
return nil
}
c.SetSecretKey(managed.secretKey)
c.SetMsgEn(managed.msgEn)
c.SetMsgDe(managed.msgDe)
c.SetSkipExchangeKey(true)
return nil
}
@@ -106,24 +101,95 @@ func UseModernPSKClient(c Client, sharedSecret []byte, opts *ModernPSKOptions) e
// It derives a transport key with Argon2id and switches message protection to
// AES-GCM. Configure it before calling Listen.
func UseModernPSKServer(s Server, sharedSecret []byte, opts *ModernPSKOptions) error {
key, aad, err := deriveModernPSKKey(sharedSecret, opts)
managed, err := deriveModernPSKProtectionProfile(sharedSecret, opts, ProtectionManaged)
if err != nil {
return err
}
transport := buildModernPSKTransportBundle(aad)
runtime, err := newModernPSKCodecRuntime(key, aad)
if err != nil {
return err
}
s.SetSecretKey(key)
s.SetDefaultCommEncode(transport.msgEn)
s.SetDefaultCommDecode(transport.msgDe)
if server, ok := s.(*ServerCommon); ok {
server.defaultFastStreamEncode = transport.fastStreamEncode
server.defaultFastBulkEncode = transport.fastBulkEncode
server.defaultFastPlainEncode = transport.fastPlainEncode
server.defaultModernPSKRuntime = runtime
server.configureServerSecurityProfiles(AuthPSK, ProtectionManaged, managed, managed, opts != nil && opts.RequireForwardSecrecy)
return nil
}
s.SetSecretKey(managed.secretKey)
s.SetDefaultCommEncode(managed.msgEn)
s.SetDefaultCommDecode(managed.msgDe)
return nil
}
// UsePSKOverExternalTransportClient authenticates bootstrap with PSK and then
// trusts the external channel for steady-state payload protection.
func UsePSKOverExternalTransportClient(c Client, sharedSecret []byte, opts *ModernPSKOptions) error {
if opts != nil && opts.RequireForwardSecrecy {
return errModernPSKForwardSecrecyUnsupported
}
bootstrap, err := deriveModernPSKProtectionProfile(sharedSecret, opts, ProtectionManaged)
if err != nil {
return err
}
steady := buildExternalProtectionProfile(bootstrap.secretKey)
if client, ok := c.(*ClientCommon); ok {
client.configureClientSecurityProfiles(AuthPSK, ProtectionExternal, bootstrap, steady, false)
return nil
}
c.SetSecretKey(bootstrap.secretKey)
c.SetMsgEn(bootstrap.msgEn)
c.SetMsgDe(bootstrap.msgDe)
c.SetSkipExchangeKey(true)
return nil
}
// UsePSKOverExternalTransportServer authenticates bootstrap with PSK and then
// trusts the external channel for steady-state payload protection.
func UsePSKOverExternalTransportServer(s Server, sharedSecret []byte, opts *ModernPSKOptions) error {
if opts != nil && opts.RequireForwardSecrecy {
return errModernPSKForwardSecrecyUnsupported
}
bootstrap, err := deriveModernPSKProtectionProfile(sharedSecret, opts, ProtectionManaged)
if err != nil {
return err
}
steady := buildExternalProtectionProfile(bootstrap.secretKey)
if server, ok := s.(*ServerCommon); ok {
server.configureServerSecurityProfiles(AuthPSK, ProtectionExternal, bootstrap, steady, false)
return nil
}
s.SetSecretKey(bootstrap.secretKey)
s.SetDefaultCommEncode(bootstrap.msgEn)
s.SetDefaultCommDecode(bootstrap.msgDe)
return nil
}
// UseNestedSecurityClient keeps notify transport protection enabled even when
// the physical connection is already protected by an outer trusted channel.
func UseNestedSecurityClient(c Client, sharedSecret []byte, opts *ModernPSKOptions) error {
managed, err := deriveModernPSKProtectionProfile(sharedSecret, opts, ProtectionNested)
if err != nil {
return err
}
if client, ok := c.(*ClientCommon); ok {
client.configureClientSecurityProfiles(AuthPSK, ProtectionNested, managed, managed, opts != nil && opts.RequireForwardSecrecy)
return nil
}
c.SetSecretKey(managed.secretKey)
c.SetMsgEn(managed.msgEn)
c.SetMsgDe(managed.msgDe)
c.SetSkipExchangeKey(true)
return nil
}
// UseNestedSecurityServer keeps notify transport protection enabled even when
// the physical connection is already protected by an outer trusted channel.
func UseNestedSecurityServer(s Server, sharedSecret []byte, opts *ModernPSKOptions) error {
managed, err := deriveModernPSKProtectionProfile(sharedSecret, opts, ProtectionNested)
if err != nil {
return err
}
if server, ok := s.(*ServerCommon); ok {
server.configureServerSecurityProfiles(AuthPSK, ProtectionNested, managed, managed, opts != nil && opts.RequireForwardSecrecy)
return nil
}
s.SetSecretKey(managed.secretKey)
s.SetDefaultCommEncode(managed.msgEn)
s.SetDefaultCommDecode(managed.msgDe)
return nil
}
@@ -132,15 +198,22 @@ func UseModernPSKServer(s Server, sharedSecret []byte, opts *ModernPSKOptions) e
//
// It is kept only as an explicit fallback path for existing deployments.
func UseLegacySecurityClient(c Client) {
if client, ok := c.(*ClientCommon); ok {
client.clearClientSecurityProfiles()
client.setClientTransportProtectionProfile(transportProtectionProfile{
mode: ProtectionManaged,
msgEn: defaultMsgEn,
msgDe: defaultMsgDe,
secretKey: bytes.Clone(defaultAesKey),
})
client.securityReadyCheck = false
client.skipKeyExchange = false
client.handshakeRsaPubKey = bytes.Clone(defaultRsaPubKey)
return
}
c.SetSecretKey(bytes.Clone(defaultAesKey))
c.SetMsgEn(defaultMsgEn)
c.SetMsgDe(defaultMsgDe)
if client, ok := c.(*ClientCommon); ok {
client.fastStreamEncode = nil
client.fastBulkEncode = nil
client.fastPlainEncode = nil
client.modernPSKRuntime = nil
}
c.SetSkipExchangeKey(false)
c.SetRsaPubKey(bytes.Clone(defaultRsaPubKey))
}
@@ -150,15 +223,21 @@ func UseLegacySecurityClient(c Client) {
//
// It is kept only as an explicit fallback path for existing deployments.
func UseLegacySecurityServer(s Server) {
if server, ok := s.(*ServerCommon); ok {
server.clearServerSecurityProfiles()
server.setServerDefaultTransportProtectionProfile(transportProtectionProfile{
mode: ProtectionManaged,
msgEn: defaultMsgEn,
msgDe: defaultMsgDe,
secretKey: bytes.Clone(defaultAesKey),
})
server.securityReadyCheck = false
server.handshakeRsaKey = bytes.Clone(defaultRsaKey)
return
}
s.SetSecretKey(bytes.Clone(defaultAesKey))
s.SetDefaultCommEncode(defaultMsgEn)
s.SetDefaultCommDecode(defaultMsgDe)
if server, ok := s.(*ServerCommon); ok {
server.defaultFastStreamEncode = nil
server.defaultFastBulkEncode = nil
server.defaultFastPlainEncode = nil
server.defaultModernPSKRuntime = nil
}
s.SetRsaPrivKey(bytes.Clone(defaultRsaKey))
}
@@ -174,6 +253,40 @@ func deriveModernPSKKey(sharedSecret []byte, opts *ModernPSKOptions) ([]byte, []
return key, cfg.AAD, nil
}
func deriveModernPSKProtectionProfile(sharedSecret []byte, opts *ModernPSKOptions, mode ProtectionMode) (transportProtectionProfile, error) {
key, aad, err := deriveModernPSKKey(sharedSecret, opts)
if err != nil {
return transportProtectionProfile{}, err
}
transport := buildModernPSKTransportBundle(aad)
runtime, err := newModernPSKCodecRuntime(key, aad)
if err != nil {
return transportProtectionProfile{}, err
}
return newTransportProtectionProfile(mode, transport, runtime, key), nil
}
func buildExternalProtectionProfile(secretKey []byte) transportProtectionProfile {
return newTransportProtectionProfile(ProtectionExternal, buildExternalTransportBundle(), nil, secretKey)
}
func deriveModernPSKSessionProtectionProfile(base transportProtectionProfile, sessionKey []byte, sessionID []byte) (transportProtectionProfile, error) {
aad := bytes.Clone(defaultModernPSKAAD)
if base.runtime != nil && len(base.runtime.aad) != 0 {
aad = bytes.Clone(base.runtime.aad)
}
runtime, err := newModernPSKCodecRuntime(sessionKey, aad)
if err != nil {
return transportProtectionProfile{}, err
}
profile := newTransportProtectionProfile(base.mode, buildModernPSKTransportBundle(aad), runtime, sessionKey)
profile.keyMode = peerAttachKeyModeECDHE
profile.sessionID = cloneTransportSessionID(sessionID)
profile.forwardSecrecy = true
profile.forwardSecrecyFallback = false
return profile, nil
}
func normalizeModernPSKOptions(opts *ModernPSKOptions) ModernPSKOptions {
cfg := DefaultModernPSKOptions()
if opts == nil {