Use nfsAEAD & pfsAEAD for paddings; Refine comments

https://github.com/XTLS/Xray-core/pull/4952#issuecomment-3210125349

373558ed7a (r164355865)
This commit is contained in:
RPRX
2025-08-21 11:27:09 +00:00
committed by GitHub
parent 373558ed7a
commit 38cc306c95
4 changed files with 35 additions and 31 deletions

View File

@@ -29,7 +29,8 @@ func Curve25519Genkey(StdEncoding bool, input_base64 string) {
} }
// Modify random bytes using algorithm described at: // Modify random bytes using algorithm described at:
// https://cr.yp.to/ecdh.html. // https://cr.yp.to/ecdh.html
// (Just to make sure printing the real private key)
privateKey[0] &= 248 privateKey[0] &= 248
privateKey[31] &= 127 privateKey[31] &= 127
privateKey[31] |= 64 privateKey[31] |= 64

View File

@@ -46,7 +46,7 @@ type ClientConn struct {
random []byte random []byte
aead cipher.AEAD aead cipher.AEAD
nonce []byte nonce []byte
peerAead cipher.AEAD peerAEAD cipher.AEAD
peerNonce []byte peerNonce []byte
PeerCache []byte PeerCache []byte
} }
@@ -97,21 +97,23 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*ClientConn, error) {
pfsDKey, _ := mlkem.NewDecapsulationKey768(pfsDKeySeed) pfsDKey, _ := mlkem.NewDecapsulationKey768(pfsDKeySeed)
pfsEKeyBytes := pfsDKey.EncapsulationKey().Bytes() pfsEKeyBytes := pfsDKey.EncapsulationKey().Bytes()
nfsKey, encapsulatedNfsKey := i.nfsEKey.Encapsulate() nfsKey, encapsulatedNfsKey := i.nfsEKey.Encapsulate()
paddingLen := crypto.RandBetween(100, 1000) nfsAEAD := NewAEAD(ClientCipher, nfsKey, pfsEKeyBytes, encapsulatedNfsKey)
clientHello := make([]byte, 5+11+1+1184+1088+5+paddingLen) clientHello := make([]byte, 5+11+1+1184+1088+crypto.RandBetween(100, 1000))
EncodeHeader(clientHello, 1, 11+1+1184+1088) EncodeHeader(clientHello, 1, 11+1+1184+1088)
copy(clientHello[5:], i.hash11[:]) copy(clientHello[5:], i.hash11[:])
clientHello[5+11] = ClientCipher clientHello[5+11] = ClientCipher
copy(clientHello[5+11+1:], pfsEKeyBytes) copy(clientHello[5+11+1:], pfsEKeyBytes)
copy(clientHello[5+11+1+1184:], encapsulatedNfsKey) copy(clientHello[5+11+1+1184:], encapsulatedNfsKey)
EncodeHeader(clientHello[5+11+1+1184+1088:], 23, int(paddingLen)) padding := clientHello[5+11+1+1184+1088:]
rand.Read(clientHello[5+11+1+1184+1088+5:]) rand.Read(padding) // important
EncodeHeader(padding, 23, len(padding)-5)
nfsAEAD.Seal(padding[:5], clientHello[5:5+11+1], padding[5:len(padding)-16], padding[:5])
if _, err := c.Conn.Write(clientHello); err != nil { if _, err := c.Conn.Write(clientHello); err != nil {
return nil, err return nil, err
} }
// client can send more paddings / NFS AEAD messages if needed // client can send more NFS AEAD paddings / messages if needed
_, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before server hello _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before server hello
if err != nil { if err != nil {
@@ -195,7 +197,7 @@ func (c *ClientConn) Read(b []byte) (int, error) {
if len(b) == 0 { if len(b) == 0 {
return 0, nil return 0, nil
} }
if c.peerAead == nil { if c.peerAEAD == nil {
_, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before random hello _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before random hello
if err != nil { if err != nil {
if c.instance != nil && strings.HasPrefix(err.Error(), "invalid header: ") { // 0-RTT's 0-RTT if c.instance != nil && strings.HasPrefix(err.Error(), "invalid header: ") { // 0-RTT's 0-RTT
@@ -221,7 +223,7 @@ func (c *ClientConn) Read(b []byte) (int, error) {
if c.random == nil { if c.random == nil {
return 0, errors.New("empty c.random") return 0, errors.New("empty c.random")
} }
c.peerAead = NewAEAD(ClientCipher, c.baseKey, peerRandomHello, c.random) c.peerAEAD = NewAEAD(ClientCipher, c.baseKey, peerRandomHello, c.random)
c.peerNonce = make([]byte, 12) c.peerNonce = make([]byte, 12)
} }
if len(c.PeerCache) != 0 { if len(c.PeerCache) != 0 {
@@ -244,13 +246,13 @@ func (c *ClientConn) Read(b []byte) (int, error) {
if len(dst) <= len(b) { if len(dst) <= len(b) {
dst = b[:len(dst)] // avoids another copy() dst = b[:len(dst)] // avoids another copy()
} }
var peerAead cipher.AEAD var peerAEAD cipher.AEAD
if bytes.Equal(c.peerNonce, MaxNonce) { if bytes.Equal(c.peerNonce, MaxNonce) {
peerAead = NewAEAD(ClientCipher, c.baseKey, peerData, h) peerAEAD = NewAEAD(ClientCipher, c.baseKey, peerData, h)
} }
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, h) _, err = c.peerAEAD.Open(dst[:0], c.peerNonce, peerData, h)
if peerAead != nil { if peerAEAD != nil {
c.peerAead = peerAead c.peerAEAD = peerAEAD
} }
IncreaseNonce(c.peerNonce) IncreaseNonce(c.peerNonce)
if err != nil { if err != nil {

View File

@@ -41,7 +41,7 @@ type ServerConn struct {
baseKey []byte baseKey []byte
ticket []byte ticket []byte
peerRandom []byte peerRandom []byte
peerAead cipher.AEAD peerAEAD cipher.AEAD
peerNonce []byte peerNonce []byte
PeerCache []byte PeerCache []byte
aead cipher.AEAD aead cipher.AEAD
@@ -173,22 +173,23 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*ServerConn, error) {
} }
pfsKey, encapsulatedPfsKey := pfsEKey.Encapsulate() pfsKey, encapsulatedPfsKey := pfsEKey.Encapsulate()
c.baseKey = append(pfsKey, nfsKey...) c.baseKey = append(pfsKey, nfsKey...)
pfsAEAD := NewAEAD(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey)
c.ticket = append(i.hash11[:], pfsAEAD.Seal(nil, peerClientHello[:11+1], []byte("VLESS"), pfsEKeyBytes)...)
IncreaseNonce(peerClientHello[:11+1])
c.ticket = append(i.hash11[:], NewAEAD(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Seal(nil, peerClientHello[:12], []byte("VLESS"), pfsEKeyBytes)...) serverHello := make([]byte, 5+1088+21+crypto.RandBetween(100, 1000))
paddingLen := crypto.RandBetween(100, 1000)
serverHello := make([]byte, 5+1088+21+5+paddingLen)
EncodeHeader(serverHello, 1, 1088+21) EncodeHeader(serverHello, 1, 1088+21)
copy(serverHello[5:], encapsulatedPfsKey) copy(serverHello[5:], encapsulatedPfsKey)
copy(serverHello[5+1088:], c.ticket[11:]) copy(serverHello[5+1088:], c.ticket[11:])
EncodeHeader(serverHello[5+1088+21:], 23, int(paddingLen)) padding := serverHello[5+1088+21:]
rand.Read(serverHello[5+1088+21+5:]) rand.Read(padding) // important
EncodeHeader(padding, 23, len(padding)-5)
pfsAEAD.Seal(padding[:5], peerClientHello[:11+1], padding[5:len(padding)-16], padding[:5])
if _, err := c.Conn.Write(serverHello); err != nil { if _, err := c.Conn.Write(serverHello); err != nil {
return nil, err return nil, err
} }
// server can send more paddings / PFS AEAD messages if needed // server can send more PFS AEAD paddings / messages if needed
if i.minutes > 0 { if i.minutes > 0 {
i.Lock() i.Lock()
@@ -207,7 +208,7 @@ func (c *ServerConn) Read(b []byte) (int, error) {
if len(b) == 0 { if len(b) == 0 {
return 0, nil return 0, nil
} }
if c.peerAead == nil { if c.peerAEAD == nil {
if c.peerRandom == nil { // 1-RTT's 0-RTT if c.peerRandom == nil { // 1-RTT's 0-RTT
_, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before ticket hello _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before ticket hello
if err != nil { if err != nil {
@@ -228,7 +229,7 @@ func (c *ServerConn) Read(b []byte) (int, error) {
} }
c.peerRandom = peerTicketHello[32:] c.peerRandom = peerTicketHello[32:]
} }
c.peerAead = NewAEAD(c.cipher, c.baseKey, c.peerRandom, c.ticket) c.peerAEAD = NewAEAD(c.cipher, c.baseKey, c.peerRandom, c.ticket)
c.peerNonce = make([]byte, 12) c.peerNonce = make([]byte, 12)
} }
if len(c.PeerCache) != 0 { if len(c.PeerCache) != 0 {
@@ -251,13 +252,13 @@ func (c *ServerConn) Read(b []byte) (int, error) {
if len(dst) <= len(b) { if len(dst) <= len(b) {
dst = b[:len(dst)] // avoids another copy() dst = b[:len(dst)] // avoids another copy()
} }
var peerAead cipher.AEAD var peerAEAD cipher.AEAD
if bytes.Equal(c.peerNonce, MaxNonce) { if bytes.Equal(c.peerNonce, MaxNonce) {
peerAead = NewAEAD(c.cipher, c.baseKey, peerData, h) peerAEAD = NewAEAD(c.cipher, c.baseKey, peerData, h)
} }
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, h) _, err = c.peerAEAD.Open(dst[:0], c.peerNonce, peerData, h)
if peerAead != nil { if peerAEAD != nil {
c.peerAead = peerAead c.peerAEAD = peerAEAD
} }
IncreaseNonce(c.peerNonce) IncreaseNonce(c.peerNonce)
if err != nil { if err != nil {

View File

@@ -57,7 +57,7 @@ func NewXorConn(conn net.Conn, mode uint32, pKey *ecdh.PublicKey, sKey *ecdh.Pri
if pKey != nil { if pKey != nil {
c.head = make([]byte, 16+32) c.head = make([]byte, 16+32)
rand.Read(c.head) rand.Read(c.head)
eSKey, _ := ecdh.X25519().GenerateKey(rand.Reader) eSKey, _ := ecdh.X25519().NewPrivateKey(c.head[16:])
NewCTR(pKey.Bytes(), c.head[:16], false).XORKeyStream(c.head[16:], eSKey.PublicKey().Bytes()) // make X25519 public key distinguishable from random bytes NewCTR(pKey.Bytes(), c.head[:16], false).XORKeyStream(c.head[16:], eSKey.PublicKey().Bytes()) // make X25519 public key distinguishable from random bytes
c.key, _ = eSKey.ECDH(pKey) c.key, _ = eSKey.ECDH(pKey)
c.ctr = NewCTR(c.key, c.head[:16], false) c.ctr = NewCTR(c.key, c.head[:16], false)