From 8ea6415424f1ab46215a1125d4429ec126e298c4 Mon Sep 17 00:00:00 2001 From: lwvmobile Date: Wed, 15 Oct 2025 17:57:15 -0400 Subject: [PATCH] DMR: Baofeng Advanced (PC5) Encryption Support; #320 Above Commit is by Anonymous Submission; --- .../cygwin_bat/complete_usage_options.txt | 13 +- include/dsd.h | 4 + include/pc5.h | 147 ++++++ src/crypt-pc5.c | 468 ++++++++++++++++++ src/dsd_main.c | 13 +- src/dsd_mbe.c | 29 ++ 6 files changed, 669 insertions(+), 5 deletions(-) create mode 100644 include/pc5.h create mode 100644 src/crypt-pc5.c diff --git a/examples/cygwin_bat/complete_usage_options.txt b/examples/cygwin_bat/complete_usage_options.txt index 1525892..36fffcb 100644 --- a/examples/cygwin_bat/complete_usage_options.txt +++ b/examples/cygwin_bat/complete_usage_options.txt @@ -1,4 +1,4 @@ -Build Version: AW 2025-197-gf3b89f1 +Build Version: AW 2025-202-geb24239 MBElib Version: 1.3.4 CODEC2 Support Enabled @@ -177,17 +177,22 @@ Advanced Decoder options: -! Manually Enter and Enforce TYT Advanced Privacy (PC4) 128 or 256 bit AP Hex Key (see example below) Encapulate in Single Quotation Marks; Space every 16 chars. -! '736B9A9C5645288B 243AD5CB8701EF8A' - -! '1111111111111111 1111111111111111 1111111111111111 1111111111111111' - + -! '1111111111111111 1111111111111111 1111111111111111 1111111111111111' + -@ Manually Enter and Enforce Retevis Advanced Privacy (RC2) 128 or 256 bit AP Hex Key (see example below) Encapulate in Single Quotation Marks; Space every 16 chars. -@ '736B9A9C5645288B 243AD5CB8701EF8A' - -@ '1122334455667788 99AABBCCDDEEFF11 1122334455667788 99AABBCCDDEEFF11' + -@ '1122334455667788 99AABBCCDDEEFF11 1122334455667788 99AABBCCDDEEFF11' -5 Manually Enter and Enforce TYT Enhanced Privacy (AES-128) EP Hex Key (see example below) Encapulate in Single Quotation Marks; Space every 16 chars. -5 '736B9A9C5645288B 243AD5CB8701EF8A' + -+ Manually Enter and Enforce Baofeng Advanced Privacy (PC5-128 or PC5-256) AP Hex Key (see example below) + Encapulate in Single Quotation Marks; Space every 16 chars. + -+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC' + -+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC EF01EF01EF01EF01 EF01EF01EF01EF01' + -9 Manually Enter and Enforce Kenwood 15-bit Scrambler Key Value (DMR) (Dec Value) -A Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value) diff --git a/include/dsd.h b/include/dsd.h index df19705..7fd0d1b 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -1002,6 +1002,9 @@ typedef struct //anytone bp int any_bp; + //baofeng ap + int baofeng_ap; + //generic ks int straight_ks; int straight_mod; @@ -1644,6 +1647,7 @@ void tyt_ap_pc4_keystream_creation(dsd_state * state, char * input); void retevis_rc2_keystream_creation(dsd_state *state, char *input); void ken_dmr_scrambler_keystream_creation(dsd_state * state, char * input); void anytone_bp_keystream_creation(dsd_state * state, char * input); +void baofeng_ap_pc5_keystream_creation(dsd_state *state, char *input); void straight_mod_xor_keystream_creation(dsd_state * state, char * input); //Kirisun diff --git a/include/pc5.h b/include/pc5.h new file mode 100644 index 0000000..22cb011 --- /dev/null +++ b/include/pc5.h @@ -0,0 +1,147 @@ +#include +#include + +#ifndef BAOFENG_AP +#define BAOFENG_AP + +#define nbround 254 +#define n1 264 + +#define UNUSEDPC5(x) ((void)x) + +/* Structure holding all PC5 state variables */ +typedef struct { + short bits[49], temp[49]; + uint8_t ptconvert; + uint8_t convert[7]; + uint8_t perm[16][256]; + uint8_t new1[256]; + uint8_t array[49]; + uint8_t array2[49]; + uint8_t decal[nbround]; + uint8_t rngxor[nbround][3]; + uint8_t rngxor2[nbround][3]; + uint8_t rounds; + uint8_t tab[256]; + uint8_t inv[256]; + uint8_t permut[3][3]; + uint64_t bb; + uint64_t x; + uint8_t tot[3]; + uint8_t l[2][3], r[2][3]; + uint8_t y, totb; + uint32_t result; + uint8_t xyz, count; + uint8_t keys[16]; + unsigned char array_arc4[256]; + int i_arc4, j_arc4; + int x1, x2, i; + unsigned char h2[n1]; + unsigned char h1[n1*3]; + uint8_t numbers[25]; +} PC5Context; + +/* Global context accessible from all .c files */ +extern PC5Context ctxpc5; + +/* Public API */ +void create_keys_pc5(PC5Context *ctxpc5, unsigned char key1[], size_t size1); +void pc5encrypt(PC5Context *ctxpc5); +void pc5decrypt(PC5Context *ctxpc5); +void binhexpc5(PC5Context *ctxpc5, short *z, int length); +void hexbinpc5(PC5Context *ctxpc5, short *q, uint8_t w, uint8_t hex); +static void u64_to_bytes_be_pc5(uint64_t val, unsigned char *out); + +/* Encrypt a 49-bit frame using PC5 algorithm (original flow) */ +static void encrypt_frame_49_pc5(short frame_bits_in[49]) { + // Only process first 24 bits for encryption, rest are XORed + ctxpc5.ptconvert = 0; + binhexpc5(&ctxpc5, frame_bits_in, 24); + + uint8_t convert[6]; + + // Store first 3 bytes in convert + for (int i = 0; i < 3; i++) convert[i] = ctxpc5.convert[i]; + + // Split each byte into two 4-bit nibbles + ctxpc5.convert[0] = convert[0] >> 4; + ctxpc5.convert[1] = convert[0] & 0xF; + ctxpc5.convert[2] = convert[1] >> 4; + ctxpc5.convert[3] = convert[1] & 0xF; + ctxpc5.convert[4] = convert[2] >> 4; + ctxpc5.convert[5] = convert[2] & 0xF; + + pc5encrypt(&ctxpc5); + + // Store encrypted nibbles back in convert + for (int i = 0; i < 6; i++) convert[i] = ctxpc5.convert[i]; + + // Recombine nibbles into bytes + ctxpc5.convert[0] = (convert[0] << 4) + convert[1]; + ctxpc5.convert[1] = (convert[2] << 4) + convert[3]; + ctxpc5.convert[2] = (convert[4] << 4) + convert[5]; + + // Convert back to bits for first 24 bits + for (int q = 0; q < 3; q++) { + uint8_t w = (uint8_t)(q * 8); + hexbinpc5(&ctxpc5, frame_bits_in, w, ctxpc5.convert[q]); + } + + // XOR the remaining bits (positions 24 to 48) + for (int i = 24; i < 49; i++) { + frame_bits_in[i] = (short)(frame_bits_in[i] ^ ctxpc5.numbers[i - 24]); + } + + for (int i = 0; i < 49; i++) ctxpc5.bits[i] = frame_bits_in[i]; +} + +/* Decrypt a 49-bit frame using PC5 algorithm (original flow) */ +static void decrypt_frame_49_pc5(short frame_bits_in[49]) { + // XOR the remaining bits (positions 24 to 48) first + for (int i = 24; i < 49; i++) { + frame_bits_in[i] = (short)(frame_bits_in[i] ^ ctxpc5.numbers[i - 24]); + } + + ctxpc5.ptconvert = 0; + binhexpc5(&ctxpc5, frame_bits_in, 24); + + uint8_t convert[6]; + + // Store first 3 bytes in convert + for (int i = 0; i < 3; i++) convert[i] = ctxpc5.convert[i]; + + // Split each byte into two 4-bit nibbles + ctxpc5.convert[0] = convert[0] >> 4; + ctxpc5.convert[1] = convert[0] & 0xF; + ctxpc5.convert[2] = convert[1] >> 4; + ctxpc5.convert[3] = convert[1] & 0xF; + ctxpc5.convert[4] = convert[2] >> 4; + ctxpc5.convert[5] = convert[2] & 0xF; + + pc5decrypt(&ctxpc5); + + // Store decrypted nibbles back in convert + for (int i = 0; i < 6; i++) convert[i] = ctxpc5.convert[i]; + + // Recombine nibbles into bytes + ctxpc5.convert[0] = (convert[0] << 4) + convert[1]; + ctxpc5.convert[1] = (convert[2] << 4) + convert[3]; + ctxpc5.convert[2] = (convert[4] << 4) + convert[5]; + + // Convert back to bits for first 24 bits + for (int q = 0; q < 3; q++) { + uint8_t w = (uint8_t)(q * 8); + hexbinpc5(&ctxpc5, frame_bits_in, w, ctxpc5.convert[q]); + } + + for (int i = 0; i < 49; i++) ctxpc5.bits[i] = frame_bits_in[i]; +} + +/* Convert 64-bit integer to bytes (big-endian) */ +static void u64_to_bytes_be_pc5(uint64_t val, unsigned char *out) { + for (int i = 0; i < 8; i++) { + out[i] = (unsigned char)((val >> (56 - 8 * i)) & 0xFF); + } +} + +#endif diff --git a/src/crypt-pc5.c b/src/crypt-pc5.c new file mode 100644 index 0000000..49f786c --- /dev/null +++ b/src/crypt-pc5.c @@ -0,0 +1,468 @@ +#include "pc5.h" +#include "dsd.h" + +#include + +/* Global PC5 context instance */ +PC5Context ctxpc5; + +/* --------------------------------- + Internal utility functions +----------------------------------- */ + +/* Rotate right */ +static uint32_t ror(uint32_t x, int shift, int bits) { + uint32_t m0 = (1u << (bits - shift)) - 1u; + uint32_t m1 = (1u << shift) - 1u; + return ((x >> shift) & m0) | ((x & m1) << (bits - shift)); +} + +/* Rotate left */ +static uint32_t rol(uint32_t x, int shift, int bits) { + uint32_t m0 = (1u << (bits - shift)) - 1u; + uint32_t m1 = (1u << shift) - 1u; + return ((x & m0) << shift) | ((x >> (bits - shift)) & m1); +} + +/* SplitMix64 random number generator */ +static uint64_t next_rng(PC5Context *ctxpc5) { + uint64_t z = (ctxpc5->x += 0x9e3779b97f4a7c15ULL); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; + z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; + return z ^ (z >> 31); +} + +/* ARC4 initialization */ +static void arc4_init(PC5Context *ctxpc5, unsigned char key[]) { + int tmp; + for (ctxpc5->i_arc4 = 0; ctxpc5->i_arc4 < 256; ctxpc5->i_arc4++) + ctxpc5->array_arc4[ctxpc5->i_arc4] = (unsigned char)ctxpc5->i_arc4; + + ctxpc5->j_arc4 = 0; + for (ctxpc5->i_arc4 = 0; ctxpc5->i_arc4 < 256; ctxpc5->i_arc4++) { + ctxpc5->j_arc4 = (ctxpc5->j_arc4 + ctxpc5->array_arc4[ctxpc5->i_arc4] + key[ctxpc5->i_arc4 % 256]) % 256; + tmp = ctxpc5->array_arc4[ctxpc5->i_arc4]; + ctxpc5->array_arc4[ctxpc5->i_arc4] = ctxpc5->array_arc4[ctxpc5->j_arc4]; + ctxpc5->array_arc4[ctxpc5->j_arc4] = tmp; + } + ctxpc5->i_arc4 = 0; + ctxpc5->j_arc4 = 0; +} + +/* ARC4 output combined with SplitMix64 stream */ +static unsigned char arc4_output(PC5Context *ctxpc5) { + uint8_t rndbyte, decal; + int tmp, t; + + ctxpc5->i_arc4 = (ctxpc5->i_arc4 + 1) % 256; + ctxpc5->j_arc4 = (ctxpc5->j_arc4 + ctxpc5->array_arc4[ctxpc5->i_arc4]) % 256; + tmp = ctxpc5->array_arc4[ctxpc5->i_arc4]; + ctxpc5->array_arc4[ctxpc5->i_arc4] = ctxpc5->array_arc4[ctxpc5->j_arc4]; + ctxpc5->array_arc4[ctxpc5->j_arc4] = tmp; + t = (ctxpc5->array_arc4[ctxpc5->i_arc4] + ctxpc5->array_arc4[ctxpc5->j_arc4]) % 256; + + if (ctxpc5->xyz == 0) ctxpc5->bb = next_rng(ctxpc5); + decal = (uint8_t)(56 - (8 * ctxpc5->xyz)); + rndbyte = (uint8_t)((ctxpc5->bb >> decal) & 0xffu); + ctxpc5->xyz++; + if (ctxpc5->xyz == 8) ctxpc5->xyz = 0; + + if (ctxpc5->count == 0) { + rndbyte = (uint8_t)(rndbyte ^ ctxpc5->array_arc4[t]); + ctxpc5->count = 1; + } else { + rndbyte = (uint8_t)(rndbyte + ctxpc5->array_arc4[t]); + ctxpc5->count = 0; + } + return rndbyte; +} + +/* Initialize MD2-II state */ +static void md2_init(PC5Context *ctxpc5) { + ctxpc5->x1 = 0; + ctxpc5->x2 = 0; + for (ctxpc5->i = 0; ctxpc5->i < n1; ctxpc5->i++) ctxpc5->h2[ctxpc5->i] = 0; + for (ctxpc5->i = 0; ctxpc5->i < n1; ctxpc5->i++) ctxpc5->h1[ctxpc5->i] = 0; +} + +/* MD2-II hashing */ +static void md2_hashing(PC5Context *ctxpc5, unsigned char t1[], size_t b6) { + static const unsigned char s4[256] = { + 13,199,11,67,237,193,164,77,115,184,141,222,73,38,147,36,150,87,21,104,12,61,156,101,111,145, + 119,22,207,35,198,37,171,167,80,30,219,28,213,121,86,29,214,242,6,4,89,162,110,175,19,157,3, + 88,234,94,144,118,159,239,100,17,182,173,238,68,16,79,132,54,163,52,9,58,57,55,229,192,170,226, + 56,231,187,158,70,224,233,245,26,47,32,44,247,8,251,20,197,185,109,153,204,218,93,178,212,137,84, + 174,24,120,130,149,72,180,181,208,255,189,152,18,143,176,60,249,27,227,128,139,243,253,59,123,172, + 108,211,96,138,10,215,42,225,40,81,65,90,25,98,126,154,64,124,116,122,5,1,168,83,190,131,191,244, + 240,235,177,155,228,125,66,43,201,248,220,129,188,230,62,75,71,78,34,31,216,254,136,91,114,106,46, + 217,196,92,151,209,133,51,236,33,252,127,179,69,7,183,105,146,97,39,15,205,112,200,166,223,45,48, + 246,186,41,148,140,107,76,85,95,194,142,50,49,134,23,135,169,221,210,203,63,165,82,161,202,53,14, + 206,232,103,102,195,117,250,99,0,74,160,241,2,113}; + + int b1, b2, b3, b4, b5; + b4 = 0; + while (b6) { + for (; b6 && ctxpc5->x2 < n1; b6--, ctxpc5->x2++) { + b5 = t1[b4++]; + ctxpc5->h1[ctxpc5->x2 + n1] = (unsigned char)b5; + ctxpc5->h1[ctxpc5->x2 + (n1 * 2)] = (unsigned char)(b5 ^ ctxpc5->h1[ctxpc5->x2]); + ctxpc5->x1 = ctxpc5->h2[ctxpc5->x2] ^= s4[b5 ^ ctxpc5->x1]; + } + if (ctxpc5->x2 == n1) { + b2 = 0; + ctxpc5->x2 = 0; + for (b3 = 0; b3 < (n1 + 2); b3++) { + for (b1 = 0; b1 < (n1 * 3); b1++) + b2 = ctxpc5->h1[b1] ^= s4[b2]; + b2 = (b2 + b3) % 256; + } + } + } +} + +/* Finalize MD2-II */ +static void md2_end(PC5Context *ctxpc5, unsigned char h4[n1]) { + unsigned char h3[n1]; + int i, n4 = n1 - ctxpc5->x2; + for (i = 0; i < n4; i++) h3[i] = (unsigned char)n4; + md2_hashing(ctxpc5, h3, (size_t)n4); + md2_hashing(ctxpc5, ctxpc5->h2, sizeof(ctxpc5->h2)); + for (i = 0; i < n1; i++) h4[i] = ctxpc5->h1[i]; +} + +/* Generate a random index */ +static int mixy(PC5Context *ctxpc5, int nn2) { + return arc4_output(ctxpc5) % nn2; +} + +/* Fisher-Yates shuffle */ +static void mixer(PC5Context *ctxpc5, uint8_t *mixu, int nn) { + int ii, jj, tmmp; + for (ii = nn - 1; ii > 0; ii--) { + jj = mixy(ctxpc5, ii + 1); + tmmp = mixu[jj]; + mixu[jj] = mixu[ii]; + mixu[ii] = (uint8_t)tmmp; + } +} + +/* Key schedule and S-box generation */ +void create_keys_pc5(PC5Context *ctxpc5, unsigned char key1[], size_t size1) { + int i, w, k; + unsigned char h4[n1]; + + md2_init(ctxpc5); + md2_hashing(ctxpc5, key1, size1); + md2_end(ctxpc5, h4); + + for (i = 0; i < 16; i++) ctxpc5->keys[i] = h4[i]; + arc4_init(ctxpc5, h4); + + ctxpc5->x = 0; + for (i = 0; i < 8; i++) ctxpc5->x = (ctxpc5->x << 8) + (uint64_t)(h4[256 + i] & 0xffu); + + ctxpc5->xyz = 0; + ctxpc5->count = 0; + + for (i = 0; i < 23000; i++) (void)arc4_output(ctxpc5); + + uint8_t numbers[256]; + + for (w = 0; w < 253; w++) { + k = arc4_output(ctxpc5) + 256; + for (i = 0; i < k; i++) (void)arc4_output(ctxpc5); + for (i = 0; i < 16; i++) numbers[i] = (uint8_t)i; + mixer(ctxpc5, numbers, 16); + for (i = 0; i < 16; i++) ctxpc5->perm[i][w] = numbers[i]; + } + + k = arc4_output(ctxpc5) + 256; + for (i = 0; i < k; i++) (void)arc4_output(ctxpc5); + for (i = 0; i < 16; i++) numbers[i] = (uint8_t)i; + mixer(ctxpc5, numbers, 16); + for (i = 0; i < 16; i++) ctxpc5->new1[i] = numbers[i]; + + k = arc4_output(ctxpc5) + 256; + for (i = 0; i < k; i++) (void)arc4_output(ctxpc5); + for (i = 0; i < nbround; i++) ctxpc5->decal[i] = (uint8_t)((arc4_output(ctxpc5) % 11) + 1); + + k = arc4_output(ctxpc5) + 256; + for (i = 0; i < k; i++) (void)arc4_output(ctxpc5); + for (w = 0; w < 3; w++) + for (i = 0; i < nbround; i++) + ctxpc5->rngxor[i][w] = arc4_output(ctxpc5) % 16; + + k = arc4_output(ctxpc5) + 256; + for (i = 0; i < k; i++) (void)arc4_output(ctxpc5); + for (i = 0; i < 16; i++) numbers[i] = (uint8_t)i; + mixer(ctxpc5, numbers, 16); + for (i = 0; i < 16; i++) { + ctxpc5->tab[i] = numbers[i]; + ctxpc5->inv[ctxpc5->tab[i]] = (unsigned char)i; + } + + k = arc4_output(ctxpc5) + 256; + for (i = 0; i < k; i++) (void)arc4_output(ctxpc5); + for (w = 0; w < 3; w++) { + k = arc4_output(ctxpc5) + 256; + for (i = 0; i < k; i++) (void)arc4_output(ctxpc5); + for (i = 0; i < 3; i++) numbers[i] = (uint8_t)i; + mixer(ctxpc5, numbers, 3); + for (i = 0; i < 3; i++) ctxpc5->permut[w][i] = numbers[i]; + } + + + + k = arc4_output(ctxpc5) + 256; + for (i = 0; i < k; i++) (void)arc4_output(ctxpc5); + for (w = 0; w < 3; w++) + for (i = 0; i < nbround; i++) + ctxpc5->rngxor2[i][w] = arc4_output(ctxpc5) % 16; + + k = arc4_output(ctxpc5) + 256; + + for (w = 0; w < 25; w++) ctxpc5->numbers[w]=arc4_output(ctxpc5) % 2; + +} + +static void compute_pc5(PC5Context *ctxpc5, uint8_t *tab1, uint8_t round) { + ctxpc5->tot[0] = (uint8_t)((ctxpc5->perm[tab1[ctxpc5->permut[0][0]]][round] + + ctxpc5->perm[tab1[ctxpc5->permut[0][1]]][round]) ^ + ctxpc5->perm[tab1[ctxpc5->permut[0][2]]][round]); + ctxpc5->tot[0] = (uint8_t)((ctxpc5->tot[0] + ctxpc5->new1[ctxpc5->tot[0]]) % 16); + ctxpc5->tot[1] = (uint8_t)((ctxpc5->perm[tab1[ctxpc5->permut[1][0]]][round] + + ctxpc5->perm[tab1[ctxpc5->permut[1][1]]][round]) ^ + ctxpc5->perm[tab1[ctxpc5->permut[1][2]]][round]); + ctxpc5->tot[1] = (uint8_t)((ctxpc5->tot[1] + ctxpc5->new1[ctxpc5->tot[1]]) % 16); + ctxpc5->tot[2] = (uint8_t)((ctxpc5->perm[tab1[ctxpc5->permut[2][0]]][round] + + ctxpc5->perm[tab1[ctxpc5->permut[2][1]]][round]) ^ + ctxpc5->perm[tab1[ctxpc5->permut[2][2]]][round]); + ctxpc5->tot[2] = (uint8_t)((ctxpc5->tot[2] + ctxpc5->new1[ctxpc5->tot[2]]) % 16); +} + +/* Convert bits to bytes */ +void binhexpc5(PC5Context *ctxpc5, short *z, int length) { + short *b = (short *)z; + uint8_t i, j; + for (i = 0; i < length; i = j) { + uint8_t a = 0; + for (j = i; j < i + 8; ++j) { + a |= (uint8_t)(b[((short)(7 - (j % 8)) + j) - (j % 8)] << (j - i)); + } + ctxpc5->convert[ctxpc5->ptconvert] = a; + ctxpc5->ptconvert++; + } +} + +/* Convert byte to bits */ +void hexbinpc5(PC5Context *ctxpc5, short *q, uint8_t w, uint8_t hex) { // warning: unused parameter ‘ctxpc5’ [-Wunused-parameter] + UNUSED(ctxpc5); + short *bits = (short *)q; + for (uint8_t i = 0; i < 8; ++i) { + bits[(short)(7 + w) - i] = (short)((hex >> i) & 1u); + } + + // UNUSEDPC4(ctxpc5); //fix above warning +} + +/* Encrypt one block */ +void pc5encrypt(PC5Context *ctxpc5) { + int i; + int g; + + for (i = 0; i < 3; i++) { + ctxpc5->l[0][i] = ctxpc5->convert[i]; + ctxpc5->r[0][i] = ctxpc5->convert[i + 3]; + } + + for (i = 1; i <= ctxpc5->rounds; i++) { + ctxpc5->r[(i - 1) % 2][0] = (ctxpc5->r[(i - 1) % 2][0] + ((uint8_t)~ctxpc5->rngxor2[ctxpc5->rounds - i][0])) % 16; + ctxpc5->r[(i - 1) % 2][1] = (ctxpc5->r[(i - 1) % 2][1] ^ ((uint8_t)~ctxpc5->rngxor2[ctxpc5->rounds - i][1])) % 16; + ctxpc5->r[(i - 1) % 2][2] = (ctxpc5->r[(i - 1) % 2][2] + ((uint8_t)~ctxpc5->rngxor2[ctxpc5->rounds - i][2])) % 16; + + ctxpc5->result = 0; + ctxpc5->result += ((uint32_t)ctxpc5->r[(i - 1) % 2][0] << 8); + ctxpc5->result += ((uint32_t)ctxpc5->r[(i - 1) % 2][1] << 4); + ctxpc5->result += ctxpc5->r[(i - 1) % 2][2]; + + ctxpc5->result = rol(ctxpc5->result, ctxpc5->decal[i - 1], 12); + + ctxpc5->r[(i - 1) % 2][0] = (uint8_t)(ctxpc5->result >> 8); + ctxpc5->r[(i - 1) % 2][1] = (uint8_t)((ctxpc5->result >> 4) & 0xfu); + ctxpc5->r[(i - 1) % 2][2] = (uint8_t)(ctxpc5->result & 0xfu); + + ctxpc5->r[(i - 1) % 2][0] = ctxpc5->tab[ctxpc5->r[(i - 1) % 2][0]]; + ctxpc5->r[(i - 1) % 2][0] = (ctxpc5->r[(i - 1) % 2][0] ^ ctxpc5->rngxor[i - 1][0]) % 16; + + ctxpc5->r[(i - 1) % 2][1] = ctxpc5->inv[ctxpc5->r[(i - 1) % 2][1]]; + + if ((ctxpc5->r[(i - 1) % 2][1] - ctxpc5->rngxor[i - 1][1]) < 0) { + g = (ctxpc5->r[(i - 1) % 2][1] - ctxpc5->rngxor[i - 1][1]) + 16; + } else { + g = (ctxpc5->r[(i - 1) % 2][1] - ctxpc5->rngxor[i - 1][1]) % 16; + } + ctxpc5->r[(i - 1) % 2][1] = g; + + ctxpc5->r[(i - 1) % 2][2] = ctxpc5->tab[ctxpc5->r[(i - 1) % 2][2]]; + ctxpc5->r[(i - 1) % 2][2] = (ctxpc5->r[(i - 1) % 2][2] ^ ctxpc5->rngxor[i - 1][2]) % 16; + + compute_pc5(ctxpc5, ctxpc5->r[(i - 1) % 2], (uint8_t)((i - 1) % 253)); + + ctxpc5->l[i % 2][0] = ctxpc5->r[(i - 1) % 2][0]; + + if ((ctxpc5->l[(i - 1) % 2][0] - ctxpc5->tot[0]) < 0) { + g = (ctxpc5->l[(i - 1) % 2][0] - ctxpc5->tot[0]) + 16; + } else { + g = ctxpc5->l[(i - 1) % 2][0] - ctxpc5->tot[0]; + } + ctxpc5->r[i % 2][0] = g; + + ctxpc5->l[i % 2][1] = ctxpc5->r[(i - 1) % 2][1]; + ctxpc5->r[i % 2][1] = (ctxpc5->l[(i - 1) % 2][1] ^ ctxpc5->tot[1]) % 16; + + ctxpc5->l[i % 2][2] = ctxpc5->r[(i - 1) % 2][2]; + + if ((ctxpc5->l[(i - 1) % 2][2] - ctxpc5->tot[2]) < 0) { + g = (ctxpc5->l[(i - 1) % 2][2] - ctxpc5->tot[2]) + 16; + } else { + g = ctxpc5->l[(i - 1) % 2][2] - ctxpc5->tot[2]; + } + ctxpc5->r[i % 2][2] = g; + } + + for (i = 0; i < 3; i++) { + ctxpc5->convert[i + 3] = ctxpc5->l[(ctxpc5->rounds - 1) % 2][i]; + ctxpc5->convert[i] = ctxpc5->r[(ctxpc5->rounds - 1) % 2][i]; + } +} + +/* Decrypt one block */ +void pc5decrypt(PC5Context *ctxpc5) { + int i; + + for (i = 0; i < 3; i++) { + ctxpc5->l[0][i] = ctxpc5->convert[i]; + ctxpc5->r[0][i] = ctxpc5->convert[i + 3]; + } + + ctxpc5->y = (uint8_t)((ctxpc5->rounds - 1) % 253); + if (ctxpc5->y == 0) ctxpc5->y = 253; + + for (i = 1; i <= ctxpc5->rounds; i++) { + ctxpc5->y--; + compute_pc5(ctxpc5, ctxpc5->r[(i - 1) % 2], ctxpc5->y); + if (ctxpc5->y == 0) ctxpc5->y = 253; + + ctxpc5->result = 0; + + ctxpc5->l[(i - 1) % 2][0] ^= ctxpc5->rngxor[ctxpc5->rounds - i][0]; + ctxpc5->l[(i - 1) % 2][0] %= 16; + ctxpc5->l[(i - 1) % 2][0] = ctxpc5->inv[ctxpc5->l[(i - 1) % 2][0]]; + + ctxpc5->l[(i - 1) % 2][1] += ctxpc5->rngxor[ctxpc5->rounds - i][1]; + ctxpc5->l[(i - 1) % 2][1] %= 16; + ctxpc5->l[(i - 1) % 2][1] = ctxpc5->tab[ctxpc5->l[(i - 1) % 2][1]]; + + ctxpc5->l[(i - 1) % 2][2] ^= ctxpc5->rngxor[ctxpc5->rounds - i][2]; + ctxpc5->l[(i - 1) % 2][2] %= 16; + ctxpc5->l[(i - 1) % 2][2] = ctxpc5->inv[ctxpc5->l[(i - 1) % 2][2]]; + + ctxpc5->result += ((uint32_t)ctxpc5->l[(i - 1) % 2][0] << 8); + ctxpc5->result += ((uint32_t)ctxpc5->l[(i - 1) % 2][1] << 4); + ctxpc5->result += ctxpc5->l[(i - 1) % 2][2]; + + ctxpc5->result = ror(ctxpc5->result, ctxpc5->decal[ctxpc5->rounds - i], 12); + + ctxpc5->l[(i - 1) % 2][0] = (uint8_t)(ctxpc5->result >> 8); + ctxpc5->l[(i - 1) % 2][1] = (uint8_t)((ctxpc5->result >> 4) & 0xfu); + ctxpc5->l[(i - 1) % 2][2] = (uint8_t)(ctxpc5->result & 0xfu); + + int temp; + + temp = ctxpc5->l[(i - 1) % 2][0] - ((uint8_t)(~ctxpc5->rngxor2[i - 1][0]) % 16); + if (temp < 0) temp += 16; + ctxpc5->l[(i - 1) % 2][0] = temp % 16; + + ctxpc5->l[(i - 1) % 2][1] ^= (uint8_t)(~ctxpc5->rngxor2[i - 1][1]) % 16; + ctxpc5->l[(i - 1) % 2][1] %= 16; + + temp = ctxpc5->l[(i - 1) % 2][2] - ((uint8_t)(~ctxpc5->rngxor2[i - 1][2]) % 16); + if (temp < 0) temp += 16; + ctxpc5->l[(i - 1) % 2][2] = temp % 16; + + ctxpc5->l[i % 2][0] = ctxpc5->r[(i - 1) % 2][0]; + ctxpc5->r[i % 2][0] = (ctxpc5->l[(i - 1) % 2][0] + ctxpc5->tot[0]) % 16; + + ctxpc5->l[i % 2][1] = ctxpc5->r[(i - 1) % 2][1]; + ctxpc5->r[i % 2][1] = (ctxpc5->l[(i - 1) % 2][1] ^ ctxpc5->tot[1]) % 16; + + ctxpc5->l[i % 2][2] = ctxpc5->r[(i - 1) % 2][2]; + ctxpc5->r[i % 2][2] = (ctxpc5->l[(i - 1) % 2][2] + ctxpc5->tot[2]) % 16; + } + + for (i = 0; i < 3; i++) { + ctxpc5->convert[i + 3] = ctxpc5->l[(ctxpc5->rounds - 1) % 2][i]; + ctxpc5->convert[i] = ctxpc5->r[(ctxpc5->rounds - 1) % 2][i]; + } +} + +/* Key creation for BAOFENG AP */ +void baofeng_ap_pc5_keystream_creation(dsd_state *state, char *input) { + + char buf[1024]; + strncpy(buf, input, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + + // Count hexadecimal characters (without spaces) + int hex_char_count = 0; + for (int i = 0; buf[i] != '\0'; i++) { + if (isxdigit(buf[i])) { + hex_char_count++; + } + } + + if (hex_char_count == 32) { // 128 bits (32 hex characters) + unsigned char key1[16] = {0}; + unsigned char key2[16] = {0}; + + char *pEnd; + uint64_t K1 = strtoull(buf, &pEnd, 16); + uint64_t K2 = strtoull(pEnd, &pEnd, 16); + + u64_to_bytes_be_pc5(K1, &key1[0]); + u64_to_bytes_be_pc5(K2, &key1[8]); + + // Reverse key for PC5 (only for 128-bit) + for (int i=0;i<16;i++) key2[i] = key1[15-i]; + + /* Create key schedule */ + create_keys_pc5(&ctxpc5, key2, sizeof(key2)); + ctxpc5.rounds = nbround; + + fprintf(stderr, "DMR BAOFENG AP (PC5) 128-bit Key %016llX%016llX with Forced Application\n", + (unsigned long long)K1, (unsigned long long)K2); + state->baofeng_ap = 1; + + } else if (hex_char_count == 64) { // 256 bits (64 hex characters) + // Remove spaces and store as ASCII string (64 bytes) + char ascii_key[65] = {0}; // 64 characters + null terminator + int j = 0; + for (int i = 0; buf[i] != '\0' && j < 64; i++) { + if (isxdigit(buf[i])) { + ascii_key[j++] = buf[i]; + } + } + ascii_key[64] = '\0'; + + /* Create key schedule */ + create_keys_pc5(&ctxpc5, (unsigned char*)ascii_key, 64); + ctxpc5.rounds = nbround; + + fprintf(stderr, "DMR BAOFENG AP (PC5) 256-bit Key: %s with Forced Application\n", ascii_key); + state->baofeng_ap = 1; + + } else { + fprintf(stderr, "ERROR: Invalid key length. Expected 32 chars (128-bit) or 64 chars (256-bit), got %d hex chars\n", hex_char_count); + return; + } +} diff --git a/src/dsd_main.c b/src/dsd_main.c index 69e6d2e..e993507 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -1065,6 +1065,7 @@ state->tyt_bp = 0; state->tyt_ep = 0; state->retevis_ap = 0; + state->baofeng_ap = 0; state->ken_sc = 0; state->any_bp = 0; @@ -1579,6 +1580,11 @@ printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n"); printf (" -5 '736B9A9C5645288B 243AD5CB8701EF8A' \n"); printf (" \n"); + printf (" -+ Manually Enter and Enforce Baofeng Advanced Privacy (PC5-128 or PC5-256) AP Hex Key (see example below)\n"); + printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n"); + printf (" -+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC' \n"); + printf (" -+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC EF01EF01EF01EF01 EF01EF01EF01EF01' \n"); + printf (" \n"); printf (" -9 Manually Enter and Enforce Kenwood 15-bit Scrambler Key Value (DMR) (Dec Value) \n"); printf (" \n"); printf (" -A Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value) \n"); @@ -1909,7 +1915,7 @@ exitflag = 0; - while ((c = getopt (argc, argv, "~yhaepPqs:t:v:z:i:o:d:c:g:n:w:B:C:R:f:m:u:x:A:S:M:G:D:L:V:U:YK:b:H:X:NQ:WrlZTF@:!:01:2:345:6:7:8*:9:Ek:I:J:O")) != -1) + while ((c = getopt (argc, argv, "~yhaepPqs:t:v:z:i:o:d:c:g:n:w:B:C:R:f:m:u:x:A:S:M:G:D:L:V:U:YK:b:H:X:NQ:WrlZTF@:!:01:2:345:6:7:8*:9:Ek:I:J:O+:")) != -1) { switch (c) @@ -2018,6 +2024,11 @@ anytone_bp_keystream_creation(&state, optarg); break; + //get user Baofeng AP Key and Force Its application + case '+': + baofeng_ap_pc5_keystream_creation(&state, optarg); + break; + //Straight KS Generation case 'S': straight_mod_xor_keystream_creation(&state, optarg); diff --git a/src/dsd_mbe.c b/src/dsd_mbe.c index c31b050..1992756 100644 --- a/src/dsd_mbe.c +++ b/src/dsd_mbe.c @@ -19,6 +19,7 @@ #include "bp.h" #include "pc4.h" #include "rc2.h" +#include "pc5.h" //NOTE: This set of functions will be reorganized and simplified (hopefully) or at least //a more logical flow will be established to jive with the new audio handling @@ -887,6 +888,20 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a } + //DMR BAOFENG AP, Either Slot (static single key'd enforced KS) + if (state->baofeng_ap == 1) + { + + short frame1_cipher[49]; + for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i]; + + decrypt_frame_49_pc5(frame1_cipher); + + memset (ambe_d, 0, 49*sizeof(char)); + for (int i = 0; i < 49; i++) ambe_d[i] = ctxpc5.bits[i]; + + } + //DMR TYT EP, Either Slot (static single key'd enforced KS) if (state->tyt_ep == 1) { @@ -1277,6 +1292,20 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a } + //DMR BAOFENG AP, Either Slot (static single key'd enforced KS) + if (state->baofeng_ap == 1) + { + + short frame1_cipher[49]; + for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i]; + + decrypt_frame_49_pc5(frame1_cipher); + + memset (ambe_d, 0, 49*sizeof(char)); + for (int i = 0; i < 49; i++) ambe_d[i] = ctxpc5.bits[i]; + + } + //DMR TYT EP, Either Slot (static single key'd enforced KS) if (state->tyt_ep == 1) {