Update v1.2.0
This commit is contained in:
@@ -27,6 +27,8 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"crypto/cipher"
|
||||
|
||||
"github.com/pion/dtls/v3"
|
||||
"github.com/pion/dtls/v3/pkg/crypto/selfsign"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
@@ -45,8 +47,6 @@ import (
|
||||
const (
|
||||
wgIfaceName = "wdtt0"
|
||||
wgServerAddr = "10.66.66.1"
|
||||
wgClientAddr = "10.66.66.2"
|
||||
wgClientCIDR = wgClientAddr + "/32"
|
||||
wgServerCIDR = wgServerAddr + "/24"
|
||||
defaultInternalWGPort = 56001
|
||||
dns = "1.1.1.1"
|
||||
@@ -64,23 +64,16 @@ type ClientDevice struct {
|
||||
}
|
||||
|
||||
type PasswordEntry struct {
|
||||
DeviceID string `json:"device_id"` // пусто = ещё не привязан
|
||||
ExpiresAt int64 `json:"expires_at"` // unix timestamp
|
||||
DownBytes int64 `json:"down_bytes"` // скачано клиентом
|
||||
UpBytes int64 `json:"up_bytes"` // отдано клиентом
|
||||
DeviceID string `json:"device_id"` // пусто = ещё не привязан
|
||||
ExpiresAt int64 `json:"expires_at"` // unix timestamp
|
||||
DownBytes int64 `json:"down_bytes"` // скачано клиентом
|
||||
UpBytes int64 `json:"up_bytes"` // отдано клиентом
|
||||
VkHash string `json:"vk_hash,omitempty"`
|
||||
Ports string `json:"ports,omitempty"` // "dtls,wg,tun"
|
||||
IsDeactivated bool `json:"is_deactivated,omitempty"`
|
||||
}
|
||||
|
||||
// Трафик главного пароля (владельца)
|
||||
var (
|
||||
mainPassDown int64
|
||||
mainPassUp int64
|
||||
)
|
||||
|
||||
// Онлайн-статус устройств
|
||||
var (
|
||||
activeDevices = make(map[string]int32) // deviceID -> кол-во активных коннектов
|
||||
activeDevicesMu sync.Mutex
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
MainPassword string `json:"main_password"`
|
||||
@@ -120,6 +113,37 @@ func generatePassword() string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
var publicIP string = ""
|
||||
|
||||
func getPublicIP() string {
|
||||
if publicIP != "" {
|
||||
return publicIP
|
||||
}
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
resp, err := client.Get("https://api.ipify.org")
|
||||
if err != nil {
|
||||
return "YOUR_SERVER_IP"
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
ipBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "YOUR_SERVER_IP"
|
||||
}
|
||||
publicIP = string(bytes.TrimSpace(ipBytes))
|
||||
return publicIP
|
||||
}
|
||||
|
||||
func stripVkUrl(url string) string {
|
||||
url = strings.TrimSpace(url)
|
||||
if idx := strings.LastIndex(url, "/"); idx != -1 {
|
||||
url = url[idx+1:]
|
||||
}
|
||||
if idx := strings.Index(url, "?"); idx != -1 {
|
||||
url = url[:idx]
|
||||
}
|
||||
return strings.TrimSpace(url)
|
||||
}
|
||||
|
||||
type wrapKeyEntry struct {
|
||||
id string
|
||||
key []byte
|
||||
@@ -340,7 +364,7 @@ func botLoop(token string, adminIDstr string, wgDev *device.Device) {
|
||||
|
||||
// Устанавливаем команды для синей кнопки Menu
|
||||
go func() {
|
||||
cmds := `{"commands":[{"command":"new","description":"Создать временный пароль"},{"command":"list","description":"Управление доступами"}]}`
|
||||
cmds := `{"commands":[{"command":"start","description":"Главное меню"},{"command":"new","description":"Создать временный пароль"},{"command":"list","description":"Управление доступами"}]}`
|
||||
resp, err := http.Post(fmt.Sprintf("https://api.telegram.org/bot%s/setMyCommands", token), "application/json", strings.NewReader(cmds))
|
||||
if err == nil {
|
||||
resp.Body.Close()
|
||||
@@ -350,8 +374,14 @@ func botLoop(token string, adminIDstr string, wgDev *device.Device) {
|
||||
offset := 0
|
||||
client := &http.Client{Timeout: 65 * time.Second}
|
||||
|
||||
// Состояние ожидания ввода дней
|
||||
// Состояние ожидания ввода
|
||||
var waitingForDays bool
|
||||
var waitingForPorts bool
|
||||
var waitingForHash bool
|
||||
var targetPassword string
|
||||
|
||||
var tempDays int
|
||||
var tempPorts string // "dtls,wg,tun"
|
||||
|
||||
for {
|
||||
url := fmt.Sprintf("https://api.telegram.org/bot%s/getUpdates?timeout=60&offset=%d", token, offset)
|
||||
@@ -410,6 +440,22 @@ func botLoop(token string, adminIDstr string, wgDev *device.Device) {
|
||||
continue
|
||||
}
|
||||
txt := fmt.Sprintf("🔑 *Пароль:* `%s`\n", pass)
|
||||
if entry.VkHash != "" {
|
||||
ports := entry.Ports
|
||||
if ports == "" {
|
||||
ports = "56000,56001,9000"
|
||||
}
|
||||
pts := strings.Split(ports, ",")
|
||||
srvIP := getPublicIP()
|
||||
link := fmt.Sprintf("wdtt://%s:%s:%s:%s:%s:%s", srvIP, pts[0], pts[1], pts[2], pass, entry.VkHash)
|
||||
txt += fmt.Sprintf("🔗 *Быстрая ссылка:* `%s`\n", link)
|
||||
}
|
||||
if entry.IsDeactivated {
|
||||
txt += "🔴 Статус: *ДЕАКТИВИРОВАН*\n"
|
||||
} else {
|
||||
txt += "🟢 Статус: *АКТИВЕН*\n"
|
||||
}
|
||||
|
||||
if entry.ExpiresAt > 0 {
|
||||
expireTime := time.Unix(entry.ExpiresAt, 0)
|
||||
remaining := time.Until(expireTime)
|
||||
@@ -421,6 +467,8 @@ func botLoop(token string, adminIDstr string, wgDev *device.Device) {
|
||||
} else {
|
||||
txt += "⏰ Бессрочный ♾\n"
|
||||
}
|
||||
|
||||
txt += fmt.Sprintf("\n📊 *Трафик:*\n• Скачано: %.2f MB\n• Отдано: %.2f MB\n", float64(entry.DownBytes)/(1024*1024), float64(entry.UpBytes)/(1024*1024))
|
||||
txt += "\n📱 *Привязанное устройство:*\n"
|
||||
var kb []map[string]interface{}
|
||||
if entry.DeviceID == "" {
|
||||
@@ -438,6 +486,17 @@ func botLoop(token string, adminIDstr string, wgDev *device.Device) {
|
||||
})
|
||||
}
|
||||
dbMutex.Unlock()
|
||||
if entry.IsDeactivated {
|
||||
kb = append(kb, map[string]interface{}{
|
||||
"text": "✅ Активировать",
|
||||
"callback_data": "react_" + pass,
|
||||
})
|
||||
} else {
|
||||
kb = append(kb, map[string]interface{}{
|
||||
"text": "⏸ Деактивировать",
|
||||
"callback_data": "deact_" + pass,
|
||||
})
|
||||
}
|
||||
kb = append(kb, map[string]interface{}{
|
||||
"text": "❌ Удалить пароль",
|
||||
"callback_data": "delpass_" + pass,
|
||||
@@ -452,6 +511,44 @@ func botLoop(token string, adminIDstr string, wgDev *device.Device) {
|
||||
}
|
||||
sendTelegram(token, adminID, txt, map[string]interface{}{"inline_keyboard": keyboard})
|
||||
|
||||
} else if strings.HasPrefix(data, "deact_") {
|
||||
pass := strings.TrimPrefix(data, "deact_")
|
||||
dbMutex.Lock()
|
||||
entry, exists := db.Passwords[pass]
|
||||
if exists && entry != nil {
|
||||
entry.IsDeactivated = true
|
||||
// Отключаем активное устройство от WG если нужно
|
||||
if entry.DeviceID != "" {
|
||||
if dev, devExists := db.Devices[entry.DeviceID]; devExists {
|
||||
pubHex, _ := b64ToHex(dev.PubKey)
|
||||
wgDev.IpcSet(fmt.Sprintf("public_key=%s\nremove=true\n", pubHex))
|
||||
}
|
||||
}
|
||||
saveDB()
|
||||
}
|
||||
dbMutex.Unlock()
|
||||
sendTelegram(token, adminID, fmt.Sprintf("⏸ Пароль `%s` деактивирован", pass), nil)
|
||||
|
||||
} else if strings.HasPrefix(data, "react_") {
|
||||
pass := strings.TrimPrefix(data, "react_")
|
||||
dbMutex.Lock()
|
||||
entry, exists := db.Passwords[pass]
|
||||
if exists && entry != nil {
|
||||
entry.IsDeactivated = false
|
||||
saveDB()
|
||||
}
|
||||
dbMutex.Unlock()
|
||||
sendTelegram(token, adminID, fmt.Sprintf("✅ Пароль `%s` активирован", pass), nil)
|
||||
|
||||
} else if data == "mainlink" {
|
||||
targetPassword = "main"
|
||||
var keyboard [][]map[string]interface{}
|
||||
keyboard = append(keyboard, []map[string]interface{}{
|
||||
{"text": "Да", "callback_data": "ports_def"},
|
||||
{"text": "Нет", "callback_data": "ports_custom"},
|
||||
})
|
||||
sendTelegram(token, adminID, "⚙️ Использовать стандартные порты для главного пароля (56000, 56001, 9000)?", map[string]interface{}{"inline_keyboard": keyboard})
|
||||
|
||||
} else if strings.HasPrefix(data, "unbind_") {
|
||||
pass := strings.TrimPrefix(data, "unbind_")
|
||||
dbMutex.Lock()
|
||||
@@ -509,6 +606,13 @@ func botLoop(token string, adminIDstr string, wgDev *device.Device) {
|
||||
|
||||
} else if data == "backlist" {
|
||||
sendPasswordList(token, adminID, wgDev)
|
||||
} else if data == "ports_def" {
|
||||
tempPorts = "56000,56001,9000"
|
||||
waitingForHash = true
|
||||
sendTelegram(token, adminID, "🔑 Укажите VK хеш (или несколько через запятую):", nil)
|
||||
} else if data == "ports_custom" {
|
||||
waitingForPorts = true
|
||||
sendTelegram(token, adminID, "⚙️ Укажите через запятую 3 порта (DTLS,WG,TUN):\nНапример: 56000,56001,9000", nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,7 +632,68 @@ func botLoop(token string, adminIDstr string, wgDev *device.Device) {
|
||||
sendTelegram(token, adminID, "❌ Неверное значение. Укажите число от 1 до 365, или отправьте /new заново.", nil)
|
||||
continue
|
||||
}
|
||||
expiresAt := time.Now().Add(time.Duration(days) * 24 * time.Hour).Unix()
|
||||
tempDays = days
|
||||
|
||||
var keyboard [][]map[string]interface{}
|
||||
keyboard = append(keyboard, []map[string]interface{}{
|
||||
{"text": "Да", "callback_data": "ports_def"},
|
||||
{"text": "Нет", "callback_data": "ports_custom"},
|
||||
})
|
||||
sendTelegram(token, adminID, "⚙️ Использовать стандартные порты (56000, 56001, 9000)?", map[string]interface{}{"inline_keyboard": keyboard})
|
||||
continue
|
||||
}
|
||||
|
||||
if waitingForPorts {
|
||||
parts := strings.Split(cmd, ",")
|
||||
if len(parts) != 3 {
|
||||
sendTelegram(token, adminID, "❌ Неверный формат. Укажите 3 порта через запятую (например: 56000,56001,9000):", nil)
|
||||
continue
|
||||
}
|
||||
p1 := strings.TrimSpace(parts[0])
|
||||
p2 := strings.TrimSpace(parts[1])
|
||||
p3 := strings.TrimSpace(parts[2])
|
||||
|
||||
if _, err := strconv.Atoi(p1); err != nil {
|
||||
sendTelegram(token, adminID, "❌ Неверный порт. Повторите ввод:", nil)
|
||||
continue
|
||||
}
|
||||
if _, err := strconv.Atoi(p2); err != nil {
|
||||
sendTelegram(token, adminID, "❌ Неверный порт. Повторите ввод:", nil)
|
||||
continue
|
||||
}
|
||||
if _, err := strconv.Atoi(p3); err != nil {
|
||||
sendTelegram(token, adminID, "❌ Неверный порт. Повторите ввод:", nil)
|
||||
continue
|
||||
}
|
||||
|
||||
waitingForPorts = false
|
||||
tempPorts = fmt.Sprintf("%s,%s,%s", p1, p2, p3)
|
||||
waitingForHash = true
|
||||
sendTelegram(token, adminID, "🔑 Укажите VK хеш (или несколько через запятую):", nil)
|
||||
continue
|
||||
}
|
||||
|
||||
if waitingForHash {
|
||||
hash := strings.ReplaceAll(cmd, " ", "")
|
||||
if strings.Contains(hash, "http") || strings.Contains(hash, "/") {
|
||||
sendTelegram(token, adminID, "❌ Пожалуйста, отправьте только хеш (или несколько хешей через запятую). Ссылки не поддерживаются.", nil)
|
||||
continue
|
||||
}
|
||||
if hash == "" {
|
||||
sendTelegram(token, adminID, "❌ Хеш не должен быть пустым.", nil)
|
||||
continue
|
||||
}
|
||||
waitingForHash = false
|
||||
|
||||
if targetPassword == "main" {
|
||||
targetPassword = ""
|
||||
srvIP := getPublicIP()
|
||||
pts := strings.Split(tempPorts, ",")
|
||||
link := fmt.Sprintf("wdtt://%s:%s:%s:%s:%s:%s", srvIP, pts[0], pts[1], pts[2], db.MainPassword, hash)
|
||||
sendTelegram(token, adminID, fmt.Sprintf("🔗 *Ссылка для главного пароля:*\n`%s`", link), nil)
|
||||
continue
|
||||
}
|
||||
|
||||
dbMutex.Lock()
|
||||
if cleanupExpiredPasswordsLocked(wgDev) > 0 {
|
||||
saveDB()
|
||||
@@ -556,11 +721,21 @@ func botLoop(token string, adminIDstr string, wgDev *device.Device) {
|
||||
sendTelegram(token, adminID, "❌ Не удалось создать WRAP-ключ для пароля. Повторите /new.", nil)
|
||||
continue
|
||||
}
|
||||
db.Passwords[newPass] = &PasswordEntry{ExpiresAt: expiresAt}
|
||||
expiresAt := time.Now().Add(time.Duration(tempDays) * 24 * time.Hour).Unix()
|
||||
db.Passwords[newPass] = &PasswordEntry{
|
||||
ExpiresAt: expiresAt,
|
||||
VkHash: hash,
|
||||
Ports: tempPorts,
|
||||
}
|
||||
saveDB()
|
||||
dbMutex.Unlock()
|
||||
|
||||
expDate := time.Unix(expiresAt, 0).Format("02.01.2006")
|
||||
sendTelegram(token, adminID, fmt.Sprintf("🔑 Новый пароль:\n`%s`\n\n⏰ Действует %d дн. (до %s)\n📱 Ожидает первого подключения", newPass, days, expDate), nil)
|
||||
srvIP := getPublicIP()
|
||||
pts := strings.Split(tempPorts, ",")
|
||||
link := fmt.Sprintf("wdtt://%s:%s:%s:%s:%s:%s", srvIP, pts[0], pts[1], pts[2], newPass, hash)
|
||||
|
||||
sendTelegram(token, adminID, fmt.Sprintf("🔑 Новый пароль:\n`%s`\n\n⏰ Действует %d дн. (до %s)\n📱 Ожидает первого подключения\n\n🔗 *Быстрая ссылка:* `%s`", newPass, tempDays, expDate, link), nil)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -677,6 +852,10 @@ func sendPasswordList(token string, adminID int64, wgDev *device.Device) {
|
||||
txt += fmt.Sprintf("🔒 Главный: `%s` (владелец)\n\n", db.MainPassword)
|
||||
|
||||
var inlineKb []map[string]interface{}
|
||||
inlineKb = append(inlineKb, map[string]interface{}{
|
||||
"text": "🔗 Ссылка на главный пароль",
|
||||
"callback_data": "mainlink",
|
||||
})
|
||||
|
||||
if len(db.Passwords) == 0 {
|
||||
txt += "_Нет сгенерированных паролей._\n"
|
||||
@@ -1224,7 +1403,6 @@ func main() {
|
||||
func handleConn(ctx context.Context, clientConn net.Conn, wgEndpoint string, wgDev *device.Device, keys *wgKeys) {
|
||||
atomic.AddInt64(&totalConns, 1)
|
||||
|
||||
var connDeviceID string
|
||||
var connPassword string
|
||||
var connIsMainPass bool
|
||||
|
||||
@@ -1276,14 +1454,16 @@ func handleConn(ctx context.Context, clientConn net.Conn, wgEndpoint string, wgD
|
||||
entry, isGenPass := db.Passwords[password]
|
||||
valid := isMainPass || (isGenPass && !isPasswordExpired(entry))
|
||||
|
||||
// Для сгенерированных паролей — проверяем привязку к устройству
|
||||
if valid && isGenPass && entry.DeviceID != "" && entry.DeviceID != deviceID {
|
||||
if valid && isGenPass && entry.IsDeactivated {
|
||||
clientConn.Write([]byte("DENIED:deactivated"))
|
||||
log.Printf("[WG] Отказ: пароль %s деактивирован, запрос от %s", maskPassword(password), deviceID)
|
||||
dbMutex.Unlock()
|
||||
} else if valid && isGenPass && entry.DeviceID != "" && entry.DeviceID != deviceID {
|
||||
// Пароль уже привязан к другому устройству
|
||||
clientConn.Write([]byte("DENIED:device_mismatch"))
|
||||
log.Printf("[WG] Отказ: пароль %s привязан к %s, запрос от %s", maskPassword(password), entry.DeviceID, deviceID)
|
||||
dbMutex.Unlock()
|
||||
} else if valid {
|
||||
connDeviceID = deviceID
|
||||
connPassword = password
|
||||
connIsMainPass = isMainPass
|
||||
|
||||
@@ -1364,20 +1544,7 @@ func handleConn(ctx context.Context, clientConn net.Conn, wgEndpoint string, wgD
|
||||
}
|
||||
atomic.AddInt64(&totalBytesFromClient, int64(len(firstPacket)))
|
||||
|
||||
// Трекинг онлайн-статуса
|
||||
if connDeviceID != "" {
|
||||
activeDevicesMu.Lock()
|
||||
activeDevices[connDeviceID]++
|
||||
activeDevicesMu.Unlock()
|
||||
defer func() {
|
||||
activeDevicesMu.Lock()
|
||||
activeDevices[connDeviceID]--
|
||||
if activeDevices[connDeviceID] <= 0 {
|
||||
delete(activeDevices, connDeviceID)
|
||||
}
|
||||
activeDevicesMu.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
pctx, pcancel := context.WithCancel(ctx)
|
||||
defer pcancel()
|
||||
@@ -1413,9 +1580,7 @@ func handleConn(ctx context.Context, clientConn net.Conn, wgEndpoint string, wgD
|
||||
}
|
||||
atomic.AddInt64(&totalBytesFromClient, int64(nn))
|
||||
// Per-password upload tracking
|
||||
if connIsMainPass {
|
||||
atomic.AddInt64(&mainPassUp, int64(nn))
|
||||
} else if connPassword != "" {
|
||||
if connPassword != "" && !connIsMainPass {
|
||||
dbMutex.Lock()
|
||||
e, ok := db.Passwords[connPassword]
|
||||
if !ok || e == nil || isPasswordExpired(e) {
|
||||
@@ -1456,9 +1621,7 @@ func handleConn(ctx context.Context, clientConn net.Conn, wgEndpoint string, wgD
|
||||
}
|
||||
atomic.AddInt64(&totalBytesToClient, int64(nn))
|
||||
// Per-password download tracking
|
||||
if connIsMainPass {
|
||||
atomic.AddInt64(&mainPassDown, int64(nn))
|
||||
} else if connPassword != "" {
|
||||
if connPassword != "" && !connIsMainPass {
|
||||
dbMutex.Lock()
|
||||
e, ok := db.Passwords[connPassword]
|
||||
if !ok || e == nil || isPasswordExpired(e) {
|
||||
@@ -1482,6 +1645,24 @@ const (
|
||||
wrapKeyLen = 32
|
||||
)
|
||||
|
||||
var aeadCache sync.Map
|
||||
|
||||
func getAEAD(key []byte) (cipher.AEAD, error) {
|
||||
if len(key) != wrapKeyLen {
|
||||
return nil, fmt.Errorf("obfs: key must be %d bytes", wrapKeyLen)
|
||||
}
|
||||
keyStr := string(key)
|
||||
if val, ok := aeadCache.Load(keyStr); ok {
|
||||
return val.(cipher.AEAD), nil
|
||||
}
|
||||
aead, err := chacha20poly1305.New(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aeadCache.Store(keyStr, aead)
|
||||
return aead, nil
|
||||
}
|
||||
|
||||
// ==================== RTP Обфускация ====================
|
||||
|
||||
type ObfsConfig struct {
|
||||
@@ -1491,9 +1672,10 @@ type ObfsConfig struct {
|
||||
}
|
||||
|
||||
type ObfsState struct {
|
||||
mu sync.Mutex
|
||||
seq uint16
|
||||
ts uint32
|
||||
mu sync.Mutex
|
||||
initSeq uint16
|
||||
initTs uint32
|
||||
count uint64
|
||||
}
|
||||
|
||||
func NewObfsConfig() *ObfsConfig {
|
||||
@@ -1510,8 +1692,9 @@ func NewObfsState() *ObfsState {
|
||||
var buf [6]byte
|
||||
rand.Read(buf[:])
|
||||
return &ObfsState{
|
||||
seq: binary.BigEndian.Uint16(buf[0:2]),
|
||||
ts: binary.BigEndian.Uint32(buf[2:6]),
|
||||
initSeq: binary.BigEndian.Uint16(buf[0:2]),
|
||||
initTs: binary.BigEndian.Uint32(buf[2:6]),
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1531,12 +1714,13 @@ func obfsWrapPacket(key, payload []byte, cfg *ObfsConfig, state *ObfsState) ([]b
|
||||
return nil, errors.New("obfs: empty payload")
|
||||
}
|
||||
state.mu.Lock()
|
||||
seq := state.seq
|
||||
ts := state.ts
|
||||
state.seq++
|
||||
state.ts += 960
|
||||
c := state.count
|
||||
state.count++
|
||||
state.mu.Unlock()
|
||||
|
||||
seq := state.initSeq + uint16(c)
|
||||
ts := state.initTs + uint32(c)*960 + uint32(c>>16)
|
||||
|
||||
nonce := obfsBuildNonce(cfg.SSRC, seq, ts)
|
||||
padRand := 0
|
||||
if cfg.PaddingMax > 0 {
|
||||
@@ -1554,7 +1738,7 @@ func obfsWrapPacket(key, payload []byte, cfg *ObfsConfig, state *ObfsState) ([]b
|
||||
binary.BigEndian.PutUint32(out[4:8], ts)
|
||||
binary.BigEndian.PutUint32(out[8:12], cfg.SSRC)
|
||||
|
||||
aead, err := chacha20poly1305.New(key)
|
||||
aead, err := getAEAD(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("obfs: cipher init: %w", err)
|
||||
}
|
||||
@@ -1597,7 +1781,7 @@ func obfsUnwrapPacket(key, wire, dst []byte) (int, error) {
|
||||
return 0, errors.New("obfs: dst buffer too small")
|
||||
}
|
||||
nonce := obfsBuildNonce(ssrc, seq, ts)
|
||||
aead, err := chacha20poly1305.New(key)
|
||||
aead, err := getAEAD(key)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("obfs: cipher init: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user