/*------------------------------------------------------------------------------- * rc4.c Crypthings * RC4 Alg *-----------------------------------------------------------------------------*/ #include "dsd.h" #include "bp.h" //this version is for voice, going to transition to a block output version void rc4_voice_decrypt(int drop, uint8_t keylength, uint8_t messagelength, uint8_t key[], uint8_t cipher[], uint8_t plain[]) { int i, j, count; uint8_t t, b; //init Sbox uint8_t S[256]; for(int i = 0; i < 256; i++) S[i] = i; //Key Scheduling j = 0; for(i = 0; i < 256; i++) { j = (j + S[i] + key[i % keylength]) % 256; t = S[i]; S[i] = S[j]; S[j] = t; } //Drop Bytes and Cipher Byte XOR i = j = 0; for(count = 0; count < (messagelength + drop); count++) { i = (i + 1) % 256; j = (j + S[i]) % 256; t = S[i]; S[i] = S[j]; S[j] = t; b = S[(S[i] + S[j]) % 256]; //return mbe payload byte here if (count >= drop) plain[count - drop] = b^cipher[count - drop]; } } //this is for PDU usage void rc4_block_output (int drop, int keylen, int meslen, uint8_t * key, uint8_t * output_blocks) { int i, j, x, count; unsigned int keylength = (unsigned int)keylen; unsigned int messagelength = (unsigned int)meslen; unsigned int S[256]; for(i=0; i<256; i++) S[i] = i; j = 0; for(i = 0; i<256; i++) { j = (j + S[i] + key[i % keylength]) % 256; unsigned int temp = S[i]; S[i] = S[j]; S[j] = temp; } //Generate Keystream i = 0; j = 0; x = 0; unsigned int byte; // fprintf (stderr, " Keystream Octets = "); for(count = 0; count < (messagelength + drop); count++) { i = (i + 1) % 256; j = (j + S[i]) % 256; unsigned int temp = S[i]; S[i] = S[j]; S[j] = temp; byte = S[(S[i] + S[j]) % 256]; //Collect Output blocks if (count >= drop) output_blocks[x++] = byte; } } //This is now verified to work after changing the drop byte value from 256 to 0. //also, had to change the application to not skip the additional 7 bits like DMRA or P25 does. void hytera_enhanced_rc4_setup(dsd_opts * opts, dsd_state * state, unsigned long long int key_value, unsigned long long int mi_value, int len) { UNUSED(opts); uint8_t key[5]; memset (key, 0, sizeof(key)); uint8_t kiv[5]; memset (kiv, 0, sizeof(kiv)); uint8_t mi[5]; memset (mi, 0, sizeof(mi)); uint8_t ks[2000]; memset (ks, 0, sizeof(ks)); //load key_value into key array key[0] = ((key_value & 0xFF00000000) >> 32UL); key[1] = ((key_value & 0xFF000000) >> 24); key[2] = ((key_value & 0xFF0000) >> 16); key[3] = ((key_value & 0xFF00) >> 8); key[4] = ((key_value & 0xFF) >> 0); //load mi_value into mi array mi[0] = ((mi_value & 0xFF00000000) >> 32UL); mi[1] = ((mi_value & 0xFF000000) >> 24); mi[2] = ((mi_value & 0xFF0000) >> 16); mi[3] = ((mi_value & 0xFF00) >> 8); mi[4] = ((mi_value & 0xFF) >> 0); //pointer to the ks_octet storage uint8_t * ks_octets; if (state->currentslot == 0) ks_octets = state->ks_octetL; else ks_octets = state->ks_octetR; //NOTE: Drop Byte value is 0 rc4_block_output(0, 5, len, key, ks); for (int i = 0; i < 5; i++) kiv[i] = key[i] ^ mi[i]; for (int i = 0; i < len; i++) ks_octets[i] = kiv[i%5] ^ ks[i]; //debug // fprintf (stderr, " KS: "); // for (int i = 0; i < len; i++) // { // if ((i != 0) && ((i%7) == 0)) // fprintf (stderr, " "); // fprintf (stderr, "%02X", ks[i]); //ks_octets // } //NULL pointer to ks_octets ks_octets = NULL; //end line break // fprintf (stderr, "\n"); } void rc_poor_keystream_output(uint8_t * key, uint8_t * ks_bytes) { int16_t i, j, x, count; uint8_t t, b; //init Sbox uint8_t S[255]; for(i = 0; i < 255; i++) S[i] = i; //Key Scheduling j = 0; for(i = 0; i < 255; i++) { j = (j + S[i] + key[i % 8]) % 255; t = S[i]; S[i] = S[j]; S[j] = t; } //KS Byte collection i = j = x = 0; for(count = 0; count < 6; count++) { i = (i + 1) % 255; j = (j + S[i]) % 255; t = S[i]; S[i] = S[j]; S[j] = t; b = (S[i] + S[j]) % 255; ks_bytes[x++] = b; } } void auctus_keystream_creation(dsd_state * state, char * input) { uint8_t key[8]; memset (key, 0, sizeof(key)); uint16_t len = strlen((const char*)input); //sanity check on provided char len if (len > 8) len = 8; //load chars as uint8_t into key array for (int i = 0; i < len; i++) key[i] = (uint8_t)input[i]; //debug fprintf (stderr, "Key String: %s; LEN: %d; HEX: ", input, len); for (int i = 0; i < len; i++) fprintf (stderr, "%02X", key[i]); fprintf (stderr, "\n"); uint8_t ks_bytes[6]; memset (ks_bytes, 0, sizeof(ks_bytes)); rc_poor_keystream_output(key, ks_bytes); uint8_t ks_bits[48]; memset(ks_bits, 0, sizeof(ks_bits)); unpack_byte_array_into_bit_array(ks_bytes, ks_bits, 6); //Reorder the bits of the keystream to compensate //for 16-bit words with reversed bit storage for (uint16_t j = 0; j < 3; j++) { uint16_t k = ((j+1)*16) - 1; uint16_t x = j*16; for (uint16_t i = 0; i < 16; i++) { state->static_ks_bits[0][x] = ks_bits[k]; state->static_ks_bits[1][x] = ks_bits[k]; //debug // fprintf (stderr, " K: %02d; ", k); //debug // fprintf (stderr, " X: %02d; ", x); k--; x++; } } fprintf (stderr,"Auctus Keystream: "); for (uint16_t i = 0; i < 6; i++) fprintf (stderr, "%02X", ks_bytes[i]); fprintf (stderr," - Word Bit Reverse: "); for (uint16_t i = 0; i < 6; i++) fprintf (stderr, "%02X", convert_bits_into_output(state->static_ks_bits[0]+(i*8), 8)); fprintf (stderr, " with Forced Application \n"); state->straight_ks = 1; state->straight_mod = 49; } void vertex_40_keystream_creation(dsd_state * state, unsigned long long int key_value) { uint8_t key[8]; memset (key, 0, sizeof(key)); //load key from key_value for (int i = 0; i < 5; i++) key[i] = (key_value >> (32-(i*8))) & 0xFF; uint8_t rc4_bytes[4]; memset (rc4_bytes, 0, sizeof(rc4_bytes)); rc4_block_output(0, 5, 4, key, rc4_bytes); uint64_t vtx_key = 0; //use the bytes from rc4 output to select 4 BP key 16-bit words for (int i = 0; i < 4; i++) { vtx_key <<= 16; vtx_key |= BPK[rc4_bytes[i]]; } #ifdef VERTEX_KEY40_BY_TG //skip constant verbosity on keyloading #else fprintf (stderr,"Vertex RC4 Bytes: "); for (uint16_t i = 0; i < 8; i++) fprintf (stderr, "%02X ", rc4_bytes[i]); fprintf (stderr, "Vertex Keystream: %016llX;", (unsigned long long int)vtx_key); fprintf (stderr, "\n"); #endif uint8_t vtx_bytes[8]; memset (vtx_bytes, 0, sizeof(vtx_bytes)); for (int i = 0; i < 8; i++) vtx_bytes[i] = (vtx_key >> (56-(i*8))) & 0xFF; unpack_byte_array_into_bit_array(vtx_bytes, state->static_ks_bits[0], 8); unpack_byte_array_into_bit_array(vtx_bytes, state->static_ks_bits[1], 8); state->vtx_key_loaded = 1; } void vertex_keystream_setup(dsd_state * state, char * input) { uint16_t len = 0; char * curr; curr = strtok(input, ":"); //should be len (mod) of key (decimal) if (curr != NULL) sscanf (curr, "%hd", &len); else goto END_KS; //len sanity check if (len != 0 && len != 10 && len != 64) { fprintf (stderr, "Vertex Standard Invalid Key Len Specified;"); goto END_KS; } curr = strtok(NULL, ":"); //should be key in hex if (curr == NULL) { fprintf (stderr, "Vertex Standard No Key Value Specified;"); goto END_KS; } if (len == 10) { sscanf (curr, "%llx", &state->vtx40_key); fprintf (stderr, "Vertex Standard 40-bit Key: %010llX \n", state->vtx40_key); vertex_40_keystream_creation(state, state->vtx40_key); } else if (len == 64) { memcpy(state->vtx256_key, curr, 64); fprintf (stderr, "Vertex Standard 256-bit Key: "); for (int i = 0; i < 64; i++) fprintf (stderr, "%c", state->vtx256_key[i]); fprintf (stderr, "\n"); vertex_256_keystream_creation(state, curr); } //special use case 0:0 to do the inversion, but the keystream of zero to clear the errors //essentially no keystream is applied, but to analyze the frames, clean up the errors else if (len == 0) { memset(state->static_ks_bits, 0, sizeof(state->static_ks_bits)); state->vtx256_ekey = 1; } END_KS: ; //do nothing } //https://patents.google.com/patent/CN106789903B/en void hytera_ota_keystream_setup(dsd_state * state, char * input) { uint16_t type = 0; char * curr; curr = strtok(input, ":"); if (curr != NULL) sscanf (curr, "%hd", &type); else goto END_KS; //type sanity check if (type != 0 && type != 128) { fprintf (stderr, "Hytera OTA Encryption Invalid Key type Specified;"); goto END_KS; } curr = strtok(NULL, ":"); //should be key in hex if (curr == NULL) { fprintf (stderr, "Hytera OTA Encryption No Key Value Specified;"); goto END_KS; } if (type == 0) //No Key specified, just check for potential HOTA and DMRA EP { fprintf (stderr, "Hytera OTA Encryption Check w/ DMRA EP Late Entry IV Check;\n"); state->hota_enc_check = 1; } else //if (type == 128) //RC4 setup on keystream { uint8_t key[50]; memset(key, 0, sizeof(key)); parse_raw_user_string(curr, key); // #ifdef DEBUG_HOTA fprintf(stderr, "Hytera OTA Encryption RC4 Key: "); for (int i = 0; i < 16; i++) fprintf (stderr, "%02X", key[i]); fprintf (stderr, "\n"); // #endif for (int i = 0; i < 16; i++) state->hota_rc4_key[i] = key[i]; uint8_t ks_bytes[80]; memset(ks_bytes, 0, sizeof(ks_bytes)); //NOTE: Drop Byte value is 0 rc4_block_output(0, 16, 80, key, ks_bytes); #ifdef DEBUG_HOTA for (int j = 0; j < 5; j++) { fprintf(stderr, "Hytera OTA Encryption RC4 KS: "); for (int i = 0; i < 16; i++) fprintf (stderr, "%02X", ks_bytes[i+j*16]); fprintf (stderr, "\n"); } #endif for (int i = 0; i < 80; i++) state->hota_rc4_ks[i] = ks_bytes[i]; //configure individual byte and full 96-bit PDU bit final bit shift / rotations for (int i = 0; i < 12; i++) state->hota_byte_shifts[i] = ks_bytes[i+16] % 8; state->hota_bit_shift = ks_bytes[29]; state->hota_bit_shift <<= 8; state->hota_bit_shift |= ks_bytes[28]; state->hota_bit_shift %= 96; #ifdef DEBUG_HOTA fprintf(stderr, "Hytera OTA Full Bit Shift: %d; Hytera OTA Byte Shifts: ", state->hota_bit_shift); for (int i = 0; i < 12; i++) fprintf (stderr, "%d,", state->hota_byte_shifts[i]); fprintf (stderr, "\n"); #endif state->hota_key_loaded = 1; state->dmr_is_hota = 1; } END_KS: // } void rsr_rot(uint8_t * input, int16_t bitlen) { uint8_t bit = input[bitlen-1]; for (int16_t i = (bitlen-1); i > 0; i--) input[i] = input[i-1]; input[0] = bit; } void lsr_rot(uint8_t * input, int16_t bitlen) { uint8_t bit = input[0]; for (int16_t i = 0; i < (bitlen-1); i++) input[i] = input[i+1]; input[(bitlen-1)] = bit; } void hytera_ota_decrypt_bptc_96(dsd_state * state, uint8_t * input, uint8_t type) { uint8_t bits[96]; memset(bits, 0, sizeof(bits)); uint8_t bytes[12]; memset(bytes, 0, sizeof(bytes)); for (int i = 0; i < 96; i++) bits[i] = input[i]; #ifdef DEBUG_HOTA fprintf (stderr, "\n\n-----BPTC 96 Bit Decryption-----"); #endif #ifdef DEBUG_HOTA fprintf (stderr, "\n"); fprintf(stderr, "HOTA Inp: "); for (int i = 0; i < 12; i++) fprintf (stderr, "%02X", convert_bits_into_output(bits+i*8, 8)); #endif //RC4 key type decryption if (state->hota_key_loaded == 1) { //reverse the final bit rotation with a right shift register for (int i = 0; i < state->hota_bit_shift; i++) rsr_rot(bits, 96); //reverse the individual bit rotations in each byte with a right shift register for (int i = 0; i < 12; i++) { int shift = state->hota_byte_shifts[i]; for (int j = 0; j < shift; j++) rsr_rot(bits+(i*8), 8); } pack_bit_array_into_byte_array(bits, bytes, 12); #ifdef DEBUG_HOTA fprintf (stderr, "\n"); fprintf(stderr, "HOTA Rot: "); for (int i = 0; i < 12; i++) fprintf (stderr, "%02X", bytes[i]); #endif //create a new RC4 keystream derivitive with preconfigured HOTA Keystream uint8_t key[19]; memset(key, 0, sizeof(key)); for (int i = 0; i < 16; i++) key[i] = state->hota_rc4_key[i]; int plen = 9; int blen = 72; int klen = 16; int k = type; //VLC or TLC w/ RS encoding if (type == 1 || type == 2) { key[klen] = (uint8_t)convert_bits_into_output(bits+blen, 8); key[klen++] ^= state->hota_rc4_ks[k++ % 16]; blen += 8; key[klen] = (uint8_t)convert_bits_into_output(bits+blen, 8); key[klen++] ^= state->hota_rc4_ks[k++ % 16]; blen += 8; key[klen] = (uint8_t)convert_bits_into_output(bits+blen, 8); key[klen++] ^= state->hota_rc4_ks[k++ % 16]; blen += 8; } else //CRC16 { plen = 10; blen = 80; key[klen] = (uint8_t)convert_bits_into_output(bits+blen, 8); key[klen++] ^= state->hota_rc4_ks[k++ % 16]; blen += 8; key[klen] = (uint8_t)convert_bits_into_output(bits+blen, 8); key[klen++] ^= state->hota_rc4_ks[k++ % 16]; blen += 8; } #ifdef DEBUG_HOTA fprintf (stderr, "\n"); fprintf(stderr, "HOTA KEY: "); for (int i = 0; i < klen; i++) fprintf (stderr, "%02X", key[i]); #endif uint8_t ks_bytes[12]; memset(ks_bytes, 0, sizeof(ks_bytes)); //NOTE: Drop Byte value is 0 rc4_block_output(0, klen, plen, key, ks_bytes); #ifdef DEBUG_HOTA fprintf (stderr, "\n"); fprintf(stderr, "HOTA RC4: "); for (int i = 0; i < plen; i++) fprintf (stderr, "%02X", ks_bytes[i]); #endif //reset the klen pointer to the start of the RS or CRC encoding klen -= (12 - plen); //decrypt final bytes for (int i = 0; i < plen; i++) bytes[i] ^= ks_bytes[i]; //Add current RS or CRC encoding for (int i = plen; i < 12; i++) bytes[i] = key[klen++]; // #ifdef DEBUG_HOTA // fprintf (stderr, "\n"); // fprintf(stderr, "HOTA Type: %X; PLEN: %d; BLEN: %d; KLEN: %d; K: %d;", type, plen, blen, klen, k); // #endif //unpack the final decrypted bytes back into a bit array unpack_byte_array_into_bit_array(bytes, input, 12); } #ifdef DEBUG_HOTA fprintf (stderr, "\n"); fprintf(stderr, "HOTA Out: "); for (int i = 0; i < 12; i++) fprintf (stderr, "%02X", bytes[i]); fprintf (stderr, "\n"); #endif } static const uint8_t elc_row_map[77] = { /* R0: (h0 << 3) | (h1 >> 5) */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, /* R1: ((h1 & 0x1F) << 6) | (h2 >> 2) */ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, /* R2: ((h2 & 0x03) << 9) | (h3 << 1) | CRC[4] */ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 72, /* R3: (h4 << 3) | ((h5 >> 6) << 1) | CRC[3] */ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 73, /* R4: ((h5 & 0x3F) << 5) | ((h6 >> 4) << 1) | CRC[2] */ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 74, /* R5: ((h6 & 0x0F) << 7) | ((h7 >> 2) << 1) | CRC[1] */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 75, /* R6: ((h7 & 0x03) << 9) | (h8 << 1) | CRC[0] */ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 76 }; void hytera_create_lc_ctx(dsd_state * state, uint8_t * input) { #ifdef DEBUG_HOTA fprintf (stderr, "\n-----Voice LC 4 Byte CTX-----"); #endif uint8_t bits[77]; memset(bits, 0, sizeof(bits)); for (int i = 0; i < 72; i++) bits[i] = input[i] & 1; /* Mode 1: left-rotate LC by 1 bit before CRC and rows */ // if (mode) //never been observed, so disabling this // lsr_rot(bits, 72); /* CRC5 over the 9 bytes */ uint32_t crc5 = 0; for (int i = 0; i < 9; i++) crc5 += convert_bits_into_output(bits + (i * 8), 8); crc5 %= 31; for (int i = 0; i < 5; i++) bits[72 + i] = (crc5 >> (4 - i)) & 1; uint16_t rows[7] = {0}; for (int i = 0; i < 77; i++) { rows[i / 11] <<= 1; rows[i / 11] |= bits[elc_row_map[i]] & 1; } #ifdef DEBUG_HOTA fprintf (stderr, "\nHOTA ROW: "); for (int i = 0; i < 7; i++) fprintf (stderr, "%03X ", rows[i]); #endif uint16_t q0 = rows[3] & 0x7FF; uint16_t q1 = 0; for (int i = 0; i < 7; i++) q1 ^= rows[i]; q1 &= 0x7FF; uint8_t ctx_out[4]; ctx_out[0] = (uint8_t)(q0 >> 3); ctx_out[1] = (uint8_t)((q0 & 7) << 5); ctx_out[2] = (uint8_t)(q1 >> 3); ctx_out[3] = (uint8_t)((q1 & 7) << 5); //load ctx to state for (int i = 0; i < 4; i++) state->hota_lc_ctx[state->currentslot][i] = ctx_out[i]; //signal ctx is available for embedded lc state->hota_ctx_ok[state->currentslot] = 1; //disable to test the search #ifdef DEBUG_HOTA fprintf (stderr, "\nHOTA CTX: Q0: %03X; Q1: %03X; CTX: %02X %02X %02X %02X;\n", q0, q1, ctx_out[0], ctx_out[1], ctx_out[2], ctx_out[3]); #endif } static const uint8_t elc_transpose_map[32] = { 0, 4, 8, 12, 16, 20, 24, 28, /* → result bits 31..24 */ 1, 5, 9, 13, 17, 21, 25, 29, /* → result bits 23..16 */ 2, 6, 10, 14, 18, 22, 26, 30, /* → result bits 15.. 8 */ 3, 7, 11, 15, 19, 23, 27, 31 /* → result bits 7.. 0 */ }; void hytera_ota_decrypt_emb_lc(dsd_state * state, uint8_t input[8][16]) { if (state->hota_ctx_ok[state->currentslot] == 0) return; #ifdef DEBUG_HOTA fprintf (stderr, "\n-----EMB LC and FEC Decryption-----\n"); #endif uint8_t bits[128]; memset(bits, 0, sizeof(bits)); uint8_t bytes[16]; memset(bytes, 0, sizeof(bytes)); int k = 0; #ifdef DEBUG_HOTA fprintf(stderr, "HOTA Inp: "); for (int i = 0; i < 8; i++) fprintf (stderr, "%04X", (uint16_t)convert_bits_into_output(input[i], 16)); fprintf (stderr, "\n"); #endif //OTA load as linear bits for (int i = 0; i < 16; i++) { for (int j = 0; j < 8; j++) bits[k++] = input[j][i]; } pack_bit_array_into_byte_array(bits, bytes, 16); #ifdef DEBUG_HOTA fprintf(stderr, "HOTA Col: "); for (int i = 0; i < 16; i++) fprintf (stderr, "%02X", bytes[i]); fprintf (stderr, "\n"); #endif //RC4 things uint8_t key[20]; memset(key, 0, sizeof(key)); uint8_t ks_bytes[16]; memset(ks_bytes, 0, sizeof(ks_bytes)); k = 0; for (int i = 0; i < 16; i++) key[k++] = state->hota_rc4_key[i]; for (int i = 0; i < 4; i++) key[k++] = state->hota_lc_ctx[state->currentslot][i]; #ifdef DEBUG_HOTA fprintf(stderr, "HOTA Key: "); for (int i = 0; i < 20; i++) fprintf (stderr, "%02X", key[i]); fprintf (stderr, "\n"); #endif //NOTE: Drop Byte value is 0 rc4_block_output(0, 20, 16, key, ks_bytes); #ifdef DEBUG_HOTA fprintf(stderr, "HOTA RC4: "); for (int i = 0; i < 12; i++) fprintf (stderr, "%02X", ks_bytes[i]); fprintf (stderr, "\n"); #endif //Nibble swap from state->hota_rc4_ks ks_bytes[1] &= 0xF0; ks_bytes[1] |= state->hota_rc4_ks[30] & 0xF; ks_bytes[3] &= 0xF0; ks_bytes[3] |= state->hota_rc4_ks[31] & 0xF; ks_bytes[5] &= 0xF0; ks_bytes[5] |= state->hota_rc4_ks[32] & 0xF; ks_bytes[7] &= 0xF0; ks_bytes[7] |= state->hota_rc4_ks[33] & 0xF; ks_bytes[9] &= 0xF0; ks_bytes[9] |= state->hota_rc4_ks[34] & 0xF; ks_bytes[11] &= 0xF0; ks_bytes[11] |= state->hota_rc4_ks[35] & 0xF; #ifdef DEBUG_HOTA fprintf(stderr, "HOTA Nib: "); for (int i = 0; i < 6; i++) fprintf (stderr, " %X", state->hota_rc4_ks[i+30] & 0xF); fprintf (stderr, "\n"); #endif #ifdef DEBUG_HOTA fprintf(stderr, "HOTA RC4: "); for (int i = 0; i < 12; i++) fprintf (stderr, "%02X", ks_bytes[i]); fprintf (stderr, "\n"); #endif //M0, M1, M2 values uint8_t ks_bits[128]; memset(ks_bits, 0, sizeof(ks_bits)); unpack_byte_array_into_bit_array(ks_bytes, ks_bits, 16); uint8_t int_bits[128]; memset(int_bits, 0, sizeof(int_bits)); k = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 32; j++) int_bits[k++] = ks_bits[elc_transpose_map[j]+(i*32)]; } uint8_t int_bytes[16]; memset(int_bytes, 0, sizeof(int_bytes)); pack_bit_array_into_byte_array(int_bits, int_bytes, 16); #ifdef DEBUG_HOTA fprintf(stderr, "HOTA Int: "); for (int i = 0; i < 12; i++) fprintf (stderr, "%02X", int_bytes[i]); fprintf (stderr, "\n"); #endif //reorder the int_bytes memset(ks_bytes, 0, sizeof(ks_bytes)); k = 0; for (int i = 0; i < 4; i++) ks_bytes[k++] = int_bytes[i]; for (int i = 0; i < 4; i++) ks_bytes[k++] = int_bytes[i+4]; for (int i = 0; i < 4; i++) ks_bytes[k++] = int_bytes[i+4]; for (int i = 0; i < 4; i++) ks_bytes[k++] = int_bytes[i+8]; for (int i = 0; i < 16; i++) bytes[i] ^= ks_bytes[i]; #ifdef DEBUG_HOTA fprintf(stderr, "HOTA KS2: "); for (int i = 0; i < 16; i++) fprintf (stderr, "%02X", ks_bytes[i]); fprintf (stderr, "\n"); #endif #ifdef DEBUG_HOTA fprintf(stderr, "HOTA Out: "); for (int i = 0; i < 16; i++) fprintf (stderr, "%02X", bytes[i]); fprintf (stderr, "\n\n"); #endif unpack_byte_array_into_bit_array(bytes, bits, 16); k = 0; //OTA reload from linear bits to 2d input array for (int i = 0; i < 16; i++) { for (int j = 0; j < 8; j++) input[j][i] = bits[k++]; } } //This is a modified decrypt but to brute force an unknown 22-bit ctx value //This should not be run on live systems, but useful for replay or debug //This takes around 25 seconds on a full search and failure on Ryzen 5 2600 void hytera_ota_search_emb_lc(dsd_state * state, uint8_t input[8][16]) { #ifdef DEBUG_HOTA fprintf (stderr, "\n-----EMB LC and FEC CTX Search-----\n"); #endif for (uint32_t ctx = 0; ctx < 0x3FFFFF; ctx++) { uint8_t bits[128]; memset(bits, 0, sizeof(bits)); uint8_t bytes[16]; memset(bytes, 0, sizeof(bytes)); int k = 0; //OTA load as linear bits for (int i = 0; i < 16; i++) { for (int j = 0; j < 8; j++) bits[k++] = input[j][i]; } pack_bit_array_into_byte_array(bits, bytes, 16); uint16_t q0 = 0, q1 = 0; q0 = (ctx >> 11) & 0x7FF; q1 = (ctx >> 0) & 0x7FF; uint8_t ctx_out[4]; memset(ctx_out, 0, sizeof(ctx_out)); ctx_out[0] = (uint8_t)(q0 >> 3); ctx_out[1] = (uint8_t)((q0 & 7) << 5); ctx_out[2] = (uint8_t)(q1 >> 3); ctx_out[3] = (uint8_t)((q1 & 7) << 5); //RC4 things uint8_t key[20]; memset(key, 0, sizeof(key)); uint8_t ks_bytes[16]; memset(ks_bytes, 0, sizeof(ks_bytes)); k = 0; for (int i = 0; i < 16; i++) key[k++] = state->hota_rc4_key[i]; for (int i = 0; i < 4; i++) key[k++] = ctx_out[i]; //NOTE: Drop Byte value is 0 rc4_block_output(0, 20, 16, key, ks_bytes); //Nibble swap from state->hota_rc4_ks ks_bytes[1] &= 0xF0; ks_bytes[1] |= state->hota_rc4_ks[30] & 0xF; ks_bytes[3] &= 0xF0; ks_bytes[3] |= state->hota_rc4_ks[31] & 0xF; ks_bytes[5] &= 0xF0; ks_bytes[5] |= state->hota_rc4_ks[32] & 0xF; ks_bytes[7] &= 0xF0; ks_bytes[7] |= state->hota_rc4_ks[33] & 0xF; ks_bytes[9] &= 0xF0; ks_bytes[9] |= state->hota_rc4_ks[34] & 0xF; ks_bytes[11] &= 0xF0; ks_bytes[11] |= state->hota_rc4_ks[35] & 0xF; //M0, M1, M2 values uint8_t ks_bits[128]; memset(ks_bits, 0, sizeof(ks_bits)); unpack_byte_array_into_bit_array(ks_bytes, ks_bits, 16); uint8_t int_bits[128]; memset(int_bits, 0, sizeof(int_bits)); k = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 32; j++) int_bits[k++] = ks_bits[elc_transpose_map[j]+(i*32)]; } uint8_t int_bytes[16]; memset(int_bytes, 0, sizeof(int_bytes)); pack_bit_array_into_byte_array(int_bits, int_bytes, 16); //reorder the int_bytes memset(ks_bytes, 0, sizeof(ks_bytes)); k = 0; for (int i = 0; i < 4; i++) ks_bytes[k++] = int_bytes[i]; for (int i = 0; i < 4; i++) ks_bytes[k++] = int_bytes[i+4]; for (int i = 0; i < 4; i++) ks_bytes[k++] = int_bytes[i+4]; for (int i = 0; i < 4; i++) ks_bytes[k++] = int_bytes[i+8]; for (int i = 0; i < 16; i++) bytes[i] ^= ks_bytes[i]; unpack_byte_array_into_bit_array(bytes, bits, 16); //FEC test on results, break and set ctx if good and unique (only one good?) uint8_t matrix[8][16]; memset(matrix, 0, sizeof(matrix)); //OTA reload from linear bits to 2d input array k = 0; for (int i = 0; i < 16; i++) { for (int j = 0; j < 8; j++) matrix[j][i] = bits[k++]; } //This will not correct, but reports all errors (hamming corrections and parity) uint32_t errors = BPTC_128x77_Check_Errors(matrix); if (errors == 0) { //Extract + CRC5 to validate uint8_t lc77[77]; if (BPTC_128x77_Extract_Data(matrix, lc77) == 0 && ComputeCrc5Bit(lc77) == ConvertBitIntoBytes(&lc77[72], 5)) { #ifdef DEBUG_HOTA fprintf (stderr, "CTX: %06X; Q0: %03X; Q1: %03X;\n", ctx, q0, q1); fprintf (stderr, "ERR: %d; CRC EXT: %02X; CRC CMP: %02X\n", errors, ComputeCrc5Bit(lc77), ConvertBitIntoBytes(&lc77[72], 5)); fprintf(stderr, "HOTA Out: "); for (int i = 0; i < 16; i++) fprintf (stderr, "%02X", bytes[i]); fprintf (stderr, "\n\n"); #endif //set missing CTX here, set flag for okay ctx, and return for (int i = 0; i < 4; i++) state->hota_lc_ctx[state->currentslot][i] = ctx_out[i]; state->hota_ctx_ok[state->currentslot] = 1; return; } } //get out of brute force loop early on exit signal if (exitflag) break; } } void hytera_ota_unit_testing(dsd_state * state) { char keystr[100]; memset(keystr, 0, sizeof(keystr)); sprintf (keystr, "%s", "2:17bfe59b29a06136fb7d3811fd1a4904"); hytera_ota_keystream_setup(state, keystr); char string[30]; memset(string, 0, sizeof(string)); uint8_t bytes[12]; uint8_t bits[96]; //PI header memset(string, 0, sizeof(string)); sprintf(string, "%s", "2ca5f7b049c350664cf09e4e"); memset(bytes, 0, sizeof(bytes)); memset(bits, 0, sizeof(bits)); parse_raw_user_string(string, bytes); unpack_byte_array_into_bit_array(bytes, bits, 12); hytera_ota_decrypt_bptc_96(state, bits, 0); fprintf (stderr, "\n"); //VLC memset(string, 0, sizeof(string)); sprintf(string, "%s", "de4998491fe4da822d5d5568"); memset(bytes, 0, sizeof(bytes)); memset(bits, 0, sizeof(bits)); parse_raw_user_string(string, bytes); unpack_byte_array_into_bit_array(bytes, bits, 12); hytera_ota_decrypt_bptc_96(state, bits, 1); fprintf (stderr, "\n"); //TLC memset(string, 0, sizeof(string)); sprintf(string, "%s", "012f658c3f2a70a8d4766a95"); memset(bytes, 0, sizeof(bytes)); memset(bits, 0, sizeof(bits)); parse_raw_user_string(string, bytes); unpack_byte_array_into_bit_array(bytes, bits, 12); hytera_ota_decrypt_bptc_96(state, bits, 2); fprintf (stderr, "\n"); //CSBK memset(string, 0, sizeof(string)); sprintf(string, "%s", "1053505ed9b4ff091b36a5c3"); memset(bytes, 0, sizeof(bytes)); memset(bits, 0, sizeof(bits)); parse_raw_user_string(string, bytes); unpack_byte_array_into_bit_array(bytes, bits, 12); hytera_ota_decrypt_bptc_96(state, bits, 3); fprintf (stderr, "\n"); //data header memset(string, 0, sizeof(string)); sprintf(string, "%s", "cba5da41f9fb252ca859ff89"); memset(bytes, 0, sizeof(bytes)); memset(bits, 0, sizeof(bits)); parse_raw_user_string(string, bytes); unpack_byte_array_into_bit_array(bytes, bits, 12); hytera_ota_decrypt_bptc_96(state, bits, 6); fprintf (stderr, "\n"); //single burst encoding memset(bits, 0, sizeof(bits)); uint16_t sb_hex = 0x14; //key id 0x14 like in sample sb_hex <<= 3; sb_hex |= 0x01; //alg 0x01 (RC4) //05030A03 is key id 1, alg 1 (009) //HOTA SB INT: 05030A03 //HOTA SB DEI: 01310131 //SB: 00000001001 - 009; //Slot 2 DMR LE SB ALG ID: 21; KEY ID: 01; RC4; //debug // sb_hex = 0x009; //load into bit array for (int i = 0; i < 11; i++) bits[i] = (sb_hex >> (10-i)) & 1; //hamming 16_11_4 encode uint8_t encoded[32]; memset(encoded, 0, sizeof(encoded)); Hamming_16_11_4_encode(bits, encoded); //Parity Bit Row (even) --> just duplicate the first 16 for (int i = 0; i < 16; i++) encoded[i+16] = encoded[i]; //NOTE: For RC, just invert each bit instead // for (int i = 0; i < 16; i++) // encoded[i+16] = encoded[i] ^ 1; //placement and interleaving uint8_t interleave[32]; memset(interleave, 0, sizeof(interleave)); const uint8_t sbrc_int[32] = { 0,24,1,25,2,26,3,27,4,28,5,29,6,30,7,31,8, 16,9,17,10,18,11,19,12,20,13,21,14,22,15,23 }; for (int i = 0; i < 32; i++) interleave[i] = encoded[sbrc_int[i]]; fprintf (stderr, "\n"); fprintf (stderr, "EMB RC / SB HEX: %03X;", sb_hex); fprintf (stderr, "\n"); fprintf (stderr, "Hamming Encoded: %04X", convert_bits_into_output(encoded, 16)); fprintf (stderr, "\n"); fprintf (stderr, "Even PB Encoded: %08X", convert_bits_into_output(encoded, 32)); fprintf (stderr, "\n"); fprintf (stderr, "CAI Interleaved: %08X", convert_bits_into_output(interleave, 32)); exitflag = 1; } //Apply KS to the Single Burst or Reverse Channel using indexed values. void hytera_ota_decrypt_sb_rc(dsd_state * state, uint8_t * input) { if (state->hota_key_loaded == 0) return; else if (state->hota_sbrc_idx1[state->currentslot] == -1) return; else if (state->hota_sbrc_idx2[state->currentslot] == -1) return; uint8_t bits[32]; memset(bits, 0, sizeof(bits)); //check for empty field (NULL message), return if empty if (memcmp(bits, input, 32) == 0) return; #ifdef DEBUG_HOTA fprintf (stderr, "\n\n-----SB_RC and FEC Decryption-----"); #endif for (int i = 0; i < 32; i++) bits[i] = input[i] & 1; uint8_t bytes[4]; pack_bit_array_into_byte_array(bits, bytes, 4); #ifdef DEBUG_HOTA fprintf(stderr, "\n"); fprintf(stderr, "HOTA SB Inp: "); for (int i = 0; i < 4; i++) fprintf(stderr, "%02X", bytes[i]); fprintf(stderr, "\n"); #endif static const uint8_t sbrc_int[32] = { 0,24,1,25,2,26,3,27,4,28,5,29,6,30,7,31,8, 16,9,17,10,18,11,19,12,20,13,21,14,22,15,23 }; uint8_t dei[32]; for (int i = 0; i < 32; i++) dei[sbrc_int[i]] = bits[i]; for (int i = 0; i < 32; i++) bits[i] = dei[i]; pack_bit_array_into_byte_array(bits, bytes, 4); #ifdef DEBUG_HOTA fprintf(stderr, "HOTA SB Dei: "); for (int i = 0; i < 4; i++) fprintf(stderr, "%02X", bytes[i]); fprintf(stderr, "\n"); #endif //idx values for KS based on calculation or prior search int idx1 = state->hota_sbrc_idx1[state->currentslot]; int idx2 = state->hota_sbrc_idx2[state->currentslot]; //The index values are always hardset according to test w/ multiple keys uint16_t S = (uint16_t)state->hota_rc4_ks[idx1+0] | ((uint16_t)state->hota_rc4_ks[idx1+1] << 8); uint16_t Q = ((uint16_t)state->hota_rc4_ks[idx2+0] << 8) | (uint16_t)state->hota_rc4_ks[idx2+1]; uint16_t U = ((uint16_t)bytes[0] << 8) | bytes[1]; uint16_t V = ((uint16_t)bytes[2] << 8) | bytes[3]; #ifdef DEBUG_HOTA fprintf(stderr, "HOTA SB UV: U=%04X V=%04X\n", U, V); #endif //apply keystream V ^= S; U ^= Q; #ifdef DEBUG_HOTA fprintf(stderr, "HOTA SB Dec: U=%04X V=%04X idx1=%u idx2=%u S=%04X Q=%04X\n", U, V, idx1, idx2, S, Q); #endif //swap endianness of U and V for unpacking bytes[0] = (uint8_t)(U >> 8); bytes[1] = (uint8_t)(U & 0xFF); bytes[2] = (uint8_t)(V >> 8); bytes[3] = (uint8_t)(V & 0xFF); #ifdef DEBUG_HOTA fprintf(stderr, "HOTA SB Dec: "); for (int i = 0; i < 4; i++) fprintf(stderr, "%02X", bytes[i]); fprintf(stderr, "\n"); #endif unpack_byte_array_into_bit_array(bytes, bits, 4); for (int i = 0; i < 32; i++) input[i] = bits[sbrc_int[i]]; pack_bit_array_into_byte_array(input, bytes, 4); #ifdef DEBUG_HOTA fprintf(stderr, "HOTA SB Out: "); for (int i = 0; i < 4; i++) fprintf(stderr, "%02X", bytes[i]); fprintf(stderr, "\n"); #endif } //UPDATE: So, does appear that this field changes depending on other factors //not key related directly, but could be CTX related, or RID or similar, //code has been updated appropriately to continue the search on this //its possible that idx1 is still always 36, idx2 is variable void hytera_ota_search_sb_rc(dsd_state * state, uint8_t * input, int ptype) { if (state->hota_key_loaded == 0) return; else if (ptype > 1) return; uint8_t bits[32]; memset (bits, 0, sizeof(bits)); //check for empty field (NULL message), return if empty if (memcmp(bits, input, 32) == 0) return; #ifdef DEBUG_HOTA fprintf (stderr, "\n\n-----SB_RC KS Index Search-----"); #endif static const uint8_t sbrc_int[32] = { 0,24,1,25,2,26,3,27,4,28,5,29,6,30,7,31,8, 16,9,17,10,18,11,19,12,20,13,21,14,22,15,23 }; for (int i = 0; i < 32; i++) bits[sbrc_int[i]] = input[i]; uint8_t bytes[4]; memset(bytes, 0, sizeof(bytes)); pack_bit_array_into_byte_array(bits, bytes, 4); uint16_t S = 0, Q = 0, U = 0, V = 0, Y = 0, Z = 0; uint8_t * K = &state->hota_rc4_ks[0]; U = ((uint16_t)bytes[0] << 8) | bytes[1]; V = ((uint16_t)bytes[2] << 8) | bytes[3]; #ifdef DEBUG_HOTA fprintf (stderr, "\nSB_RC ENC U: %04X; ENC V: %04X; PPI: %d;", U, V, ptype); #endif // for (int i = 0; i < 80; i++) //may can end at 40, or remove loop and hard set to 36 { int i = 36; //hardset i for idx1 that seems to be a constant //Calculate Clear V from S stream bytes S = (K[(i+0) % 80] << 0) | (K[(i+1) % 80] << 8); Y = V ^ S; //invert parity row bits if needed if (ptype) Y = ~Y; for (int j = 0; j < 80; j++) //can be in range of 0 to 80, verified { //calculate Clear U from Q stream bytes Q = (K[(j+0) % 80] << 8) | (K[(j+1) % 80] << 0); Z = U ^ Q; //Y and Z match, proceed if (Y == Z) { //hamming encode on the 11-bit data portion of Clear U (Z) to see if it matches 5 lsb unsigned char hamming[16]; memset(hamming, 0, sizeof(hamming)); unsigned char hammchk[16]; memset(hammchk, 0, sizeof(hammchk)); for (int h = 0; h < 16; h++) hamming[h] = (Z >> (15 - h)) & 1; Hamming_16_11_4_encode(hamming, hammchk); //hamming result is positive, successful search if (memcmp(hamming, hammchk, 16) == 0) { #ifdef DEBUG_HOTA fprintf (stderr, "\nSuccess: IDX1: %d; IDX2: %d; ", i, j); fprintf (stderr, "S: %04X; Q: %04X; CLR U: %04X; CLR V: %04X;", S, Q, Z, Y); // fprintf (stderr, " HE: %04X; HC: %04X;", (uint16_t)convert_bits_into_output(hamming, 16), (uint16_t)convert_bits_into_output(hammchk, 16)); #endif state->hota_sbrc_idx1[state->currentslot] = i; state->hota_sbrc_idx2[state->currentslot] = j; return; } } } } } uint8_t hytera_ep_data_decrypt(dsd_opts * opts, dsd_state * state, uint8_t * input, int len) { //check slot for RC4 Encrypted PDU Alg 0xD0 (0x68 * alg 2) uint8_t slot = state->currentslot; if (slot == 0 && state->payload_algid != 0xD0) return 0; else if (slot == 1 && state->payload_algidR != 0xD0) return 0; //local copy of EP key (RC4 Enhanced) unsigned long long int R = 0; //check to see if a key is loaded by key_id into the array if (R == 0) { uint8_t key_id = 0; if (slot == 0) key_id = state->payload_keyid; else key_id = state->payload_keyidR; R = state->rkey_array[key_id]; } //if R is still zero, check to see if a key is manually loaded if (R == 0) { if (slot == 0) R = state->R; else R = state->RR; } //NOTE: Since data targets are more often individual, loading by tgt //doesn't make since, loading by src makes more sense, but then again //could be 50-50 if a request packet is replied, and that is enc'd //if no key is loaded, return as not decrypted if (R == 0) return 0; unsigned long long int mi = 0; if (slot == 0) mi = state->payload_mi; else mi = state->payload_miR; if (mi == 0) return 0; //TODO: 2000 -> expected size of decryption hytera_enhanced_rc4_setup(opts, state, R, mi, 2000); uint8_t ks_bytes[2000]; memset(ks_bytes, 0, sizeof(ks_bytes)); if (slot == 0) memcpy(ks_bytes, state->ks_octetL, 2000); else memcpy(ks_bytes, state->ks_octetR, 2000); //find current idx for keystream application int bp_idx = (state->data_block_counter[slot] - 1) * len; //apply keystream for (int i = 0; i < len; i++) input[i] ^= ks_bytes[bp_idx++]; //return this is decrypted return 1; } uint8_t hytera_bp_data_decrypt(dsd_state * state, uint8_t * input, int len) { uint8_t slot = state->currentslot; if (slot == 0 && state->payload_algid != 0x68) return 0; else if (slot == 1 && state->payload_algidR != 0x68) return 0; //load Hytera BP Key uint8_t key_loaded = 0; uint8_t key[32]; memset(key, 0, sizeof(key)); uint8_t empt[32]; memset(empt, 0, sizeof(empt)); if (key_loaded == 0) { uint8_t key_id = 0; if (slot == 0) key_id = state->payload_keyid; else key_id = state->payload_keyidR; for (int i = 0; i < 8; i++) { key[i+0] = ((state->rkey_array[key_id+0x000]) >> (56-(i*8))) & 0xFF; key[i+8] = ((state->rkey_array[key_id+0x101]) >> (56-(i*8))) & 0xFF; key[i+16] = ((state->rkey_array[key_id+0x201]) >> (56-(i*8))) & 0xFF; key[i+24] = ((state->rkey_array[key_id+0x301]) >> (56-(i*8))) & 0xFF; } //if key is loaded with a key, then flag on the key loaded variable if (memcmp(key, empt, sizeof(key)) != 0) key_loaded = 1; } if (key_loaded == 0) { for (int i = 0; i < 8; i++) { key[i+0] = (state->K1) >> (56-(i*8)) & 0xFF; key[i+8] = (state->K2) >> (56-(i*8)) & 0xFF; key[i+16] = (state->K3) >> (56-(i*8)) & 0xFF; key[i+24] = (state->K4) >> (56-(i*8)) & 0xFF; } //if key is loaded with a key, then flag on the key loaded variable if (memcmp(key, empt, sizeof(key)) != 0) key_loaded = 1; } //NOTE: Since data targets are more often individual, loading by tgt //doesn't make since, loading by src makes more sense, but then again //could be 50-50 if a request packet is replied, and that is enc'd //if no key is loaded, return as not decrypted if (key_loaded == 0) return 0; uint8_t key_bytes[64]; memset (key_bytes, 0, sizeof(key_bytes)); //Find len on loaded Hytera BP key w/ memcmp on key and load into key_bytes int key_len = 32; //TODO: We are assumming the key isn't something like 0x100000000000000000000000000000.... zero fill to 16 or 32 //need to fix this checkdown, but may not be plausible without redoing the Hytera BP code entirely at this point //test for 64 char (32 byte) key if ((memcmp(empt, key+16, 8) != 0) || (memcmp(empt, key+24, 8) != 0)) { memcpy(key_bytes, key, 32); key_len = 32; } //test for 32 char (16 byte) key else if ((memcmp(empt, key+8, 8) != 0)) { memcpy(key_bytes, key, 16); key_len = 16; } //this is a 10 char (5 byte) key w/ offset else { memcpy(key_bytes, key+3, 5); key_len = 5; } //find current idx for keystream application int bp_idx = (state->data_block_counter[slot] - 1) * len; //apply keystream for (int i = 0; i < len; i++) input[i] ^= key_bytes[bp_idx++ % key_len]; //return this is decrypted return 1; }