- 新增 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 文档
140 lines
4.6 KiB
Go
140 lines
4.6 KiB
Go
package notify
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ecdh"
|
|
"crypto/hmac"
|
|
cryptorand "crypto/rand"
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"errors"
|
|
)
|
|
|
|
const (
|
|
peerAttachECDHEPublicKeySize = 32
|
|
peerAttachSessionIDSize = 16
|
|
|
|
peerAttachKeyModeStatic = "psk-static"
|
|
peerAttachKeyModeECDHE = "psk-ecdhe"
|
|
transportKeyModeExternal = "external"
|
|
)
|
|
|
|
var errPeerAttachForwardSecrecyInvalid = errors.New("peer attach forward secrecy is invalid")
|
|
|
|
type peerAttachRequestState struct {
|
|
forwardSecrecy *peerAttachForwardSecrecyClientState
|
|
}
|
|
|
|
type peerAttachForwardSecrecyClientState struct {
|
|
privateKey *ecdh.PrivateKey
|
|
publicKey []byte
|
|
}
|
|
|
|
type peerAttachResponseVerifyResult struct {
|
|
authFallback bool
|
|
steadyProfile transportProtectionProfile
|
|
}
|
|
|
|
func newPeerAttachForwardSecrecyClientState() (*peerAttachForwardSecrecyClientState, error) {
|
|
curve := ecdh.X25519()
|
|
privateKey, err := curve.GenerateKey(cryptorand.Reader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
publicKey := privateKey.PublicKey().Bytes()
|
|
if len(publicKey) != peerAttachECDHEPublicKeySize {
|
|
return nil, errPeerAttachForwardSecrecyInvalid
|
|
}
|
|
return &peerAttachForwardSecrecyClientState{
|
|
privateKey: privateKey,
|
|
publicKey: bytes.Clone(publicKey),
|
|
}, nil
|
|
}
|
|
|
|
func derivePeerAttachForwardSecrecyTransportProfile(base transportProtectionProfile, bootstrapKey []byte, localPrivateKey *ecdh.PrivateKey, peerPublicKey []byte, req peerAttachRequest, resp peerAttachResponse) (transportProtectionProfile, error) {
|
|
if len(bootstrapKey) == 0 || localPrivateKey == nil || len(peerPublicKey) != peerAttachECDHEPublicKeySize {
|
|
return transportProtectionProfile{}, errPeerAttachForwardSecrecyInvalid
|
|
}
|
|
curve := ecdh.X25519()
|
|
publicKey, err := curve.NewPublicKey(peerPublicKey)
|
|
if err != nil {
|
|
return transportProtectionProfile{}, errPeerAttachForwardSecrecyInvalid
|
|
}
|
|
sharedSecret, err := localPrivateKey.ECDH(publicKey)
|
|
if err != nil {
|
|
return transportProtectionProfile{}, errPeerAttachForwardSecrecyInvalid
|
|
}
|
|
transcriptHash := peerAttachForwardSecrecyTranscriptHash(req, resp)
|
|
ikm := make([]byte, 0, len(sharedSecret)+len(transcriptHash))
|
|
ikm = append(ikm, sharedSecret...)
|
|
ikm = append(ikm, transcriptHash...)
|
|
prk := hkdfExtractSHA256(bootstrapKey, ikm)
|
|
sessionKey := hkdfExpandSHA256(prk, []byte("notify/transport/session/v1"), 32)
|
|
sessionID := hkdfExpandSHA256(prk, []byte("notify/session-id/v1"), peerAttachSessionIDSize)
|
|
return deriveModernPSKSessionProtectionProfile(base, sessionKey, sessionID)
|
|
}
|
|
|
|
func peerAttachForwardSecrecyTranscriptHash(req peerAttachRequest, resp peerAttachResponse) []byte {
|
|
buf := make([]byte, 0, 256+len(req.PeerID)+len(resp.PeerID)+len(resp.Error)+len(req.ClientECDHEPublicKey)+len(resp.ServerECDHEPublicKey))
|
|
buf = appendPeerAttachTranscriptString(buf, "notify/peer-attach/forward-secrecy/v1")
|
|
buf = binary.BigEndian.AppendUint64(buf, req.Features)
|
|
buf = appendPeerAttachTranscriptString(buf, req.PeerID)
|
|
buf = appendPeerAttachTranscriptBytes(buf, req.ClientNonce)
|
|
buf = appendPeerAttachTranscriptBytes(buf, req.ClientECDHEPublicKey)
|
|
buf = binary.BigEndian.AppendUint64(buf, resp.Features)
|
|
buf = appendPeerAttachTranscriptString(buf, resp.PeerID)
|
|
buf = appendPeerAttachTranscriptBool(buf, resp.Accepted)
|
|
buf = appendPeerAttachTranscriptBool(buf, resp.Reused)
|
|
buf = appendPeerAttachTranscriptString(buf, resp.Error)
|
|
buf = appendPeerAttachTranscriptBytes(buf, resp.ServerNonce)
|
|
buf = appendPeerAttachTranscriptString(buf, resp.KeyMode)
|
|
buf = appendPeerAttachTranscriptBytes(buf, resp.ServerECDHEPublicKey)
|
|
sum := sha256.Sum256(buf)
|
|
return sum[:]
|
|
}
|
|
|
|
func appendPeerAttachTranscriptBytes(dst []byte, data []byte) []byte {
|
|
dst = binary.BigEndian.AppendUint32(dst, uint32(len(data)))
|
|
return append(dst, data...)
|
|
}
|
|
|
|
func appendPeerAttachTranscriptString(dst []byte, value string) []byte {
|
|
return appendPeerAttachTranscriptBytes(dst, []byte(value))
|
|
}
|
|
|
|
func appendPeerAttachTranscriptBool(dst []byte, value bool) []byte {
|
|
if value {
|
|
return append(dst, 1)
|
|
}
|
|
return append(dst, 0)
|
|
}
|
|
|
|
func hkdfExtractSHA256(salt []byte, ikm []byte) []byte {
|
|
mac := hmac.New(sha256.New, salt)
|
|
_, _ = mac.Write(ikm)
|
|
return mac.Sum(nil)
|
|
}
|
|
|
|
func hkdfExpandSHA256(prk []byte, info []byte, size int) []byte {
|
|
if size <= 0 {
|
|
return nil
|
|
}
|
|
out := make([]byte, 0, size)
|
|
var block []byte
|
|
for counter := byte(1); len(out) < size; counter++ {
|
|
mac := hmac.New(sha256.New, prk)
|
|
if len(block) != 0 {
|
|
_, _ = mac.Write(block)
|
|
}
|
|
_, _ = mac.Write(info)
|
|
_, _ = mac.Write([]byte{counter})
|
|
block = mac.Sum(nil)
|
|
remaining := size - len(out)
|
|
if remaining > len(block) {
|
|
remaining = len(block)
|
|
}
|
|
out = append(out, block[:remaining]...)
|
|
}
|
|
return out
|
|
}
|