From 3aabd8560404427f4a49f129e4230c11bca56a24 Mon Sep 17 00:00:00 2001 From: lwvmobile Date: Thu, 11 Sep 2025 22:49:22 -0400 Subject: [PATCH] DMR: Retevis / Anytone AP (RC2) Encryption Support; --- .../cygwin_bat/complete_usage_options.txt | 4 + include/dsd.h | 8 +- include/rc2.h | 43 +++ src/crypt-rc2.c | 334 ++++++++++++++++++ src/dsd_main.c | 12 +- src/dsd_mbe.c | 31 ++ 6 files changed, 428 insertions(+), 4 deletions(-) create mode 100644 include/rc2.h create mode 100644 src/crypt-rc2.c diff --git a/examples/cygwin_bat/complete_usage_options.txt b/examples/cygwin_bat/complete_usage_options.txt index c53abdf..c5a941a 100644 --- a/examples/cygwin_bat/complete_usage_options.txt +++ b/examples/cygwin_bat/complete_usage_options.txt @@ -175,6 +175,10 @@ Advanced Decoder options: -! Manually Enter and Enforce TYT Advanced Privacy (PC4) AP Hex Key (see example below) Encapulate in Single Quotation Marks; Space every 16 chars. -! '736B9A9C5645288B 243AD5CB8701EF8A' + + -@ Manually Enter and Enforce Retevis Advanced Privacy (RC2) AP Hex Key (see example below) + Encapulate in Single Quotation Marks; Space every 16 chars. + -@ '736B9A9C5645288B 243AD5CB8701EF8A' -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. diff --git a/include/dsd.h b/include/dsd.h index 4514c09..e11c6f8 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -991,6 +991,9 @@ typedef struct int tyt_ap; int tyt_bp; int tyt_ep; + // retrevis rc2 + int retevis_ap; + void *rc2_context; //kenwood scrambler on DMR with forced application int ken_sc; @@ -1629,12 +1632,11 @@ void aes_cfb_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * in, void aes_ctr_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type); void aes_ctr_bitwise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type); -//Tytera Encryption Modes +//Tytera / Retevis / Anytone / Kenwood / Misc DMR Encryption Modes void tyt16_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24], int fnum); void tyt_ep_aes_keystream_creation(dsd_state * state, char * input); void tyt_ap_pc4_keystream_creation(dsd_state * state, char * input); - -//Misc Other Encryption Modes +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 straight_mod_xor_keystream_creation(dsd_state * state, char * input); diff --git a/include/rc2.h b/include/rc2.h new file mode 100644 index 0000000..8e4731e --- /dev/null +++ b/include/rc2.h @@ -0,0 +1,43 @@ +#ifndef RETEVIS_AP +#define RETEVIS_AP + +#include +#include "dsd.h" + +#define n1 264 + +typedef struct { + uint8_t array_rc4[256]; + int i_rc4, j_rc4; + uint64_t x; + uint8_t xyz, count; + uint64_t bb; +} RC4State; + +typedef struct { + int x1, x2; + unsigned char h2[n1]; + unsigned char h1[n1 * 3]; +} MD2State; + +typedef struct { + uint16_t xkey[64]; + uint8_t plain[8]; + uint8_t cipher[8]; +} RC2State; + +typedef struct { + RC4State rc4; + MD2State md2; + RC2State rc2; + uint64_t internal_state; + uint64_t internal_zero; + uint8_t keys[16]; +} CryptoContext; + +/* Public API */ +void create_keys_rc2(CryptoContext* ctx, unsigned char key1[], size_t size1); +void encryption_rc2(CryptoContext* ctx, uint8_t s1[49]); +void decrypt_rc2(CryptoContext* ctx, uint8_t bits[49]); + +#endif diff --git a/src/crypt-rc2.c b/src/crypt-rc2.c new file mode 100644 index 0000000..05d03b0 --- /dev/null +++ b/src/crypt-rc2.c @@ -0,0 +1,334 @@ +#include "rc2.h" +#include "dsd.h" + +static inline uint64_t rol64(uint64_t x, int n) { + return ((x << n) | (x >> (63 - n) >> 1)) & 0xffffffffffffffff; +} + +void swapbit(uint64_t* internalstate, uint8_t bit) { + unsigned char bitB = bit & 1; + if (bitB) + *internalstate |= bitB; + else + *internalstate &= (~bitB ^ 1); +} + +// MD2 functions +void md2_init(MD2State* state) { + state->x1 = 0; + state->x2 = 0; + memset(state->h2, 0, n1); + memset(state->h1, 0, n1 * 3); +} + +void md2_hashing(MD2State* state, unsigned char t1[], size_t b6) { + static 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 b4 = 0; + + while (b6) { + for (; b6 && state->x2 < n1; b6--, state->x2++) { + int b5 = t1[b4++]; + state->h1[state->x2 + n1] = b5; + state->h1[state->x2 + (n1 * 2)] = b5 ^ state->h1[state->x2]; + state->x1 = state->h2[state->x2] ^= s4[b5 ^ state->x1]; + } + + if (state->x2 == n1) { + int b2 = 0; + state->x2 = 0; + + for (int b3 = 0; b3 < (n1 + 2); b3++) { + for (int b1 = 0; b1 < (n1 * 3); b1++) + b2 = state->h1[b1] ^= s4[b2]; + b2 = (b2 + b3) % 256; + } + } + } +} + +void md2_end(MD2State* state, unsigned char h4[n1]) { + unsigned char h3[n1]; + int n4 = n1 - state->x2; + for (int i = 0; i < n4; i++) h3[i] = n4; + md2_hashing(state, h3, n4); + md2_hashing(state, state->h2, sizeof(state->h2)); + for (int i = 0; i < n1; i++) h4[i] = state->h1[i]; +} + +// RC4 functions +uint64_t next(RC4State* state) { + uint64_t z = (state->x += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); +} + +void rc4_init(RC4State* state, unsigned char key[]) { + int tmp; + + for (state->i_rc4 = 0; state->i_rc4 < 256; state->i_rc4++) { + state->array_rc4[state->i_rc4] = state->i_rc4; + } + + state->j_rc4 = 0; + for (state->i_rc4 = 0; state->i_rc4 < 256; state->i_rc4++) { + state->j_rc4 = (state->j_rc4 + state->array_rc4[state->i_rc4] + key[state->i_rc4 % 256]) % 256; + tmp = state->array_rc4[state->i_rc4]; + state->array_rc4[state->i_rc4] = state->array_rc4[state->j_rc4]; + state->array_rc4[state->j_rc4] = tmp; + } + state->i_rc4 = 0; + state->j_rc4 = 0; +} + +unsigned char rc4_output(RC4State* state) { + uint8_t rndbyte, decal; + int tmp, t; + + state->i_rc4 = (state->i_rc4 + 1) % 256; + state->j_rc4 = (state->j_rc4 + state->array_rc4[state->i_rc4]) % 256; + tmp = state->array_rc4[state->i_rc4]; + state->array_rc4[state->i_rc4] = state->array_rc4[state->j_rc4]; + state->array_rc4[state->j_rc4] = tmp; + t = (state->array_rc4[state->i_rc4] + state->array_rc4[state->j_rc4]) % 256; + + + if (state->xyz == 0) state->bb = next(state); + decal = 56 - (8 * state->xyz); + rndbyte = (state->bb >> decal) & 0xff; + state->xyz++; + if (state->xyz == 8) state->xyz = 0; + + if (state->count == 0) { + rndbyte = rndbyte ^ state->array_rc4[t]; + state->count = 1; + } else { + rndbyte = rndbyte + state->array_rc4[t]; + state->count = 0; + } + + + return rndbyte; +} + +/* Convert 64-bit integer to bytes (big-endian) */ +static void u64_to_bytes_be(uint64_t val, unsigned char *out) { + for (int i = 0; i < 8; i++) { + out[i] = (unsigned char)((val >> (56 - 8 * i)) & 0xFF); + } +} + +// RC2 functions +void rc2_keyschedule(RC2State* state) { + unsigned i; + // Phase 3 - copy to xkey in little-endian order + i = 63; + do { + state->xkey[i] = ((unsigned char *)state->xkey)[2 * i] + + (((unsigned char *)state->xkey)[2 * i + 1] << 8); + } while (i--); +} + +void rc2_encrypt(RC2State* state) { + uint16_t x76, x54, x32, x10, i; + + x76 = (state->plain[7] << 8) + state->plain[6]; + x54 = (state->plain[5] << 8) + state->plain[4]; + x32 = (state->plain[3] << 8) + state->plain[2]; + x10 = (state->plain[1] << 8) + state->plain[0]; + + for (i = 0; i < 16; i++) { + x10 += (x32 & ~x76) + (x54 & x76) + state->xkey[4 * i + 0]; + x10 = (x10 << 1) + (x10 >> 15 & 1); + + x32 += (x54 & ~x10) + (x76 & x10) + state->xkey[4 * i + 1]; + x32 = (x32 << 2) + (x32 >> 14 & 3); + + x54 += (x76 & ~x32) + (x10 & x32) + state->xkey[4 * i + 2]; + x54 = (x54 << 3) + (x54 >> 13 & 7); + + x76 += (x10 & ~x54) + (x32 & x54) + state->xkey[4 * i + 3]; + x76 = (x76 << 5) + (x76 >> 11 & 31); + + if (i == 4 || i == 10) { + x10 += state->xkey[x76 & 63]; + x32 += state->xkey[x10 & 63]; + x54 += state->xkey[x32 & 63]; + x76 += state->xkey[x54 & 63]; + } + } + + state->cipher[0] = (unsigned char)x10; + state->cipher[1] = (unsigned char)(x10 >> 8); + state->cipher[2] = (unsigned char)x32; + state->cipher[3] = (unsigned char)(x32 >> 8); + state->cipher[4] = (unsigned char)x54; + state->cipher[5] = (unsigned char)(x54 >> 8); + state->cipher[6] = (unsigned char)x76; + state->cipher[7] = (unsigned char)(x76 >> 8); + +} + +// Main cryptographic functions +void create_keys_rc2(CryptoContext* ctx, unsigned char key1[], size_t size1) { + unsigned char h4[n1]; + + // Initialize MD2 and hash the key + md2_init(&ctx->md2); + md2_hashing(&ctx->md2, key1, size1); + md2_end(&ctx->md2, h4); + + // Copy first 16 bytes to keys + for (int i = 0; i < 16; i++) ctx->keys[i] = h4[i]; + + // Initialize RC4 with hashed key + rc4_init(&ctx->rc4, h4); + + // Initialize RC4 state variables + ctx->rc4.x = 0; + for (int i = 0; i < 8; i++) ctx->rc4.x = (ctx->rc4.x << 8) + (h4[256 + i] & 0xff); + + ctx->rc4.xyz = 0; + ctx->rc4.count = 0; + + // Warm-up RC4 + for (int i = 0; i < 22000; i++) rc4_output(&ctx->rc4); + + // Generate RC2 keys + int k = rc4_output(&ctx->rc4) + 256; + for (int i = 0; i < k; i++) rc4_output(&ctx->rc4); + + for (int i = 0; i < 64; i++) { + ctx->rc2.xkey[i] = (rc4_output(&ctx->rc4) << 8) + rc4_output(&ctx->rc4); + } + + // Generate internal zero value + k = rc4_output(&ctx->rc4) + 256; + for (int i = 0; i < k; i++) rc4_output(&ctx->rc4); + + ctx->internal_zero = 0; + for (int i = 0; i < 8; i++) { + ctx->internal_zero = (ctx->internal_zero << 8) + rc4_output(&ctx->rc4); + } + + // Generate RC2 key schedule + rc2_keyschedule(&ctx->rc2); +} + +void encryption_rc2(CryptoContext* ctx, uint8_t bits[49]) { + ctx->internal_state = ctx->internal_zero; + + for (int sso = 0; sso < 49; sso++) { + // Prepare plaintext from internal state + ctx->rc2.plain[0] = (ctx->internal_state >> 56) & 0xff; + ctx->rc2.plain[1] = (ctx->internal_state >> 48) & 0xff; + ctx->rc2.plain[2] = (ctx->internal_state >> 40) & 0xff; + ctx->rc2.plain[3] = (ctx->internal_state >> 32) & 0xff; + ctx->rc2.plain[4] = (ctx->internal_state >> 24) & 0xff; + ctx->rc2.plain[5] = (ctx->internal_state >> 16) & 0xff; + ctx->rc2.plain[6] = (ctx->internal_state >> 8) & 0xff; + ctx->rc2.plain[7] = ctx->internal_state & 0xff; + + // Encrypt with RC2 + rc2_encrypt(&ctx->rc2); + + // Reconstruct internal state from ciphertext + ctx->internal_state = 0; + for (int i = 0; i < 8; i++) { + ctx->internal_state = (ctx->internal_state << 8) + (ctx->rc2.cipher[i] & 0xff); + } + + // XOR the bit and update internal state + bits[48 - sso] = bits[48 - sso] ^ (ctx->internal_state & 1); + ctx->internal_state = rol64(ctx->internal_state, 1); + swapbit(&ctx->internal_state, bits[48 - sso]); + } +} + +void decrypt_rc2(CryptoContext* ctx, uint8_t bits[49]) { + uint8_t tempy; + ctx->internal_state = ctx->internal_zero; + + + for (int sso = 0; sso < 49; sso++) { + // Prepare plaintext from internal state + ctx->rc2.plain[0] = (ctx->internal_state >> 56) & 0xff; + ctx->rc2.plain[1] = (ctx->internal_state >> 48) & 0xff; + ctx->rc2.plain[2] = (ctx->internal_state >> 40) & 0xff; + ctx->rc2.plain[3] = (ctx->internal_state >> 32) & 0xff; + ctx->rc2.plain[4] = (ctx->internal_state >> 24) & 0xff; + ctx->rc2.plain[5] = (ctx->internal_state >> 16) & 0xff; + ctx->rc2.plain[6] = (ctx->internal_state >> 8) & 0xff; + ctx->rc2.plain[7] = ctx->internal_state & 0xff; + + // Encrypt with RC2 + rc2_encrypt(&ctx->rc2); + + // Reconstruct internal state from ciphertext + ctx->internal_state = 0; + for (int i = 0; i < 8; i++) { + ctx->internal_state = (ctx->internal_state << 8) + (ctx->rc2.cipher[i] & 0xff); + } + + // XOR the bit and update internal state + tempy = bits[48 - sso]; + bits[48 - sso] = bits[48 - sso] ^ (ctx->internal_state & 1); + ctx->internal_state = rol64(ctx->internal_state, 1); + swapbit(&ctx->internal_state, tempy); + + } +} + +/* Key creation for Retevis AP */ +void retevis_rc2_keystream_creation(dsd_state *state, char *input) { + + unsigned char key1[16] = {0}; + unsigned char key2[16] = {0}; + + char buf[1024]; + strncpy(buf, input, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + + char *pEnd; + uint64_t K1 = strtoull(buf, &pEnd, 16); + uint64_t K2 = strtoull(pEnd, &pEnd, 16); + + u64_to_bytes_be(K1, &key1[0]); + u64_to_bytes_be(K2, &key1[8]); + + for (int i=0;i<16;i++) key2[i] = key1[15-i]; + + // Initialize RC2 context + static CryptoContext rc2_ctx; + create_keys_rc2(&rc2_ctx, key2, sizeof(key2)); + + // Store context in DSD state + state->rc2_context = malloc(sizeof(CryptoContext)); + memcpy(state->rc2_context, &rc2_ctx, sizeof(CryptoContext)); + + fprintf(stderr, "DMR RETEVIS AP (RC2) 128-bit Key %016llX%016llX with Forced Application\n", + (unsigned long long)K1, (unsigned long long)K2); + state->retevis_ap = 1; +} diff --git a/src/dsd_main.c b/src/dsd_main.c index 860cf93..f13250c 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -1064,6 +1064,7 @@ state->tyt_ap = 0; state->tyt_bp = 0; state->tyt_ep = 0; + state->retevis_ap = 0; state->ken_sc = 0; state->any_bp = 0; @@ -1565,6 +1566,10 @@ printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n"); printf (" -! '736B9A9C5645288B 243AD5CB8701EF8A' \n"); printf (" \n"); + printf (" -@ Manually Enter and Enforce Retevis Advanced Privacy (RC2) AP Hex Key (see example below)\n"); + printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n"); + printf (" -@ '736B9A9C5645288B 243AD5CB8701EF8A' \n"); + printf (" \n"); printf (" -5 Manually Enter and Enforce TYT Enhanced Privacy (AES-128) EP Hex Key (see example below)\n"); printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n"); printf (" -5 '736B9A9C5645288B 243AD5CB8701EF8A' \n"); @@ -1899,7 +1904,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:89: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:89:Ek:I:J:O")) != -1) { switch (c) @@ -1987,6 +1992,11 @@ case '!': tyt_ap_pc4_keystream_creation(&state, optarg); break; + + //get user Retevis AP Key and Force Its application + case '@': + retevis_rc2_keystream_creation(&state, optarg); + break; //get user TYT EP Key and Force Its application case '5': diff --git a/src/dsd_mbe.c b/src/dsd_mbe.c index da24bd8..c31b050 100644 --- a/src/dsd_mbe.c +++ b/src/dsd_mbe.c @@ -18,6 +18,7 @@ #include "dsd.h" #include "bp.h" #include "pc4.h" +#include "rc2.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 @@ -857,6 +858,21 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a unpack_ambe(plain, ambe_d); } + + //DMR Retevis AP, Either Slot (static single key'd enforced KS) + if (state->retevis_ap == 1) + { + + uint8_t frame1_cipher[49]; + + for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i]; + + decrypt_rc2((CryptoContext *)state->rc2_context, frame1_cipher); + + memset (ambe_d, 0, 49*sizeof(char)); + for (int i = 0; i < 49; i++) ambe_d[i] = frame1_cipher[i]; + + } //DMR TYT AP, Either Slot (static single key'd enforced KS) if (state->tyt_ap == 1) @@ -1233,6 +1249,21 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a } + //DMR Retevis AP, Either Slot (static single key'd enforced KS) + if (state->retevis_ap == 1) + { + + uint8_t frame1_cipher[49]; + + for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i]; + + decrypt_rc2((CryptoContext *)state->rc2_context, frame1_cipher); + + memset (ambe_d, 0, 49*sizeof(char)); + for (int i = 0; i < 49; i++) ambe_d[i] = frame1_cipher[i]; + + } + //DMR TYT AP, Either Slot (static single key'd enforced KS) if (state->tyt_ap == 1) {