From f2254f413a3b97e1644de7c22a88d44501581583 Mon Sep 17 00:00:00 2001 From: lwvmobile Date: Fri, 4 Sep 2026 15:47:56 -0400 Subject: [PATCH] Vertex Standard 10 Char and 64 Char Encryption Support; #316 Special mode of 0:0 to apply the inversion to the interlevaed frame if present without application of a keystream for analysis; Misc Tweaks to VTX STD ENC Header and VTX TMS message; TG Value loading of VTX 10 char keys available only by #define VERTEX_KEY40_BY_TG which will override the Moto BP loadout; --- .../cygwin_bat/complete_usage_options.txt | 7 +- include/dsd.h | 15 ++- src/crypt-aes.c | 52 +++++++++ src/crypt-etc.c | 106 ++++++++++++++++++ src/crypt-rc4.c | 91 +++++++++++++++ src/crypt-tyt.c | 52 --------- src/dmr_block.c | 71 +++++++++--- src/dmr_bs.c | 14 +++ src/dmr_ms.c | 14 +++ src/dmr_pdu.c | 23 ++-- src/dmr_utils.c | 40 +++++++ src/dsd_audio2.c | 16 +++ src/dsd_main.c | 47 +++++--- src/dsd_mbe.c | 42 +++++++ 14 files changed, 492 insertions(+), 98 deletions(-) diff --git a/examples/cygwin_bat/complete_usage_options.txt b/examples/cygwin_bat/complete_usage_options.txt index c931075..43144e6 100644 --- a/examples/cygwin_bat/complete_usage_options.txt +++ b/examples/cygwin_bat/complete_usage_options.txt @@ -1,4 +1,4 @@ -Build Version: AW 2026-37-g198f0ea +Build Version: AW 2026-39-gc97400a MBElib Version: 1.3.4 CODEC2 Support Enabled @@ -100,7 +100,6 @@ Decoder options: -fE Decode only EDACS EA/ProVoice with ESK 0xA0* -fm Decode only dPMR* -l Disable DMR, dPMR, NXDN, M17 input filtering - -u Unvoiced speech quality (default=3) -xx Expect non-inverted X2-TDMA signal -xr Expect inverted DMR signal -xd Expect inverted ICOM dPMR signal @@ -183,6 +182,10 @@ Advanced Decoder options: -, Manually Enter and Enforce Auctus A6 (GoComm) 8 Char Key Value (DMR) (String Value) -, Secure04 + -u Manually Enter and Enforce Vertex Standard 10 or 64 Char Hex Key + -u 10:1234567891 or 64:AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDD + -u 0:0 no KS application, but apply the inversion to the interleave (clear errors on 256) + -S Manually Enter and Enforce Generic Static Keystream -> Length and BYTE PACKED / ALIGNED String for AMBE (up to 882 bits) For Example, enter 16-bit Keystream 0909 as: -S 16:0909 diff --git a/include/dsd.h b/include/dsd.h index a6e32f7..2d8ddc0 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -649,6 +649,14 @@ typedef struct //xl specific, we need to know if the ESS is from HDU, or from LDU2 int xl_is_hdu; + //Vertex Standard Keys + // #define VERTEX_KEY40_BY_TG + // #define VERTEX_KEY256_BY_TG + char vtx256_key[64]; + unsigned long long int vtx40_key; + unsigned long long int vtx256_ekey; + uint8_t vtx_key_loaded; + unsigned int debug_audio_errors; unsigned int debug_audio_errorsR; unsigned int debug_header_errors; @@ -1383,6 +1391,7 @@ uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint8_t ComputeCrc5Bit(uint8_t * DMRData); uint16_t ComputeCrc9Bit(uint8_t * DMRData, uint32_t NbData); uint32_t ComputeCrc32Bit(uint8_t * DMRData, uint32_t NbData); +uint32_t ComputeCrc32BitV(uint8_t * DMRData, uint32_t NbData); //new simplified dmr functions void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst); @@ -1646,7 +1655,7 @@ 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 / Retevis / Anytone / Kenwood / Misc DMR Encryption Modes +//Tytera / Retevis / Anytone / Kenwood / Vertex / Baofeng / 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); @@ -1657,6 +1666,10 @@ void baofeng_ap_pc5_keystream_creation(dsd_state *state, char *input); void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24]); void straight_mod_xor_keystream_creation(dsd_state * state, char * input); void auctus_keystream_creation(dsd_state * state, char * input); +void vertex_keystream_setup(dsd_state * state, char * input); +void vertex_40_keystream_creation(dsd_state * state, unsigned long long int key_value); +void vertex_256_keystream_creation(dsd_state * state, char * key_string); +void vtx256_ambe2_interleave_inversion(char ambe_fr[4][24]); //Kirisun uint32_t kirisun_lfsr(unsigned long long int mi); diff --git a/src/crypt-aes.c b/src/crypt-aes.c index d805315..46a436a 100644 --- a/src/crypt-aes.c +++ b/src/crypt-aes.c @@ -39,6 +39,7 @@ #include #include #include "dsd.h" +#include "bp.h" #define AES_BLOCKLEN 16 unsigned Nb = 4; @@ -510,6 +511,57 @@ void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) } +//Vertex Standard "AES" Encryption - convert AES key into a round key at 256 Nb,Nk,Nr values +//then select entries from the BP table to invert and convert to a keystream +void vertex_256_keystream_creation(dsd_state * state, char * key_string) +{ + + Nb = 4; + Nk = 8; + Nr = 14; + + uint8_t key[32]; + memset (key, 0, sizeof(key)); + + parse_raw_user_string(key_string, key); + + struct AES_ctx ctx; + KeyExpansion(ctx.RoundKey, key); + + uint64_t vtx_key = 0; + + //use the bytes from rc4 output to select 4 BP key 16-bit words + for (int i = 224; i < 228; i++) + { + vtx_key <<= 16; + vtx_key |= BPK[ctx.RoundKey[i]]; + } + + //invert value + vtx_key = ~vtx_key; + + fprintf (stderr,"Vertex Round Key Bytes: "); + for (uint16_t i = 224; i < 228; i++) + fprintf (stderr, "%02X ", ctx.RoundKey[i]); + fprintf (stderr, "Vertex Keystream: %016llX;", (unsigned long long int)vtx_key); + fprintf (stderr, "\n"); + + 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); + + //hex representation of the effective keystream value + state->vtx256_ekey = convert_bits_into_output(state->static_ks_bits[0], 64); + + state->vtx_key_loaded = 1; + +} + //byte-wise output of AES OFB Keystream //input iv is a 16-byte uint8_t array of initialization vector //input key is up to 32-byte uint8_t array of key value diff --git a/src/crypt-etc.c b/src/crypt-etc.c index e108c6b..835b469 100644 --- a/src/crypt-etc.c +++ b/src/crypt-etc.c @@ -1,4 +1,5 @@ #include "dsd.h" +#include "dmr_const.h" void ken_dmr_scrambler_keystream_creation(dsd_state * state, char * input) { @@ -123,4 +124,109 @@ void straight_mod_xor_keystream_creation(dsd_state * state, char * input) if (curr == NULL) fprintf (stderr, "Straight KS String Malformed! No KS Created!\n"); +} + +//connect systems 72-bit (9-byte) Extended Encryption //TODO: Move this later +void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24]) +{ + + char interleaved[72]; + memset (interleaved, 0, sizeof(interleaved)); + + //interleave the frame + const int *w, *x, *y, *z; + w = rW; x = rX; y = rY; z = rZ; + + for (int8_t i = 0; i < 36; i++) + { + interleaved[(i*2)+0] = ambe_fr[*w][*x]; + interleaved[(i*2)+1] = ambe_fr[*y][*z]; + + w++; + x++; + y++; + z++; + } + + uint8_t ks_bytes[9]; memset(ks_bytes, 0, sizeof(ks_bytes)); + uint8_t ks[72]; memset(ks, 0, sizeof(ks)); + + //keys are loaded in reverse byte order, stored in state->static_ks_bits[0] + for (int i = 0; i < 9; i++) + ks_bytes[i] = state->static_ks_bits[0][8-i]; + + //convert byte array into a bit array + unpack_byte_array_into_bit_array(ks_bytes, ks, 9); + + //apply keystream to interleave + for (int8_t i = 0; i < 72; i++) + interleaved[i] ^= ks[71-i]; + + //deinterleave back into ambe_fr frame + w = rW; x = rX; y = rY; z = rZ; + int k = 0; + for (int8_t i = 0; i < 36; i++) + { + ambe_fr[*w][*x] = interleaved[k++]; + ambe_fr[*y][*z] = interleaved[k++]; + + w++; + x++; + y++; + z++; + } + +} + +void vtx256_ambe2_interleave_inversion(char ambe_fr[4][24]) +{ + + uint32_t silence_check = 0; + for (int i = 0; i < 20; i++) + { + silence_check <<= 1; + silence_check |= (uint8_t)ambe_fr[0][23-i]; + } + + if (silence_check == 0xF8014) + goto END_INV; + + char interleaved[72]; + memset (interleaved, 0, sizeof(interleaved)); + + //interleave the frame + const int *w, *x, *y, *z; + w = rW; x = rX; y = rY; z = rZ; + + for (int8_t i = 0; i < 36; i++) + { + interleaved[(i*2)+0] = ambe_fr[*w][*x]; + interleaved[(i*2)+1] = ambe_fr[*y][*z]; + + w++; + x++; + y++; + z++; + } + + //invert the bits in the 49 to 71 position + for (int8_t i = 49; i < 72; i++) + interleaved[i] ^= 1; + + //deinterleave back into ambe_fr frame + w = rW; x = rX; y = rY; z = rZ; + int k = 0; + for (int8_t i = 0; i < 36; i++) + { + ambe_fr[*w][*x] = interleaved[k++]; + ambe_fr[*y][*z] = interleaved[k++]; + + w++; + x++; + y++; + z++; + } + + END_INV: ; //do nothing + } \ No newline at end of file diff --git a/src/crypt-rc4.c b/src/crypt-rc4.c index aa9acdf..03cbf7e 100644 --- a/src/crypt-rc4.c +++ b/src/crypt-rc4.c @@ -4,6 +4,7 @@ *-----------------------------------------------------------------------------*/ #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[]) @@ -245,3 +246,93 @@ void auctus_keystream_creation(dsd_state * state, char * input) 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]]; + } + + 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"); + + 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); + vertex_40_keystream_creation(state, state->vtx40_key); + } + + else if (len == 64) + { + memcpy(state->vtx256_key, curr, 64); + 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 + +} diff --git a/src/crypt-tyt.c b/src/crypt-tyt.c index 93993a7..89501c3 100644 --- a/src/crypt-tyt.c +++ b/src/crypt-tyt.c @@ -307,55 +307,3 @@ void tyt_ep_aes_keystream_creation(dsd_state * state, char * input) state->tyt_ep = 1; } - -//connect systems 72-bit (9-byte) Extended Encryption //TODO: Move this later -void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24]) -{ - - char interleaved[72]; - memset (interleaved, 0, sizeof(interleaved)); - - //interleave the frame - const int *w, *x, *y, *z; - w = rW; x = rX; y = rY; z = rZ; - - for (int8_t i = 0; i < 36; i++) - { - interleaved[(i*2)+0] = ambe_fr[*w][*x]; - interleaved[(i*2)+1] = ambe_fr[*y][*z]; - - w++; - x++; - y++; - z++; - } - - uint8_t ks_bytes[9]; memset(ks_bytes, 0, sizeof(ks_bytes)); - uint8_t ks[72]; memset(ks, 0, sizeof(ks)); - - //keys are loaded in reverse byte order, stored in state->static_ks_bits[0] - for (int i = 0; i < 9; i++) - ks_bytes[i] = state->static_ks_bits[0][8-i]; - - //convert byte array into a bit array - unpack_byte_array_into_bit_array(ks_bytes, ks, 9); - - //apply keystream to interleave - for (int8_t i = 0; i < 72; i++) - interleaved[i] ^= ks[71-i]; - - //deinterleave back into ambe_fr frame - w = rW; x = rX; y = rY; z = rZ; - int k = 0; - for (int8_t i = 0; i < 36; i++) - { - ambe_fr[*w][*x] = interleaved[k++]; - ambe_fr[*y][*z] = interleaved[k++]; - - w++; - x++; - y++; - z++; - } - -} diff --git a/src/dmr_block.c b/src/dmr_block.c index ad9d8fe..baf732b 100644 --- a/src/dmr_block.c +++ b/src/dmr_block.c @@ -187,6 +187,7 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t else if (p_mfid == 0x68) sprintf (mfid_string, "%s", "Hytera"); else if (p_mfid == 0x08) sprintf (mfid_string, "%s", "Hytera"); else if (p_mfid == 0x06) sprintf (mfid_string, "%s", "Trid/Mot"); + else if (p_mfid == 0x77) sprintf (mfid_string, "%s", "VTX STD"); else if (p_mfid == 0x00) sprintf (mfid_string, "%s", "Standard"); else sprintf (mfid_string, "%s", "Other"); } @@ -378,34 +379,25 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t if (p_sap != 1 && p_mfid == 0x77) { - fprintf (stderr, "\n Vertex Standard PDU ENC Header:"); - fprintf (stderr, " MFID: %02X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 8)); + fprintf (stderr, "\n Vertex Standard PDU ENC Header;"); - //Unsure of how similar this is to the DMRA Enc Header, but using - //guess work and best judgment from single sample of Vertex Enhanced + //This Extended Header only seems to exist to signal encryption, + //but is otherwise mostly empty aside from a FID and single 0x01 value + //[4F][77][01][00][00][00][00][00][00][00][XX][XX] - //main issue is, I don't know if the 0x01 signalled is a key id (which is known to be 0x01) - //or if it is an encryption alg id (alg may have to be inferred based on what is loaded in radio) + //Further analysis shows that using different key types on different channels + //does not change the byte values of this header, and what is reported is that + //the Vertex Standard radios only allow one encryption type and key per channel. //set to 0x100 so it won't trigger any weird flags, but still has a non-zero value to be checked later if (state->currentslot == 0) state->dmr_so = 0x100; else state->dmr_soR = 0x100; - if (state->currentslot == 0) - state->payload_keyid = (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8); - else state->payload_keyidR = (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8); - fprintf (stderr, " Key ID: %02X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8)); - //going to use algid of 0x7 to signal a vertex standard enc method if (state->currentslot == 0) state->payload_algid = 0x7; else state->payload_algidR = 0x7; - //unknown if MI, or IV is present on this header (need the AES256 Vertex PDU to compare) - if (state->currentslot == 0) - state->payload_mi = (unsigned long long int)ConvertBitIntoBytes(&dheader_bits[48], 32); - else state->payload_miR = (unsigned long long int)ConvertBitIntoBytes(&dheader_bits[48], 32); - //reset ks start value state->data_ks_start[slot] = 0; @@ -1115,6 +1107,53 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte decrypted_pdu = 1; } + else if (alg == 7 && state->vtx_key_loaded == 1) //Vertex Standard, either enc type + { + pack_bit_array_into_byte_array(state->static_ks_bits[slot], ob, 8); + end += (poc + 4); //also perform application over padding bytes and crc value + for (i = 0; i < end; i++) + state->dmr_pdu_sf[slot][i+start] ^= ob[i%8]; + decrypted_pdu = 1; + + //Recalcuate CRC32 + uint8_t reordered_bits[1000]; + memset (reordered_bits, 0, sizeof(reordered_bits)); + + //rearrage for ridiculously stupid CRC32 LSO/MSO ordering + for(i = 0, j = 0; i < ctr; i+=2, j+=16) + { + reordered_bits[j + 0] = (state->dmr_pdu_sf[slot][i+1] >> 7) & 0x01; + reordered_bits[j + 1] = (state->dmr_pdu_sf[slot][i+1] >> 6) & 0x01; + reordered_bits[j + 2] = (state->dmr_pdu_sf[slot][i+1] >> 5) & 0x01; + reordered_bits[j + 3] = (state->dmr_pdu_sf[slot][i+1] >> 4) & 0x01; + reordered_bits[j + 4] = (state->dmr_pdu_sf[slot][i+1] >> 3) & 0x01; + reordered_bits[j + 5] = (state->dmr_pdu_sf[slot][i+1] >> 2) & 0x01; + reordered_bits[j + 6] = (state->dmr_pdu_sf[slot][i+1] >> 1) & 0x01; + reordered_bits[j + 7] = (state->dmr_pdu_sf[slot][i+1] >> 0) & 0x01; + + reordered_bits[j + 8] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01; + reordered_bits[j + 9] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01; + reordered_bits[j + 10] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01; + reordered_bits[j + 11] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01; + reordered_bits[j + 12] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01; + reordered_bits[j + 13] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01; + reordered_bits[j + 14] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01; + reordered_bits[j + 15] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01; + } + CRCComputed = (uint32_t)ComputeCrc32BitV(reordered_bits, (ctr * 8) - 32); + CRCExtracted = (uint32_t)convert_bits_into_output(reordered_bits+((ctr * 8) - 32), 32); + if (CRCComputed == CRCExtracted) + CRCCorrect = 1; + else CRCCorrect = 0; + + //set back to original value + end -= (poc + 4); + + //debug + // fprintf (stderr, " CTR: %d; CRC32 CMP: %08X EXT: %08X", ctr, CRCComputed, CRCExtracted); + + } + else if (alg == 5 && akl == 1 && mi != 0) //AES-256 OFB { int nblocks = (state->data_byte_ctr[slot] / 16) + 1; diff --git a/src/dmr_bs.c b/src/dmr_bs.c index a8e2f59..ce26c27 100644 --- a/src/dmr_bs.c +++ b/src/dmr_bs.c @@ -509,6 +509,13 @@ void dmrBS (dsd_opts * opts, dsd_state * state) csi72_ambe2_codeword_keystream(state, ambe_fr3); } + if (state->vtx256_ekey != 0) + { + vtx256_ambe2_interleave_inversion(ambe_fr); + vtx256_ambe2_interleave_inversion(ambe_fr2); + vtx256_ambe2_interleave_inversion(ambe_fr3); + } + #ifdef PRINT_AMBE72 ambe2_codeword_print_i(opts, ambe_fr); ambe2_codeword_print_i(opts, ambe_fr2); @@ -968,6 +975,13 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state) csi72_ambe2_codeword_keystream(state, ambe_fr3); } + if (state->vtx256_ekey != 0) + { + vtx256_ambe2_interleave_inversion(ambe_fr); + vtx256_ambe2_interleave_inversion(ambe_fr2); + vtx256_ambe2_interleave_inversion(ambe_fr3); + } + #ifdef PRINT_AMBE72 ambe2_codeword_print_i(opts, ambe_fr); ambe2_codeword_print_i(opts, ambe_fr2); diff --git a/src/dmr_ms.c b/src/dmr_ms.c index c4021cf..e22a3a3 100644 --- a/src/dmr_ms.c +++ b/src/dmr_ms.c @@ -270,6 +270,13 @@ void dmrMS (dsd_opts * opts, dsd_state * state) csi72_ambe2_codeword_keystream(state, ambe_fr3); } + if (state->vtx256_ekey != 0) + { + vtx256_ambe2_interleave_inversion(ambe_fr); + vtx256_ambe2_interleave_inversion(ambe_fr2); + vtx256_ambe2_interleave_inversion(ambe_fr3); + } + #ifdef PRINT_AMBE72 ambe2_codeword_print_i(opts, ambe_fr); ambe2_codeword_print_i(opts, ambe_fr2); @@ -588,6 +595,13 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state) csi72_ambe2_codeword_keystream(state, ambe_fr3); } + if (state->vtx256_ekey != 0) + { + vtx256_ambe2_interleave_inversion(ambe_fr); + vtx256_ambe2_interleave_inversion(ambe_fr2); + vtx256_ambe2_interleave_inversion(ambe_fr3); + } + #ifdef PRINT_AMBE72 ambe2_codeword_print_i(opts, ambe_fr); ambe2_codeword_print_i(opts, ambe_fr2); diff --git a/src/dmr_pdu.c b/src/dmr_pdu.c index 4535ef9..4d8576d 100644 --- a/src/dmr_pdu.c +++ b/src/dmr_pdu.c @@ -486,29 +486,24 @@ void decode_ip_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t * else if (port1 == 5007 && port2 == 5007) { - //get pad amount to see where to stop parsing UTF16 text - int pad = state->data_block_poc[slot]; - - //sanity check - if (len > (50+pad)) - len = len - pad - 50; - fprintf (stderr, "VTX STD TMS; "); - //+28, get 9 bytes for unknown "header" information - //unknown elements 0E0002000000000000, could include a len value or type (utf-16, utf-8, is07?) - //the sample has a len of 8 utf-16 chars, only way to get that is on the 2 if not byte aligned (1000) + int tmslen = input[28]; + + //+29, get 8 bytes for unknown "header" information + //unknown elements 0002000000000000, could include type (utf-16, utf-8, is07?) + //the sample has a len of 14 utf-16 chars, corresponding to the found 0x0E value if (opts->payload == 1) { - // fprintf (stderr, "Len: %03d; ", len); + fprintf (stderr, "Len: %03d; ", tmslen); fprintf (stderr, "??: "); - for (int i = 0; i < 9; i++) - fprintf (stderr, "%02X", input[i+28]); + for (int i = 0; i < 8; i++) + fprintf (stderr, "%02X", input[i+29]); fprintf (stderr, "; Text: "); } sprintf (state->dmr_lrrp_gps[slot], "VTX TMS SRC: %d; DST: %d; ", src24, dst24); - utf16_to_text(state, 1, len, input+49); + utf16_to_text(state, 1, tmslen*2, input+37); } else if (port1 == 5017 && port2 == 5017) { diff --git a/src/dmr_utils.c b/src/dmr_utils.c index c9a9aa2..5fa9083 100644 --- a/src/dmr_utils.c +++ b/src/dmr_utils.c @@ -438,3 +438,43 @@ uint32_t ComputeCrc32Bit(uint8_t * DMRData, uint32_t NbData) /* Return the CRC */ return CRC; } /* End ComputeCrc32Bit() */ + +uint32_t ComputeCrc32BitV(uint8_t * DMRData, uint32_t NbData) +{ + uint32_t i; + uint32_t CRC = 0x00000000; /* Initialization value = 0x00000000 */ + /* Polynomial x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 + * Normal = 0x04C11DB7 + * Reversed = 0xEDB88320 + * Reciprocal = 0xDB710641 + * Reversed reciprocal = 0x82608EDB */ + uint32_t Polynome = 0x04C11DB7; + for(i = 0; i < NbData; i++) + { + if(((CRC >> 31) & 1) ^ (DMRData[i] & 1)) + { + CRC = (CRC << 1) ^ Polynome; + } + else + { + CRC <<= 1; + } + } + + //Alternate Return Configuration for Vertex + //Encrypted PDUs that have already been reordered once before + //CRC32 CMP: 1622377A EXT: 377A1622 + uint32_t a, b, c, d; + a = b = c = d = 0; + + a = ((CRC >> 0) & 0xFF) << 16; + b = ((CRC >> 8) & 0xFF) << 24; + c = ((CRC >> 16) & 0xFF) << 0; + d = ((CRC >> 24) & 0xFF) << 8; + + CRC = a + b + c + d; + + /* Return the CRC */ + return CRC; +} /* End ComputeCrc32BitV() */ + diff --git a/src/dsd_audio2.c b/src/dsd_audio2.c index c00300f..4f0402b 100644 --- a/src/dsd_audio2.c +++ b/src/dsd_audio2.c @@ -92,6 +92,10 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state) { encL = 0; } + else if (state->vtx_key_loaded == 1) + { + encL = 0; + } } else if (state->payload_algid == 0x02 || state->payload_algid == 0x21 || state->payload_algid == 0x22) { @@ -118,6 +122,10 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state) { encR = 0; } + else if (state->vtx_key_loaded == 1) + { + encR = 0; + } } else if (state->payload_algidR == 0x02 ||state->payload_algidR == 0x21 || state->payload_algidR == 0x22) { @@ -1123,6 +1131,10 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state) { encL = 0; } + else if (state->vtx_key_loaded == 1) + { + encL = 0; + } } else if (state->payload_algid == 0x02 || state->payload_algid == 0x21 || state->payload_algid == 0x22) { @@ -1149,6 +1161,10 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state) { encR = 0; } + else if (state->vtx_key_loaded == 1) + { + encR = 0; + } } else if (state->payload_algidR == 0x02 ||state->payload_algidR == 0x21 || state->payload_algidR == 0x22) { diff --git a/src/dsd_main.c b/src/dsd_main.c index 6ac88fd..e11d3d3 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -437,6 +437,11 @@ state->K2 = 0; state->K3 = 0; state->K4 = 0; + //vertex standard keys + // memset(state->vtx256_key, 0, sizeof(state->vtx256_key)); + state->vtx40_key = 0; + state->vtx256_ekey = 0; + state->vtx_key_loaded = 0; memset (state->A1, 0, sizeof(state->A1)); memset (state->A2, 0, sizeof(state->A2)); memset (state->A3, 0, sizeof(state->A3)); @@ -1093,6 +1098,12 @@ //xl specific, we need to know if the ESS is from HDU, or from LDU2 state->xl_is_hdu = 0; + + //vertex standard keys + memset(state->vtx256_key, 0, sizeof(state->vtx256_key)); + state->vtx40_key = 0; + state->vtx256_ekey = 0; + state->vtx_key_loaded = 0; //NXDN, when a new IV has arrived state->nxdn_new_iv = 0; @@ -1497,7 +1508,7 @@ printf (" -fE Decode only EDACS EA/ProVoice with ESK 0xA0*\n"); printf (" -fm Decode only dPMR*\n"); printf (" -l Disable DMR, dPMR, NXDN, M17 input filtering\n"); - printf (" -u Unvoiced speech quality (default=3)\n"); + // printf (" -u Unvoiced speech quality (default=3)\n"); printf (" -xx Expect non-inverted X2-TDMA signal\n"); printf (" -xr Expect inverted DMR signal\n"); printf (" -xd Expect inverted ICOM dPMR signal\n"); @@ -1585,6 +1596,10 @@ printf (" -, Manually Enter and Enforce Auctus A6 (GoComm) 8 Char Key Value (DMR) (String Value) \n"); printf (" -, Secure04\n"); printf (" \n"); + printf (" -u Manually Enter and Enforce Vertex Standard 10 or 64 Char Hex Key \n"); + printf (" -u 10:1234567891 or 64:AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDD\n"); + printf (" -u 0:0 no KS application, but apply the inversion to the interleave (clear errors on 256)\n"); + printf (" \n"); printf (" -S Manually Enter and Enforce Generic Static Keystream -> Length and BYTE PACKED / ALIGNED String for AMBE (up to 882 bits)\n"); printf (" For Example, enter 16-bit Keystream 0909 as:\n"); printf (" -S 16:0909\n"); @@ -2049,6 +2064,12 @@ fprintf (stderr,"DMR RAS 56-bit Effective Key: %llX\n", state.ras_effective_key); break; + //Vertex Standard KS Generation + case 'u': + vertex_keystream_setup(&state, optarg); + state.keyloader = 0; + break; + //Straight KS Generation case 'S': straight_mod_xor_keystream_creation(&state, optarg); @@ -3106,18 +3127,18 @@ fprintf (stderr,"Enabling 6000 sps P25p2 all optimizations.\n"); } break; - case 'u': - sscanf (optarg, "%i", &opts.uvquality); - if (opts.uvquality < 1) - { - opts.uvquality = 1; - } - else if (opts.uvquality > 64) - { - opts.uvquality = 64; - } - fprintf (stderr,"Setting unvoice speech quality to %i waves per band.\n", opts.uvquality); - break; + // case 'u': + // sscanf (optarg, "%i", &opts.uvquality); + // if (opts.uvquality < 1) + // { + // opts.uvquality = 1; + // } + // else if (opts.uvquality > 64) + // { + // opts.uvquality = 64; + // } + // fprintf (stderr,"Setting unvoice speech quality to %i waves per band.\n", opts.uvquality); + // break; case 'x': if (optarg[0] == 'x') { diff --git a/src/dsd_mbe.c b/src/dsd_mbe.c index 658cf8c..0110f19 100644 --- a/src/dsd_mbe.c +++ b/src/dsd_mbe.c @@ -599,7 +599,14 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a } if (state->rkey_array[hash] != 0) { + #ifdef VERTEX_KEY40_BY_TG + state->vtx40_key = state->rkey_array[hash] & 0xFFFFFFFFFF; + vertex_40_keystream_creation(state, state->vtx40_key); + #elif VERTEX_KEY256_BY_TG + //TODO: This + #else state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255 + #endif state->K1 = state->H = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit opts->dmr_mute_encL = 0; // fprintf (stderr, "Key: %X ", state->rkey_array[hash]); @@ -619,6 +626,20 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a } } + if ( (state->vtx_key_loaded != 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) || + (state->vtx_key_loaded != 0 && state->forced_alg_id == 1) ) + { + if (memcmp(ambe_d, ambe_silence, 49) != 0) + { + for (int i = 0; i < 49; i++) + { + if (i < 8 || i > 11) + ambe_d[i] ^= (char)state->static_ks_bits[0][i]; + } + } + opts->dmr_mute_encL = 0; //shim to unmute + } + if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) || (state->K1 > 0 && state->forced_alg_id == 1) ) { @@ -1066,7 +1087,14 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a } if (state->rkey_array[hash] != 0) { + #ifdef VERTEX_KEY40_BY_TG + state->vtx40_key = state->rkey_array[hash] & 0xFFFFFFFFFF; + vertex_40_keystream_creation(state, state->vtx40_key); + #elif VERTEX_KEY256_BY_TG + //TODO: This + #else state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255 + #endif state->K1 = state->H = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit opts->dmr_mute_encR = 0; // fprintf (stderr, "Key: %X ", state->rkey_array[hash]); @@ -1086,6 +1114,20 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a } } + if ( (state->vtx_key_loaded != 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) || + (state->vtx_key_loaded != 0 && state->forced_alg_id == 1) ) + { + if (memcmp(ambe_d, ambe_silence, 49) != 0) + { + for (int i = 0; i < 49; i++) + { + if (i < 8 || i > 11) + ambe_d[i] ^= (char)state->static_ks_bits[1][i]; + } + } + opts->dmr_mute_encR = 0; //shim to unmute + } + if ( (state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) || (state->K1 > 0 && state->forced_alg_id == 1)) {