Pārlūkot izejas kodu

Backport pion/dtls AEAD nonce generation fix

Rod Hynes 1 mēnesi atpakaļ
vecāks
revīzija
aec4612a9f

+ 1 - 1
go.mod

@@ -44,6 +44,7 @@ require (
 	github.com/Psiphon-Labs/quic-go v0.0.0-20250527153145-79fe45fb83b1
 	github.com/Psiphon-Labs/utls v0.0.0-20260129182755-24497d415a8d
 	github.com/armon/go-proxyproto v0.0.0-20180202201750-5b7edb60ff5f
+	github.com/axiomhq/hyperloglog v0.2.6
 	github.com/bifurcation/mint v0.0.0-20180306135233-198357931e61
 	github.com/bits-and-blooms/bloom/v3 v3.6.0
 	github.com/cespare/xxhash v1.1.0
@@ -105,7 +106,6 @@ require (
 	github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
 	github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect
 	github.com/andybalholm/brotli v1.1.1 // indirect
-	github.com/axiomhq/hyperloglog v0.2.6 // indirect
 	github.com/bits-and-blooms/bitset v1.10.0 // indirect
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
 	github.com/cloudflare/circl v1.6.1 // indirect

+ 0 - 8
go.sum

@@ -16,12 +16,6 @@ github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/Psiphon-Inc/rotate-safe-writer v0.0.0-20210303140923-464a7a37606e h1:NPfqIbzmijrl0VclX2t8eO5EPBhqe47LLGKpRrcVjXk=
 github.com/Psiphon-Inc/rotate-safe-writer v0.0.0-20210303140923-464a7a37606e/go.mod h1:ZdY5pBfat/WVzw3eXbIf7N1nZN0XD5H5+X8ZMDWbCs4=
-github.com/Psiphon-Inc/uds-ipc v0.0.0-20251003205000-ec2282b63bb4 h1:o6yYfSWOQ3A8GVd/9Pv8V2FwuVyDJ2gjsHSWnv2Igis=
-github.com/Psiphon-Inc/uds-ipc v0.0.0-20251003205000-ec2282b63bb4/go.mod h1:R8TGG+OXumorJdpAcSFE4SVTT2frMFEGY43Zc/44sNk=
-github.com/Psiphon-Inc/uds-ipc v0.0.0-20251003212312-ed8477de04f5 h1:ZR+pf49zi/729cdcMmaqKxtqpde04+QF3DaVFoE6xyM=
-github.com/Psiphon-Inc/uds-ipc v0.0.0-20251003212312-ed8477de04f5/go.mod h1:R8TGG+OXumorJdpAcSFE4SVTT2frMFEGY43Zc/44sNk=
-github.com/Psiphon-Inc/uds-ipc v0.0.0-20251007150957-166e89b034be h1:TDXrQ1eVlmc/eB3WofOXgYfDKYeiY19+ZCQCkH/6PcU=
-github.com/Psiphon-Inc/uds-ipc v0.0.0-20251007150957-166e89b034be/go.mod h1:R8TGG+OXumorJdpAcSFE4SVTT2frMFEGY43Zc/44sNk=
 github.com/Psiphon-Inc/uds-ipc v1.0.1 h1:K3Z0cS1XfzDdhxWTIwh/hiLrkRR83ZxUo2bqgBOGuZE=
 github.com/Psiphon-Inc/uds-ipc v1.0.1/go.mod h1:R8TGG+OXumorJdpAcSFE4SVTT2frMFEGY43Zc/44sNk=
 github.com/Psiphon-Labs/bolt v0.0.0-20200624191537-23cedaef7ad7 h1:Hx/NCZTnvoKZuIBwSmxE58KKoNLXIGG6hBJYN7pj9Ag=
@@ -34,8 +28,6 @@ github.com/Psiphon-Labs/psiphon-tls v0.0.0-20250318183125-2a2fae2db378 h1:LqI8cx
 github.com/Psiphon-Labs/psiphon-tls v0.0.0-20250318183125-2a2fae2db378/go.mod h1:7ZUnPnWT5z8J8hxfsVjKHYK77Zme/Y0If1b/zeziiJs=
 github.com/Psiphon-Labs/quic-go v0.0.0-20250527153145-79fe45fb83b1 h1:zD7JvZCV8gjvtI0AZmE81Ffc/v7A+qwU1/YfUmN/Flk=
 github.com/Psiphon-Labs/quic-go v0.0.0-20250527153145-79fe45fb83b1/go.mod h1:rONdWgPMbFjyyBai7gB1IBF4pT9r4l0GyiDst5XR1SY=
-github.com/Psiphon-Labs/utls v0.0.0-20250623193530-396869e9cd87 h1:h/OnQpPMwC7pKN9YQTJ+vQATjchta6kgumJNnkJBq1k=
-github.com/Psiphon-Labs/utls v0.0.0-20250623193530-396869e9cd87/go.mod h1:1vv0gVAzq9e2XYkW8HAKrmtuuZrBdDixQFx5H22KAjI=
 github.com/Psiphon-Labs/utls v0.0.0-20260129182755-24497d415a8d h1:PlKwrArEuQOVqEmThSs9KsXMiBduP8MSu9rlWmQ4jgE=
 github.com/Psiphon-Labs/utls v0.0.0-20260129182755-24497d415a8d/go.mod h1:1vv0gVAzq9e2XYkW8HAKrmtuuZrBdDixQFx5H22KAjI=
 github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=

+ 3 - 5
replace/dtls/pkg/crypto/ciphersuite/ccm.go

@@ -5,7 +5,6 @@ package ciphersuite
 
 import (
 	"crypto/aes"
-	"crypto/rand"
 	"encoding/binary"
 	"fmt"
 
@@ -65,10 +64,9 @@ func (c *CCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error)
 	payload := raw[recordlayer.HeaderSize:]
 	raw = raw[:recordlayer.HeaderSize]
 
-	nonce := append(append([]byte{}, c.localWriteIV[:4]...), make([]byte, 8)...)
-	if _, err := rand.Read(nonce[4:]); err != nil {
-		return nil, err
-	}
+	// [Psiphon]
+	// See comment in generateAEADNonce.
+	nonce := generateAEADNonce(c.localWriteIV, &pkt.Header)
 
 	additionalData := generateAEADAdditionalData(&pkt.Header, len(payload))
 	encryptedPayload := c.localCCM.Seal(nil, nonce, payload, additionalData)

+ 19 - 0
replace/dtls/pkg/crypto/ciphersuite/ciphersuite.go

@@ -33,6 +33,25 @@ func generateAEADAdditionalData(h *recordlayer.Header, payloadLen int) []byte {
 	return additionalData[:]
 }
 
+// [Psiphon]
+// Backport of https://github.com/pion/dtls/commit/61762dee8217991882c5eb79856b9e7a73ee349f. In
+// newer pion/dtls, AEAD nonce generation has moved to common code in aead.encrypt. In this older
+// fork, apply the fix to both CCM.Encrypt and GCM.Encrypt via this helper function.
+//
+// As in the upstream fix, nonce generation is changed from random bytes, which is more vulnerable
+// to reuse, to instead follow the scheme recommended in
+// https://www.rfc-editor.org/rfc/rfc9325#name-nonce-reuse-in-tls-12.
+func generateAEADNonce(writeIV []byte, h *recordlayer.Header) []byte {
+	if gcmNonceLength != 12 || ccmNonceLength != 12 {
+		panic("unexpected nonce length")
+	}
+	nonce := make([]byte, 12)
+	copy(nonce, writeIV[:4])
+	seq64 := (uint64(h.Epoch) << 48) | (h.SequenceNumber & 0x0000ffffffffffff)
+	binary.BigEndian.PutUint64(nonce[4:], seq64)
+	return nonce
+}
+
 // examinePadding returns, in constant time, the length of the padding to remove
 // from the end of payload. It also returns a byte which is equal to 255 if the
 // padding was valid and 0 otherwise. See RFC 2246, Section 6.2.3.2.

+ 3 - 6
replace/dtls/pkg/crypto/ciphersuite/gcm.go

@@ -6,7 +6,6 @@ package ciphersuite
 import (
 	"crypto/aes"
 	"crypto/cipher"
-	"crypto/rand"
 	"encoding/binary"
 	"fmt"
 
@@ -58,11 +57,9 @@ func (g *GCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error)
 	payload := raw[recordlayer.HeaderSize:]
 	raw = raw[:recordlayer.HeaderSize]
 
-	nonce := make([]byte, gcmNonceLength)
-	copy(nonce, g.localWriteIV[:4])
-	if _, err := rand.Read(nonce[4:]); err != nil {
-		return nil, err
-	}
+	// [Psiphon]
+	// See comment in generateAEADNonce.
+	nonce := generateAEADNonce(g.localWriteIV, &pkt.Header)
 
 	additionalData := generateAEADAdditionalData(&pkt.Header, len(payload))
 	encryptedPayload := g.localGCM.Seal(nil, nonce, payload, additionalData)

+ 3 - 5
vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go

@@ -5,7 +5,6 @@ package ciphersuite
 
 import (
 	"crypto/aes"
-	"crypto/rand"
 	"encoding/binary"
 	"fmt"
 
@@ -65,10 +64,9 @@ func (c *CCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error)
 	payload := raw[recordlayer.HeaderSize:]
 	raw = raw[:recordlayer.HeaderSize]
 
-	nonce := append(append([]byte{}, c.localWriteIV[:4]...), make([]byte, 8)...)
-	if _, err := rand.Read(nonce[4:]); err != nil {
-		return nil, err
-	}
+	// [Psiphon]
+	// See comment in generateAEADNonce.
+	nonce := generateAEADNonce(c.localWriteIV, &pkt.Header)
 
 	additionalData := generateAEADAdditionalData(&pkt.Header, len(payload))
 	encryptedPayload := c.localCCM.Seal(nil, nonce, payload, additionalData)

+ 19 - 0
vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go

@@ -33,6 +33,25 @@ func generateAEADAdditionalData(h *recordlayer.Header, payloadLen int) []byte {
 	return additionalData[:]
 }
 
+// [Psiphon]
+// Backport of https://github.com/pion/dtls/commit/61762dee8217991882c5eb79856b9e7a73ee349f. In
+// newer pion/dtls, AEAD nonce generation has moved to common code in aead.encrypt. In this older
+// fork, apply the fix to both CCM.Encrypt and GCM.Encrypt via this helper function.
+//
+// As in the upstream fix, nonce generation is changed from random bytes, which is more vulnerable
+// to reuse, to instead follow the scheme recommended in
+// https://www.rfc-editor.org/rfc/rfc9325#name-nonce-reuse-in-tls-12.
+func generateAEADNonce(writeIV []byte, h *recordlayer.Header) []byte {
+	if gcmNonceLength != 12 || ccmNonceLength != 12 {
+		panic("unexpected nonce length")
+	}
+	nonce := make([]byte, 12)
+	copy(nonce, writeIV[:4])
+	seq64 := (uint64(h.Epoch) << 48) | (h.SequenceNumber & 0x0000ffffffffffff)
+	binary.BigEndian.PutUint64(nonce[4:], seq64)
+	return nonce
+}
+
 // examinePadding returns, in constant time, the length of the padding to remove
 // from the end of payload. It also returns a byte which is equal to 255 if the
 // padding was valid and 0 otherwise. See RFC 2246, Section 6.2.3.2.

+ 3 - 6
vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go

@@ -6,7 +6,6 @@ package ciphersuite
 import (
 	"crypto/aes"
 	"crypto/cipher"
-	"crypto/rand"
 	"encoding/binary"
 	"fmt"
 
@@ -58,11 +57,9 @@ func (g *GCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error)
 	payload := raw[recordlayer.HeaderSize:]
 	raw = raw[:recordlayer.HeaderSize]
 
-	nonce := make([]byte, gcmNonceLength)
-	copy(nonce, g.localWriteIV[:4])
-	if _, err := rand.Read(nonce[4:]); err != nil {
-		return nil, err
-	}
+	// [Psiphon]
+	// See comment in generateAEADNonce.
+	nonce := generateAEADNonce(g.localWriteIV, &pkt.Header)
 
 	additionalData := generateAEADAdditionalData(&pkt.Header, len(payload))
 	encryptedPayload := g.localGCM.Seal(nil, nonce, payload, additionalData)