ios: PCA Phobos VPN for iPhone (source)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Бобырев Андрей
2026-10-23 03:37:06 +03:00
parent dff6af2c8e
commit 071c1dc7f1
63 changed files with 7726 additions and 0 deletions

View 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;
}
}
}
}