mirror of
https://github.com/hackdiaz-dev/free-turn-proxy.git
synced 2026-10-12 09:41:53 +02:00
refactor(wrap): перейти на мимикрию под SRTP в обход content-фильтра VK TURN
Wire-формат noise-AEAD (1b7cb4c) был реализован верно, но перестал доставлять трафик через VK TURN после auth: ChannelData payload, не похожий по форме на SRTP/DTLS, похоже, дропается VK TURN после недавнего изменения на стороне сервера. Новый wire-формат мимикрирует под SRTP (RFC 3550 / RFC 7714 AEAD-профиль): [12B RTP header | 12B nonce | AEAD ciphertext | 16B tag] RTP header: byte 0: 0x80 V=2, P=0, X=0, CC=0 byte 1: 0x6F M=0, PT=111 (opus, частый VK voice PT) byte 2-3: seq16 BE монотонный, init random byte 4-7: ts32 BE монотонный +960 на пакет (20ms @ 48kHz) byte 8-11: SSRC случайный на conn, MSB кодирует направление Nonce = 4B sessionID (MSB=направление) || 8B counter (init random). AAD = первые 24 байта, так что любое искажение RTP-заголовка ломает Open. Overhead 40 B/пакет против 36 у noise-AEAD и 12 у исходного XOR. Если VK TURN форвардит SRTP-образный трафик по быстрому пути, повышенный overhead окупается отсутствием медленного shaping. Тесты: round-trip, прогрессия RTP-полей, бит направления, искажение AAD.
This commit is contained in:
+1
-1
@@ -2222,7 +2222,7 @@ func createSmuxSession(ctx context.Context, tp *turnParams, peer *net.UDPAddr, i
|
||||
}
|
||||
|
||||
// relayPacketConn wraps a TURN relay PacketConn to direct all writes to the peer.
|
||||
// When wc != nil, packets are wrapped/unwrapped with AEAD noise obfuscation.
|
||||
// When wc != nil, packets are wrapped/unwrapped with SRTP-mimicry AEAD.
|
||||
type relayPacketConn struct {
|
||||
relay net.PacketConn
|
||||
peer net.Addr
|
||||
|
||||
+59
-48
@@ -14,35 +14,45 @@ import (
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
// Wire format — noise-only AEAD (no protocol mimicry):
|
||||
// Wire format — SRTP-like mimicry:
|
||||
//
|
||||
// [1B header | (prefix_len-1)B random | 12B nonce | AEAD ciphertext | 16B tag]
|
||||
// [12B RTP header | 12B explicit nonce | AEAD ciphertext | 16B tag]
|
||||
//
|
||||
// prefix_len = 1 + (header & 0x07), so byte 0 is uniformly random across all
|
||||
// 256 values while encoding 1..8 bytes of leading random padding. There is
|
||||
// no fixed byte, no version, no length field — everything past the header
|
||||
// byte is indistinguishable from random to a passive observer.
|
||||
// RTP header (RFC 3550):
|
||||
//
|
||||
// Nonce (12B) = 4B sessionID || 8B counter (big-endian). sessionID has its
|
||||
// MSB set on server side, cleared on client side, preventing nonce reuse
|
||||
// between directions under the same key. counter starts at a random uint64
|
||||
// to avoid reuse across process restarts.
|
||||
// byte 0: 0x80 V=2, P=0, X=0, CC=0
|
||||
// byte 1: 0x6F M=0, PT=111 (opus, typical voice PT)
|
||||
// byte 2-3: seq16 BE monotonic, init random
|
||||
// byte 4-7: ts32 BE monotonic, init random, increments by 960 (20ms @ 48kHz)
|
||||
// byte 8-11: SSRC random per conn, MSB encodes direction
|
||||
//
|
||||
// AAD = nonce. The random prefix is not authenticated (it carries no
|
||||
// information).
|
||||
// 12B explicit nonce = 4B sessionID || 8B counter (BE). sessionID MSB
|
||||
// matches SSRC MSB (direction bit). counter starts at a random uint64.
|
||||
// AAD = first 24 bytes (RTP header || nonce).
|
||||
//
|
||||
// VK TURN appears to forward SRTP-shaped ChannelData on a fast path and
|
||||
// drop anomalous payloads. AEAD ciphertext + 16B tag is plausible as
|
||||
// AES-GCM SRTP per RFC 7714.
|
||||
|
||||
const (
|
||||
wrapKeyLen = 32
|
||||
wrapNonceLen = 12
|
||||
wrapTagLen = 16
|
||||
wrapPrefixMin = 1
|
||||
wrapPrefixMax = 8
|
||||
wrapKeyLen = 32
|
||||
wrapRTPHdrLen = 12
|
||||
wrapNonceLen = 12
|
||||
wrapTagLen = 16
|
||||
wrapHeaderLen = wrapRTPHdrLen + wrapNonceLen // 24
|
||||
wrapOverhead = wrapHeaderLen + wrapTagLen // 40
|
||||
wrapRTPVersion = 0x80 // V=2, P=0, X=0, CC=0
|
||||
wrapRTPPT = 0x6F // M=0, PT=111 (opus)
|
||||
wrapTSStep = 960 // 20ms @ 48kHz
|
||||
)
|
||||
|
||||
type wrapConn struct {
|
||||
aead cipher.AEAD
|
||||
sessionID [4]byte
|
||||
sessionID [4]byte // 4B prefix for nonce; MSB encodes direction
|
||||
ssrc [4]byte // SSRC for RTP header; MSB encodes direction
|
||||
counter atomic.Uint64
|
||||
seq atomic.Uint32 // RTP sequence (used as uint16)
|
||||
timestamp atomic.Uint32 // RTP timestamp
|
||||
}
|
||||
|
||||
func newWrapConn(key []byte, isServer bool) (*wrapConn, error) {
|
||||
@@ -54,14 +64,23 @@ func newWrapConn(key []byte, isServer bool) (*wrapConn, error) {
|
||||
return nil, fmt.Errorf("wrap: aead init: %w", err)
|
||||
}
|
||||
w := &wrapConn{aead: aead}
|
||||
if _, err := rand.Read(w.sessionID[:]); err != nil {
|
||||
return nil, fmt.Errorf("wrap: sessionID rand: %w", err)
|
||||
|
||||
var rnd [16]byte
|
||||
if _, err := rand.Read(rnd[:]); err != nil {
|
||||
return nil, fmt.Errorf("wrap: rand init: %w", err)
|
||||
}
|
||||
copy(w.sessionID[:], rnd[0:4])
|
||||
copy(w.ssrc[:], rnd[4:8])
|
||||
if isServer {
|
||||
w.sessionID[0] |= 0x80
|
||||
w.ssrc[0] |= 0x80
|
||||
} else {
|
||||
w.sessionID[0] &^= 0x80
|
||||
w.ssrc[0] &^= 0x80
|
||||
}
|
||||
w.seq.Store(uint32(binary.BigEndian.Uint16(rnd[8:10])))
|
||||
w.timestamp.Store(binary.BigEndian.Uint32(rnd[10:14]))
|
||||
|
||||
var cb [8]byte
|
||||
if _, err := rand.Read(cb[:]); err != nil {
|
||||
return nil, fmt.Errorf("wrap: counter rand: %w", err)
|
||||
@@ -72,56 +91,48 @@ func newWrapConn(key []byte, isServer bool) (*wrapConn, error) {
|
||||
|
||||
// wrapMaxWire returns max wire bytes for a given payload size.
|
||||
func wrapMaxWire(payloadLen int) int {
|
||||
return wrapPrefixMax + wrapNonceLen + payloadLen + wrapTagLen
|
||||
return wrapOverhead + payloadLen
|
||||
}
|
||||
|
||||
func (w *wrapConn) wrapInto(dst, payload []byte) (int, error) {
|
||||
// Header + prefix length.
|
||||
var hdr [1]byte
|
||||
if _, err := rand.Read(hdr[:]); err != nil {
|
||||
return 0, fmt.Errorf("wrap: prefix rand: %w", err)
|
||||
}
|
||||
prefixLen := wrapPrefixMin + int(hdr[0]&0x07) // 1..8
|
||||
|
||||
wireLen := prefixLen + wrapNonceLen + len(payload) + wrapTagLen
|
||||
wireLen := wrapOverhead + len(payload)
|
||||
if len(dst) < wireLen {
|
||||
return 0, errors.New("wrap: dst buffer too small")
|
||||
}
|
||||
|
||||
dst[0] = hdr[0]
|
||||
if prefixLen > 1 {
|
||||
if _, err := rand.Read(dst[1:prefixLen]); err != nil {
|
||||
return 0, fmt.Errorf("wrap: prefix rand: %w", err)
|
||||
}
|
||||
}
|
||||
// RTP header.
|
||||
dst[0] = wrapRTPVersion
|
||||
dst[1] = wrapRTPPT
|
||||
seq := uint16(w.seq.Add(1) - 1)
|
||||
binary.BigEndian.PutUint16(dst[2:4], seq)
|
||||
ts := w.timestamp.Add(wrapTSStep) - wrapTSStep
|
||||
binary.BigEndian.PutUint32(dst[4:8], ts)
|
||||
copy(dst[8:12], w.ssrc[:])
|
||||
|
||||
// Nonce: 4B sessionID || 8B counter.
|
||||
noncePos := prefixLen
|
||||
// Explicit nonce.
|
||||
noncePos := wrapRTPHdrLen
|
||||
copy(dst[noncePos:noncePos+4], w.sessionID[:])
|
||||
ctr := w.counter.Add(1) - 1
|
||||
binary.BigEndian.PutUint64(dst[noncePos+4:noncePos+wrapNonceLen], ctr)
|
||||
|
||||
nonce := dst[noncePos : noncePos+wrapNonceLen]
|
||||
ctPos := noncePos + wrapNonceLen
|
||||
// In-place: write plaintext at ctPos, then Seal over it.
|
||||
aad := dst[:wrapHeaderLen]
|
||||
ctPos := wrapHeaderLen
|
||||
copy(dst[ctPos:], payload)
|
||||
w.aead.Seal(dst[ctPos:ctPos], nonce, dst[ctPos:ctPos+len(payload)], nonce)
|
||||
w.aead.Seal(dst[ctPos:ctPos], nonce, dst[ctPos:ctPos+len(payload)], aad)
|
||||
|
||||
return wireLen, nil
|
||||
}
|
||||
|
||||
func (w *wrapConn) unwrapPacket(wire, dst []byte) (int, error) {
|
||||
if len(wire) < wrapPrefixMin+wrapNonceLen+wrapTagLen {
|
||||
if len(wire) < wrapOverhead {
|
||||
return 0, errors.New("wrap: packet too short")
|
||||
}
|
||||
prefixLen := wrapPrefixMin + int(wire[0]&0x07)
|
||||
if len(wire) < prefixLen+wrapNonceLen+wrapTagLen {
|
||||
return 0, errors.New("wrap: packet too short for prefix")
|
||||
}
|
||||
nonce := wire[prefixLen : prefixLen+wrapNonceLen]
|
||||
ct := wire[prefixLen+wrapNonceLen:]
|
||||
nonce := wire[wrapRTPHdrLen : wrapRTPHdrLen+wrapNonceLen]
|
||||
aad := wire[:wrapHeaderLen]
|
||||
ct := wire[wrapHeaderLen:]
|
||||
|
||||
plain, err := w.aead.Open(ct[:0], nonce, ct, nonce)
|
||||
plain, err := w.aead.Open(ct[:0], nonce, ct, aad)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("wrap: AEAD open: %w", err)
|
||||
}
|
||||
|
||||
+54
-21
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -19,7 +20,6 @@ func TestWrapConnRoundTrip(t *testing.T) {
|
||||
t.Fatalf("newWrapConn(server): %v", err)
|
||||
}
|
||||
|
||||
// Client → Server
|
||||
wire := make([]byte, wrapMaxWire(len(payload)))
|
||||
n, err := client.wrapInto(wire, payload)
|
||||
if err != nil {
|
||||
@@ -27,6 +27,12 @@ func TestWrapConnRoundTrip(t *testing.T) {
|
||||
}
|
||||
wire = wire[:n]
|
||||
|
||||
if wire[0] != wrapRTPVersion {
|
||||
t.Fatalf("RTP byte0 = 0x%02X, want 0x%02X", wire[0], wrapRTPVersion)
|
||||
}
|
||||
if wire[1] != wrapRTPPT {
|
||||
t.Fatalf("RTP byte1 (PT) = 0x%02X, want 0x%02X", wire[1], wrapRTPPT)
|
||||
}
|
||||
if bytes.Contains(wire, payload) {
|
||||
t.Fatalf("wrapped packet contains plaintext payload")
|
||||
}
|
||||
@@ -58,7 +64,7 @@ func TestWrapConnRoundTrip(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrapPrefixLenInRange(t *testing.T) {
|
||||
func TestWrapRTPHeaderProgression(t *testing.T) {
|
||||
key := bytes.Repeat([]byte{0x42}, wrapKeyLen)
|
||||
wc, err := newWrapConn(key, false)
|
||||
if err != nil {
|
||||
@@ -66,21 +72,35 @@ func TestWrapPrefixLenInRange(t *testing.T) {
|
||||
}
|
||||
payload := []byte("x")
|
||||
|
||||
// Run many trials; every prefix_len must fall in [1..8].
|
||||
for range 200 {
|
||||
wire := make([]byte, wrapMaxWire(len(payload)))
|
||||
n, err := wc.wrapInto(wire, payload)
|
||||
if err != nil {
|
||||
t.Fatalf("wrapInto: %v", err)
|
||||
}
|
||||
prefixLen := wrapPrefixMin + int(wire[0]&0x07)
|
||||
if prefixLen < 1 || prefixLen > 8 {
|
||||
t.Fatalf("prefix_len out of range: %d", prefixLen)
|
||||
}
|
||||
expected := prefixLen + wrapNonceLen + len(payload) + wrapTagLen
|
||||
if n != expected {
|
||||
t.Fatalf("wire size mismatch: got %d want %d (prefix_len=%d)", n, expected, prefixLen)
|
||||
}
|
||||
wire1 := make([]byte, wrapMaxWire(len(payload)))
|
||||
n1, err := wc.wrapInto(wire1, payload)
|
||||
if err != nil {
|
||||
t.Fatalf("wrapInto 1: %v", err)
|
||||
}
|
||||
wire2 := make([]byte, wrapMaxWire(len(payload)))
|
||||
n2, err := wc.wrapInto(wire2, payload)
|
||||
if err != nil {
|
||||
t.Fatalf("wrapInto 2: %v", err)
|
||||
}
|
||||
if n1 != n2 {
|
||||
t.Fatalf("wire size variance: %d vs %d", n1, n2)
|
||||
}
|
||||
|
||||
seq1 := binary.BigEndian.Uint16(wire1[2:4])
|
||||
seq2 := binary.BigEndian.Uint16(wire2[2:4])
|
||||
if seq2 != seq1+1 {
|
||||
t.Fatalf("seq did not increment: %d → %d", seq1, seq2)
|
||||
}
|
||||
|
||||
ts1 := binary.BigEndian.Uint32(wire1[4:8])
|
||||
ts2 := binary.BigEndian.Uint32(wire2[4:8])
|
||||
if ts2-ts1 != wrapTSStep {
|
||||
t.Fatalf("timestamp step = %d, want %d", ts2-ts1, wrapTSStep)
|
||||
}
|
||||
|
||||
// SSRC stable across packets.
|
||||
if !bytes.Equal(wire1[8:12], wire2[8:12]) {
|
||||
t.Fatalf("SSRC changed between packets")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +121,12 @@ func TestWrapDirectionBit(t *testing.T) {
|
||||
if server.sessionID[0]&0x80 == 0 {
|
||||
t.Fatalf("server sessionID MSB should be 1, got 0x%02X", server.sessionID[0])
|
||||
}
|
||||
if client.ssrc[0]&0x80 != 0 {
|
||||
t.Fatalf("client SSRC MSB should be 0, got 0x%02X", client.ssrc[0])
|
||||
}
|
||||
if server.ssrc[0]&0x80 == 0 {
|
||||
t.Fatalf("server SSRC MSB should be 1, got 0x%02X", server.ssrc[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeWrapKeyRequiresValidKeyWhenEnabled(t *testing.T) {
|
||||
@@ -157,12 +183,19 @@ func TestUnwrapRejectsTamperedPacket(t *testing.T) {
|
||||
}
|
||||
wire = wire[:n]
|
||||
|
||||
// Flip a bit in the ciphertext (past prefix+nonce).
|
||||
prefixLen := wrapPrefixMin + int(wire[0]&0x07)
|
||||
wire[prefixLen+wrapNonceLen+1] ^= 0xFF
|
||||
// Flip a bit in the ciphertext.
|
||||
wire[wrapHeaderLen+1] ^= 0xFF
|
||||
|
||||
dst := make([]byte, 1600)
|
||||
if _, err := server.unwrapPacket(wire, dst); err == nil {
|
||||
t.Fatalf("unwrapPacket accepted tampered packet")
|
||||
t.Fatalf("unwrapPacket accepted tampered ciphertext")
|
||||
}
|
||||
|
||||
// Re-wrap and tamper RTP header (AAD).
|
||||
n2, _ := client.wrapInto(wire, payload)
|
||||
wire = wire[:n2]
|
||||
wire[8] ^= 0x01 // flip a bit in SSRC
|
||||
if _, err := server.unwrapPacket(wire, dst); err == nil {
|
||||
t.Fatalf("unwrapPacket accepted tampered AAD")
|
||||
}
|
||||
}
|
||||
|
||||
+43
-34
@@ -19,20 +19,24 @@ import (
|
||||
)
|
||||
|
||||
// Wire format is identical to client — see client/wrap.go. Server sets the
|
||||
// MSB of sessionID; client clears it. Counter init is random per process.
|
||||
// MSB of sessionID/SSRC; client clears it. RTP header fields are per-conn.
|
||||
|
||||
const (
|
||||
wrapKeyLen = 32
|
||||
wrapNonceLen = 12
|
||||
wrapTagLen = 16
|
||||
wrapPrefixMin = 1
|
||||
wrapPrefixMax = 8
|
||||
wrapKeyLen = 32
|
||||
wrapRTPHdrLen = 12
|
||||
wrapNonceLen = 12
|
||||
wrapTagLen = 16
|
||||
wrapHeaderLen = wrapRTPHdrLen + wrapNonceLen
|
||||
wrapOverhead = wrapHeaderLen + wrapTagLen
|
||||
wrapRTPVersion = 0x80
|
||||
wrapRTPPT = 0x6F
|
||||
wrapTSStep = 960
|
||||
)
|
||||
|
||||
// bufPool eliminates per-packet heap allocation on the hot read/write paths.
|
||||
var bufPool = sync.Pool{
|
||||
New: func() any {
|
||||
b := make([]byte, 1600+wrapPrefixMax+wrapNonceLen+wrapTagLen)
|
||||
b := make([]byte, 1600+wrapOverhead)
|
||||
return &b
|
||||
},
|
||||
}
|
||||
@@ -81,11 +85,18 @@ func (l *wrapPacketListener) Accept() (net.PacketConn, net.Addr, error) {
|
||||
return pc, addr, err
|
||||
}
|
||||
c := &wrapPacketConn{inner: pc, ws: l.ws}
|
||||
// Per-connection sessionID (server MSB=1) + random counter init.
|
||||
if _, err := rand.Read(c.sessionID[:]); err != nil {
|
||||
return nil, addr, fmt.Errorf("wrap: sessionID rand: %w", err)
|
||||
|
||||
var rnd [16]byte
|
||||
if _, err := rand.Read(rnd[:]); err != nil {
|
||||
return nil, addr, fmt.Errorf("wrap: rand init: %w", err)
|
||||
}
|
||||
copy(c.sessionID[:], rnd[0:4])
|
||||
copy(c.ssrc[:], rnd[4:8])
|
||||
c.sessionID[0] |= 0x80
|
||||
c.ssrc[0] |= 0x80
|
||||
c.seq.Store(uint32(binary.BigEndian.Uint16(rnd[8:10])))
|
||||
c.timestamp.Store(binary.BigEndian.Uint32(rnd[10:14]))
|
||||
|
||||
var cb [8]byte
|
||||
if _, err := rand.Read(cb[:]); err != nil {
|
||||
return nil, addr, fmt.Errorf("wrap: counter rand: %w", err)
|
||||
@@ -103,13 +114,16 @@ type wrapPacketConn struct {
|
||||
inner net.PacketConn
|
||||
ws *wrapState
|
||||
sessionID [4]byte
|
||||
ssrc [4]byte
|
||||
counter atomic.Uint64
|
||||
seq atomic.Uint32
|
||||
timestamp atomic.Uint32
|
||||
}
|
||||
|
||||
func (c *wrapPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
||||
bp := bufPool.Get().(*[]byte) //nolint:errcheck // pool New always returns *[]byte
|
||||
buf := *bp
|
||||
need := len(p) + wrapPrefixMax + wrapNonceLen + wrapTagLen
|
||||
need := len(p) + wrapOverhead
|
||||
if cap(buf) < need {
|
||||
buf = make([]byte, need)
|
||||
*bp = buf
|
||||
@@ -121,17 +135,14 @@ func (c *wrapPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
||||
return 0, addr, err
|
||||
}
|
||||
wire := buf[:n]
|
||||
if len(wire) < wrapPrefixMin+wrapNonceLen+wrapTagLen {
|
||||
if len(wire) < wrapOverhead {
|
||||
return 0, addr, errors.New("wrap: packet too short")
|
||||
}
|
||||
prefixLen := wrapPrefixMin + int(wire[0]&0x07)
|
||||
if len(wire) < prefixLen+wrapNonceLen+wrapTagLen {
|
||||
return 0, addr, errors.New("wrap: packet too short for prefix")
|
||||
}
|
||||
nonce := wire[prefixLen : prefixLen+wrapNonceLen]
|
||||
ct := wire[prefixLen+wrapNonceLen:]
|
||||
nonce := wire[wrapRTPHdrLen : wrapRTPHdrLen+wrapNonceLen]
|
||||
aad := wire[:wrapHeaderLen]
|
||||
ct := wire[wrapHeaderLen:]
|
||||
|
||||
plain, err := c.ws.aead.Open(ct[:0], nonce, ct, nonce)
|
||||
plain, err := c.ws.aead.Open(ct[:0], nonce, ct, aad)
|
||||
if err != nil {
|
||||
return 0, addr, fmt.Errorf("wrap: AEAD open: %w", err)
|
||||
}
|
||||
@@ -143,12 +154,7 @@ func (c *wrapPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
||||
}
|
||||
|
||||
func (c *wrapPacketConn) WriteTo(p []byte, addr net.Addr) (int, error) {
|
||||
var hdr [1]byte
|
||||
if _, err := rand.Read(hdr[:]); err != nil {
|
||||
return 0, fmt.Errorf("wrap: prefix rand: %w", err)
|
||||
}
|
||||
prefixLen := wrapPrefixMin + int(hdr[0]&0x07)
|
||||
wireLen := prefixLen + wrapNonceLen + len(p) + wrapTagLen
|
||||
wireLen := wrapOverhead + len(p)
|
||||
|
||||
bp := bufPool.Get().(*[]byte) //nolint:errcheck // pool New always returns *[]byte
|
||||
out := *bp
|
||||
@@ -159,22 +165,25 @@ func (c *wrapPacketConn) WriteTo(p []byte, addr net.Addr) (int, error) {
|
||||
out = out[:wireLen]
|
||||
defer bufPool.Put(bp)
|
||||
|
||||
out[0] = hdr[0]
|
||||
if prefixLen > 1 {
|
||||
if _, err := rand.Read(out[1:prefixLen]); err != nil {
|
||||
return 0, fmt.Errorf("wrap: prefix rand: %w", err)
|
||||
}
|
||||
}
|
||||
// RTP header.
|
||||
out[0] = wrapRTPVersion
|
||||
out[1] = wrapRTPPT
|
||||
seq := uint16(c.seq.Add(1) - 1)
|
||||
binary.BigEndian.PutUint16(out[2:4], seq)
|
||||
ts := c.timestamp.Add(wrapTSStep) - wrapTSStep
|
||||
binary.BigEndian.PutUint32(out[4:8], ts)
|
||||
copy(out[8:12], c.ssrc[:])
|
||||
|
||||
noncePos := prefixLen
|
||||
noncePos := wrapRTPHdrLen
|
||||
copy(out[noncePos:noncePos+4], c.sessionID[:])
|
||||
ctr := c.counter.Add(1) - 1
|
||||
binary.BigEndian.PutUint64(out[noncePos+4:noncePos+wrapNonceLen], ctr)
|
||||
|
||||
nonce := out[noncePos : noncePos+wrapNonceLen]
|
||||
ctPos := noncePos + wrapNonceLen
|
||||
aad := out[:wrapHeaderLen]
|
||||
ctPos := wrapHeaderLen
|
||||
copy(out[ctPos:], p)
|
||||
c.ws.aead.Seal(out[ctPos:ctPos], nonce, out[ctPos:ctPos+len(p)], nonce)
|
||||
c.ws.aead.Seal(out[ctPos:ctPos], nonce, out[ctPos:ctPos+len(p)], aad)
|
||||
|
||||
if _, err := c.inner.WriteTo(out, addr); err != nil {
|
||||
return 0, err
|
||||
|
||||
Reference in New Issue
Block a user