From eb76ea20399a76a0d19a36dd162cbd7a4111eb2a Mon Sep 17 00:00:00 2001 From: lwvmobile Date: Fri, 31 Oct 2025 09:41:10 -0400 Subject: [PATCH] DMR: Add -M option to allow forcing any algid value; Remove Setting from DMR_LE and place in MBE section instead; Change state->M to state->forced_alg_id to make it easier to find later; --- .../cygwin_bat/complete_usage_options.txt | 6 ++-- include/dsd.h | 2 +- src/dmr_le.c | 21 ++------------ src/dsd_main.c | 17 +++++++---- src/dsd_mbe.c | 28 ++++++++++++++----- src/dsd_ncurses_handler.c | 8 +++--- src/dsd_ncurses_menu.c | 12 ++++---- src/dsd_ncurses_printer.c | 6 ++-- src/nxdn_deperm.c | 12 ++++---- src/nxdn_element.c | 12 ++++---- src/nxdn_frame.c | 6 ++-- 11 files changed, 68 insertions(+), 62 deletions(-) diff --git a/examples/cygwin_bat/complete_usage_options.txt b/examples/cygwin_bat/complete_usage_options.txt index 77ed9ab..1038c27 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-207-gf8521fd +Build Version: AW 2025-209-g14edf32 MBElib Version: 1.3.4 CODEC2 Support Enabled @@ -127,7 +127,7 @@ Advanced Decoder options: Enabling on some systems could lead to bad channel assignments/site data decoding if bad or marginal signal -b Manually Enter Basic Privacy Key (Decimal Value of Key Number) - (NOTE: This used to be the 'K' option! + (NOTE: This used to be the 'K' option!) -H Manually Enter Hytera 10/32/64 Char Basic Privacy Hex Key (see example below) Encapulate in Single Quotation Marks; Space every 16 chars. @@ -193,6 +193,8 @@ Advanced Decoder options: -0 Force RC4 Key over Missing PI header/LE Encryption Identifiers (DMR) + -M Force Entered Alg ID Value over Missing PI header/LE Encryption Identifiers (DMR) + -3 Disable DMR Late Entry Encryption Identifiers (VC6 Single Burst) Note: Disable this if false positives on Voice ENC occur. diff --git a/include/dsd.h b/include/dsd.h index 7ae09a6..359f48d 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -632,7 +632,7 @@ typedef struct unsigned long long int K2; unsigned long long int K3; unsigned long long int K4; - int M; + uint8_t forced_alg_id; int menuopen; //AES Key Segments diff --git a/src/dmr_le.c b/src/dmr_le.c index d504080..2789930 100644 --- a/src/dmr_le.c +++ b/src/dmr_le.c @@ -14,23 +14,6 @@ void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, uint8_t slot = state->currentslot; - //enforce RC4 due to missing PI header, but with valid SVC Opts - //ideally, this would be handled by VC-F single burst, but its not fully reliable compared to this - //due to multiple signalling occurring inside of it, depending on system type - if (state->M == 0x21) - { - if (slot == 0 && state->dmr_so & 0x40) - { - state->payload_algid = 0x21; - state->payload_keyid = 0xFF; - } - if (slot == 1 && state->dmr_soR & 0x40) - { - state->payload_algidR = 0x21; - state->payload_keyidR = 0xFF; - } - } - //collect our fragments and place them into storage state->late_entry_mi_fragment[slot][vc][0] = (uint64_t)ConvertBitIntoBytes(&ambe_fr[3][0], 4); state->late_entry_mi_fragment[slot][vc][1] = (uint64_t)ConvertBitIntoBytes(&ambe_fr2[3][0], 4); @@ -535,7 +518,7 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power) if (state->dmr_so & 0x40 && key != 0 && alg != 0) { //if we aren't forcing a particular alg or privacy key set - if (state->M == 0) + if (state->forced_alg_id == 0) { fprintf (stderr, "\n"); fprintf (stderr, "%s", KCYN); @@ -561,7 +544,7 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power) if (state->dmr_soR & 0x40 && key != 0 && alg != 0) { //if we aren't forcing a particular alg or privacy key set - if (state->M == 0) + if (state->forced_alg_id == 0) { fprintf (stderr, "\n"); fprintf (stderr, "%s", KCYN); diff --git a/src/dsd_main.c b/src/dsd_main.c index f7ecc87..03a4277 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -1034,7 +1034,7 @@ state->K2 = 0; state->K3 = 0; state->K4 = 0; - state->M = 0; //force key priority over settings from fid/so + state->forced_alg_id = 0; //force key priority over settings from fid/so state->dmr_stereo = 0; //1, or 0? state->dmrburstL = 17; //initialize at higher value than possible @@ -1514,7 +1514,7 @@ printf (" Enabling on some systems could lead to bad channel assignments/site data decoding if bad or marginal signal\n"); printf ("\n"); printf (" -b Manually Enter Basic Privacy Key (Decimal Value of Key Number)\n"); - printf (" (NOTE: This used to be the 'K' option! \n"); + printf (" (NOTE: This used to be the 'K' option!) \n"); printf ("\n"); printf (" -H Manually Enter Hytera 10/32/64 Char Basic Privacy Hex Key (see example below)\n"); printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n"); @@ -1580,6 +1580,8 @@ printf (" \n"); printf (" -0 Force RC4 Key over Missing PI header/LE Encryption Identifiers (DMR) \n"); printf (" \n"); + printf (" -M Force Entered Alg ID Value over Missing PI header/LE Encryption Identifiers (DMR) \n"); + printf (" \n"); printf (" -3 Disable DMR Late Entry Encryption Identifiers (VC6 Single Burst) \n"); printf (" Note: Disable this if false positives on Voice ENC occur. \n"); printf ("\n"); @@ -1884,7 +1886,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: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:G:D:L:V:U:YK:b:H:X:M:NQ:WrlZTF@:!:01:2:345:6:^:7:8_:9:Ek:I:J:O+:")) != -1) { switch (c) @@ -1934,9 +1936,14 @@ //rc4 enforcement on DMR (due to missing the PI header) case '0': - state.M = 0x21; + state.forced_alg_id = 0x21; fprintf (stderr,"Force RC4 Key over Missing PI header/LE Encryption Identifiers (DMR)\n"); break; + + case 'M': + sscanf (optarg, "%hhX", &state.forced_alg_id); + fprintf (stderr,"Force DMR ALG ID 0x%02X over Missing PI header/LE Encryption Identifiers (DMR)\n", state.forced_alg_id); + break; //load single rc4/des key case '1': @@ -2234,7 +2241,7 @@ break; case '4': - state.M = 1; + state.forced_alg_id = 1; fprintf (stderr,"Force Privacy Key over Encryption Identifiers (DMR BP and NXDN Scrambler) \n"); break; diff --git a/src/dsd_mbe.c b/src/dsd_mbe.c index f4a082e..5ecc81b 100644 --- a/src/dsd_mbe.c +++ b/src/dsd_mbe.c @@ -185,6 +185,20 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a char zeroes[49]; memset(zeroes, 0, sizeof(zeroes)); size_t zeroes_threshold = 20; + if (state->forced_alg_id > 1 && state->forced_alg_id != 0x16) //1 and 0x16 is saved for BP stuff, so anything higher than that (Kirisun, requires svc opts set as well) + { + if (state->currentslot == 0 && state->dmr_so & 0x40) + { + state->payload_algid = state->forced_alg_id; + state->payload_keyid = 0xFF; + } + if (state->currentslot == 1 && state->dmr_soR & 0x40) + { + state->payload_algidR = state->forced_alg_id; + state->payload_keyidR = 0xFF; + } + } + //these conditions should ensure no clashing with the BP/HBP/Scrambler key loading machanisms already coded in if (state->currentslot == 0 && state->payload_algid != 0 && state->payload_algid != 0x80 && state->keyloader == 1) keyring (opts, state); @@ -460,7 +474,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a state->errs2 += mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d); if ( (state->nxdn_cipher_type == 0x01 && state->R != 0) || - (state->M == 1 && state->R > 0) ) + (state->forced_alg_id == 1 && state->R > 0) ) { if (state->payload_miN == 0) @@ -568,7 +582,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a //EXPERIMENTAL!! //load basic privacy key number from array by the tg value (if not forced) //currently only Moto BP and Hytera 10 Char BP - if (state->M == 0 && state->payload_algid == 0) + if (state->forced_alg_id == 0 && state->payload_algid == 0) { //see if we need to hash a value larger than 16-bits hash = state->lasttg & 0xFFFFFF; @@ -594,7 +608,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a } if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) || - (state->K > 0 && state->M == 1) ) + (state->K > 0 && state->forced_alg_id == 1) ) { k = BPK[state->K]; k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k ); @@ -606,7 +620,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a } if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) || - (state->K1 > 0 && state->M == 1) ) + (state->K1 > 0 && state->forced_alg_id == 1) ) { int pos = 0; @@ -1030,7 +1044,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a //EXPERIMENTAL!! //load basic privacy key number from array by the tg value (if not forced) //currently only Moto BP and Hytera 10 Char BP - if (state->M == 0 && state->payload_algidR == 0) + if (state->forced_alg_id == 0 && state->payload_algidR == 0) { //see if we need to hash a value larger than 16-bits hash = state->lasttgR & 0xFFFFFF; @@ -1056,7 +1070,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a } if ( (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) || - (state->K > 0 && state->M == 1) ) + (state->K > 0 && state->forced_alg_id == 1) ) { k = BPK[state->K]; k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k ); @@ -1068,7 +1082,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a } if ( (state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) || - (state->K1 > 0 && state->M == 1)) + (state->K1 > 0 && state->forced_alg_id == 1)) { int pos = 0; diff --git a/src/dsd_ncurses_handler.c b/src/dsd_ncurses_handler.c index f7d10bc..bae2815 100644 --- a/src/dsd_ncurses_handler.c +++ b/src/dsd_ncurses_handler.c @@ -211,14 +211,14 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c) if (c == 52) // '4' key, toggle force privacy key over fid and svc (dmr) { - if (state->M == 1 || state->M == 0x21) state->M = 0; - else state->M = 1; + if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21) state->forced_alg_id = 0; + else state->forced_alg_id = 1; } if (c == 54) // '6' key, toggle force rc4 key over missing pi header/late entry { - if (state->M == 1 || state->M == 0x21) state->M = 0; - else state->M = 0x21; + if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21) state->forced_alg_id = 0; + else state->forced_alg_id = 0x21; } if (c == 105) //'i' key, toggle signal inversion on inverted types diff --git a/src/dsd_ncurses_menu.c b/src/dsd_ncurses_menu.c index 2e3b099..8e4b901 100644 --- a/src/dsd_ncurses_menu.c +++ b/src/dsd_ncurses_menu.c @@ -921,11 +921,11 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) //toggle enforcement of basic privacy key over enc bit set on traffic if (option == 4) { - if (state->M == 1 || state->M == 0x21) + if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21) { - state->M = 0; + state->forced_alg_id = 0; } - else state->M = 1; + else state->forced_alg_id = 1; } if (option == 5) @@ -951,11 +951,11 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) //toggle enforcement of rc4 key over missing pi header/le on DMR // if (option == 6) // { - // if (state->M == 1 || state->M == 0x21) + // if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21) // { - // state->M = 0; + // state->forced_alg_id = 0; // } - // else state->M = 0x21; + // else state->forced_alg_id = 0x21; // } //load AES keys diff --git a/src/dsd_ncurses_printer.c b/src/dsd_ncurses_printer.c index 5c945e6..f1384dc 100644 --- a/src/dsd_ncurses_printer.c +++ b/src/dsd_ncurses_printer.c @@ -495,18 +495,18 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } #endif // if (opts->aggressive_framesync == 0) printw ("| Selective CRC ERR Bypass Enabled (RAS) \n"); - if (state->M == 1) + if (state->forced_alg_id == 1) { if (state->R != 0) printw ("| Forcing Key Priority -- NXDN Sc Key: %05lld \n", state->R); if (state->K != 0) printw ("| Forcing Key Priority -- Moto BP Key: %03lld \n", state->K); if (state->K1 != 0) printw ("| Forcing Key Priority -- Hytera BP Key: %016llX \n", state->K1); if (state->K != 0 && state->K1 != 0) printw ("| Warning! Multiple DMR Key Types Loaded! \n"); //warning may not be required } - if (state->M == 0x21) + if (state->forced_alg_id == 0x21) { if (state->R != 0) printw ("| Forcing Key Priority -- RC4 Key: %010llX \n", state->R); } - if (state->M == 0x16) printw ("| Forcing Key Priority -- TYT 16-bit Key: %04llX \n", state->H); + if (state->forced_alg_id == 0x16) printw ("| Forcing Key Priority -- TYT 16-bit Key: %04llX \n", state->H); if (opts->scanner_mode == 1) { diff --git a/src/nxdn_deperm.c b/src/nxdn_deperm.c index e3a2a3e..d1f898b 100644 --- a/src/nxdn_deperm.c +++ b/src/nxdn_deperm.c @@ -303,7 +303,7 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60]) fprintf (stderr, "PF 1/1"); if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed - else if (state->M == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed + else if (state->forced_alg_id == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed if (crc == check) NXDN_Elements_Content_decode(opts, state, 1, nsf_sacch); // else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, nsf_sacch); @@ -361,7 +361,7 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60]) if (part_of_frame == 0) { if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed - else if (state->M == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed + else if (state->forced_alg_id == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed } // if (crc != check) @@ -378,13 +378,13 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60]) if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) { if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed - else if (state->M == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed + else if (state->forced_alg_id == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed } //this seems to work much better now else if (part_of_frame != 0 && state->nxdn_cipher_type == 0x1) { if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed - else if (state->M == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed + else if (state->forced_alg_id == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed //advance seed by required number of turns depending on the current pf value int start = 0; int end = part_of_frame; @@ -587,7 +587,7 @@ void nxdn_deperm_sacch2(dsd_opts * opts, dsd_state * state, uint8_t bits[60]) memcpy(state->dmr_pdu_sf[0]+sf_idx, trellis_buf+bf_idx, sf_size*sizeof(uint8_t)); //if force application of scrambler key, then let's reset, regardless of CRC check - if (sf_fb && state->M == 1) + if (sf_fb && state->forced_alg_id == 1) state->payload_miN = 0; //currently using static values so event log will log something, and do wav files, etc @@ -1323,7 +1323,7 @@ void nxdn_deperm_scch(dsd_opts * opts, dsd_state * state, uint8_t bits[60], uint if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) { if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed - else if (state->M == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed + else if (state->forced_alg_id == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed } /* diff --git a/src/nxdn_element.c b/src/nxdn_element.c index e15bb14..2665b3c 100644 --- a/src/nxdn_element.c +++ b/src/nxdn_element.c @@ -189,7 +189,7 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state, state->nxdn_last_rid = 0; state->nxdn_last_tg = 0; - if (state->M == 0) + if (state->forced_alg_id == 0) state->nxdn_cipher_type = 0; sprintf (state->nxdn_call_type, "%s", ""); @@ -207,7 +207,7 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state, state->nxdn_last_rid = 0; state->nxdn_last_tg = 0; - if (state->M == 0) + if (state->forced_alg_id == 0) state->nxdn_cipher_type = 0; sprintf (state->nxdn_call_type, "%s", ""); #endif @@ -689,7 +689,7 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa fprintf (stderr, " Key Loaded: %lld", state->rkey_array[DestinationID]); state->payload_miN = state->R; //should be okay to load here, will test } - if (state->M == 1) state->nxdn_cipher_type = 0x1; + if (state->forced_alg_id == 1) state->nxdn_cipher_type = 0x1; } //rtl else if (opts->audio_in_type == 3) @@ -737,7 +737,7 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa fprintf (stderr, " Key Loaded: %lld", state->rkey_array[DestinationID]); state->payload_miN = state->R; //should be okay to load here, will test } - if (state->M == 1) state->nxdn_cipher_type = 0x1; + if (state->forced_alg_id == 1) state->nxdn_cipher_type = 0x1; #endif } @@ -1760,7 +1760,7 @@ void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uin //check the rkey array for a scrambler key value //TGT ID and Key ID could clash though if csv or system has both with different keys if (state->rkey_array[id] != 0) state->R = state->rkey_array[id]; - if (state->M == 1) state->nxdn_cipher_type = 0x1; + if (state->forced_alg_id == 1) state->nxdn_cipher_type = 0x1; } //rtl else if (opts->audio_in_type == 3) @@ -1779,7 +1779,7 @@ void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uin //check the rkey array for a scrambler key value //TGT ID and Key ID could clash though if csv or system has both with different keys if (state->rkey_array[id] != 0) state->R = state->rkey_array[id]; - if (state->M == 1) state->nxdn_cipher_type = 0x1; + if (state->forced_alg_id == 1) state->nxdn_cipher_type = 0x1; #endif } diff --git a/src/nxdn_frame.c b/src/nxdn_frame.c index cbdd13c..5e26ce8 100644 --- a/src/nxdn_frame.c +++ b/src/nxdn_frame.c @@ -465,7 +465,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state) if (state->rkey_array[limazulu] != 0) state->R = state->rkey_array[limazulu]; - if (state->R != 0 && state->M == 1) state->nxdn_cipher_type = 0x1; + if (state->R != 0 && state->forced_alg_id == 1) state->nxdn_cipher_type = 0x1; //add additional time to last_sync_time for LimaZulu to hold on current frequency //a little longer without affecting normal scan time on trunk_hangtime variable @@ -521,7 +521,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state) if (voice && facch == 1) //facch steal 1 -- before voice { //force scrambler here, but with unspecified key (just use what's loaded) - if (state->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1; + if (state->forced_alg_id == 1 && state->R != 0) state->nxdn_cipher_type = 0x1; //roll the voice scrambler LFSR here if key available to advance seed -- half rotation on a facch steal if (state->nxdn_cipher_type == 0x1 && state->R != 0) { @@ -580,7 +580,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state) //update last voice sync time state->last_vc_sync_time = time(NULL); //turn on scrambler if forced by user option - if (state->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1; + if (state->forced_alg_id == 1 && state->R != 0) state->nxdn_cipher_type = 0x1; //process voice frame nxdn_voice (opts, state, voice, dbuf); }