Ground-Zerro / Phobos Public
Code Issues Pull requests Actions Releases View on GitHub ↗
2.3 KB go
/* SPDX-License-Identifier: MIT
 *
 * Phobos
 */

// Package cobf binds the shared wg-obfuscator C sources in
// src/phobos-obfuscator. It is the only implementation of the Phobos wire
// protocol in this tree; there is deliberately no Go port to drift from.
package cobf

/*
#cgo CFLAGS: -I${SRCDIR}/../../../src/phobos-obfuscator
#cgo windows LDFLAGS: -lws2_32
#include "bridge.h"
*/
import "C"

import "unsafe"

func keyPtr(key []byte) *C.char {
	return (*C.char)(unsafe.Pointer(unsafe.SliceData(key)))
}

// Encode obfuscates buffer[:length] in place and returns the new length,
// which may exceed length when the C side appends dummy padding.
func Encode(buffer []byte, length int, key []byte, maxDummyData, obfuscateBytes int) int {
	return int(C.cobf_encode((*C.uint8_t)(unsafe.SliceData(buffer)), C.int(length),
		keyPtr(key), C.int(len(key)), C.int(maxDummyData), C.int(obfuscateBytes)))
}

// Decode deobfuscates buffer[:length] in place and returns the payload length.
// version reports the protocol version detected in the packet header.
func Decode(buffer []byte, length int, key []byte, obfuscateBytes int) (n int, version uint8) {
	var v C.uint8_t
	n = int(C.cobf_decode((*C.uint8_t)(unsafe.SliceData(buffer)), C.int(length),
		keyPtr(key), C.int(len(key)), C.int(obfuscateBytes), &v))
	return n, uint8(v)
}

// XOR applies the keystream to buffer[:length] in place. Encode and Decode
// already do this; it is exported for the golden-vector regression tests.
func XOR(buffer []byte, length int, key []byte) {
	C.cobf_xor((*C.uint8_t)(unsafe.SliceData(buffer)), C.int(length), keyPtr(key), C.int(len(key)))
}

// StreamCipher is the continuous keystream used for SOCKS5 TCP streams. Its
// state lives in Go memory: the C struct holds no pointers, so it needs no
// manual lifecycle.
type StreamCipher struct {
	state []uint64
}

func NewStreamCipher(key []byte) *StreamCipher {
	words := (int(C.cobf_stream_size()) + 7) / 8
	s := &StreamCipher{state: make([]uint64, words)}
	C.cobf_stream_init(unsafe.Pointer(unsafe.SliceData(s.state)), keyPtr(key), C.int(len(key)))
	return s
}

func (s *StreamCipher) Apply(buffer []byte) {
	if len(buffer) == 0 {
		return
	}
	C.cobf_stream_apply(unsafe.Pointer(unsafe.SliceData(s.state)),
		(*C.uint8_t)(unsafe.SliceData(buffer)), C.int(len(buffer)))
}