ios: PCA Phobos VPN for iPhone (source)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
130
ios/Tunnel/obfuscator/obfuscation.c
Normal file
130
ios/Tunnel/obfuscator/obfuscation.c
Normal file
@@ -0,0 +1,130 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "wg-obfuscator.h"
|
||||
#include "obfuscation.h"
|
||||
|
||||
static uint8_t crc_a[256]; // step(crc, 0): contribution of the current CRC state
|
||||
static uint8_t crc_b[256]; // step(0, inbyte): contribution of the input byte
|
||||
static uint8_t tables_ready = 0;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *data; // cached keystream bytes, NULL until first allocation
|
||||
int valid; // number of valid bytes in data
|
||||
int cap; // allocated capacity of data
|
||||
uint8_t crc; // CRC state right after the 'valid' bytes
|
||||
} keystream_row_t;
|
||||
|
||||
static keystream_row_t rows[256];
|
||||
|
||||
// Single step of the original bit-by-bit CRC8 update, kept only to build the tables.
|
||||
static uint8_t crc8_step(uint8_t crc, uint8_t inbyte) {
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
uint8_t mix = (crc ^ inbyte) & 0x01;
|
||||
crc >>= 1;
|
||||
if (mix) {
|
||||
crc ^= 0x8C;
|
||||
}
|
||||
inbyte >>= 1;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static void ensure_tables(void) {
|
||||
if (tables_ready) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 256; i++) {
|
||||
crc_a[i] = crc8_step((uint8_t)i, 0);
|
||||
crc_b[i] = crc8_step(0, (uint8_t)i);
|
||||
}
|
||||
tables_ready = 1;
|
||||
}
|
||||
|
||||
// Ensure the given row holds at least 'need' valid keystream bytes (capped at
|
||||
// KEYSTREAM_ROW_MAX). On allocation failure the row is left as large as it could
|
||||
// grow; the caller then computes the missing tail on the fly.
|
||||
static void grow_row(keystream_row_t *row, int cls, int need, const char *key, int key_length) {
|
||||
if (need > KEYSTREAM_ROW_MAX) {
|
||||
need = KEYSTREAM_ROW_MAX;
|
||||
}
|
||||
if (row->valid >= need) {
|
||||
return;
|
||||
}
|
||||
if (row->cap < need) {
|
||||
int newcap = (need + 255) & ~255; // round up to a 256-byte chunk
|
||||
if (newcap > KEYSTREAM_ROW_MAX) {
|
||||
newcap = KEYSTREAM_ROW_MAX;
|
||||
}
|
||||
uint8_t *p = realloc(row->data, newcap);
|
||||
if (!p) {
|
||||
return; // keep whatever we already have; tail handled by caller
|
||||
}
|
||||
row->data = p;
|
||||
row->cap = newcap;
|
||||
}
|
||||
|
||||
uint8_t crc = row->crc;
|
||||
int ki = row->valid % key_length;
|
||||
for (int i = row->valid; i < need; i++) {
|
||||
uint8_t inbyte = (uint8_t)(key[ki] + cls + key_length);
|
||||
crc = crc_a[crc] ^ crc_b[inbyte];
|
||||
row->data[i] = crc;
|
||||
if (++ki == key_length) {
|
||||
ki = 0;
|
||||
}
|
||||
}
|
||||
row->valid = need;
|
||||
row->crc = crc;
|
||||
}
|
||||
|
||||
// XOR 'n' keystream bytes into dst, 8 bytes at a time. memcpy avoids both
|
||||
// strict-aliasing violations and unaligned-access assumptions.
|
||||
static void xor_block(uint8_t *dst, const uint8_t *ks, int n) {
|
||||
int i = 0;
|
||||
for (; i + 8 <= n; i += 8) {
|
||||
uint64_t a, b;
|
||||
memcpy(&a, dst + i, 8);
|
||||
memcpy(&b, ks + i, 8);
|
||||
a ^= b;
|
||||
memcpy(dst + i, &a, 8);
|
||||
}
|
||||
for (; i < n; i++) {
|
||||
dst[i] ^= ks[i];
|
||||
}
|
||||
}
|
||||
|
||||
void xor_data(uint8_t *buffer, int length, char *key, int key_length) {
|
||||
if (length <= 0) {
|
||||
return;
|
||||
}
|
||||
ensure_tables();
|
||||
|
||||
int cls = length & 0xFF;
|
||||
keystream_row_t *row = &rows[cls];
|
||||
|
||||
int cached = (length <= KEYSTREAM_ROW_MAX) ? length : KEYSTREAM_ROW_MAX;
|
||||
if (row->valid < cached) {
|
||||
grow_row(row, cls, cached, key, key_length);
|
||||
}
|
||||
|
||||
int n = (length < row->valid) ? length : row->valid;
|
||||
if (n > 0) {
|
||||
xor_block(buffer, row->data, n);
|
||||
}
|
||||
|
||||
// Tail beyond the cache limit (or beyond what we could allocate): compute on
|
||||
// the fly, continuing the CRC chain from the last cached state.
|
||||
if (n < length) {
|
||||
uint8_t crc = row->crc;
|
||||
int ki = n % key_length;
|
||||
for (int i = n; i < length; i++) {
|
||||
uint8_t inbyte = (uint8_t)(key[ki] + cls + key_length);
|
||||
crc = crc_a[crc] ^ crc_b[inbyte];
|
||||
buffer[i] ^= crc;
|
||||
if (++ki == key_length) {
|
||||
ki = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user