diff --git a/.gitignore b/.gitignore index 4110a88..c8daee9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,8 @@ *.dylib build *.sh - - +*.csv +examples *.bin *.ans diff --git a/examples/multi_key.csv b/examples/multi_key.csv new file mode 100644 index 0000000..4f4f2e9 --- /dev/null +++ b/examples/multi_key.csv @@ -0,0 +1,6 @@ +key id or tg id (dec), key number or value (dec) //NXDN, BP, 10-char Hytera BP only +2,70 +1,123 +100,70 +12,48713912656 +672560,254 diff --git a/include/dsd.h b/include/dsd.h index 7a8eb26..2970c32 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -649,7 +649,7 @@ typedef struct uint32_t nxdn_location_sys_code; uint16_t nxdn_location_site_code; - //multi-key array for nxdn keys + //multi-key array unsigned long long int rkey_array[0xFFFF]; //dmr late entry mi diff --git a/src/dmr_csbk.c b/src/dmr_csbk.c index 8408347..c828f9b 100644 --- a/src/dmr_csbk.c +++ b/src/dmr_csbk.c @@ -34,7 +34,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8 if (IrrecoverableErrors == 0) { //Hytera XPT CSBK Check -- if bits 0 and 1 are used as lcss, gi, ts, then this bit may be set on - if (csbk_fid == 0x68 && csbk_o == 0x0A) csbk_pf = 0; + if ( csbk_fid == 0x68 && (csbk_o == 0x0A || csbk_o == 0x0B) ) csbk_pf = 0; if (csbk_pf == 1) //check the protect flag, don't run if set { fprintf (stderr, "%s", KRED); @@ -1163,7 +1163,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8 } //end 0x0A - //XPT Site Information - Adj Site Info + //XPT Site Information - Adj Site Info?? if (csbk_o == 0x0B) { @@ -1171,12 +1171,16 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8 fprintf (stderr, "\n"); fprintf (stderr, "%s", KYEL); - // fprintf (stderr, " Hytera XPT CSBK 0x0B "); + fprintf (stderr, " Hytera XPT CSBK 0x0B "); //wraithe's theory -- seems pretty good to me //still only a theory though until can be verified //by having a system with precisely known variables + //new samples show this doesn't always quite work, there seems to be + //more than one data set transmitted in this CSBK + //one set works, the other doesn't fit the pattern + // Site ID [5 bits] // Unknown_3 [3 bits] // Free Repeater on site [4 bits] @@ -1187,33 +1191,34 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8 // DMR PDU Payload [0B][68][10][20][18][10][20][10][28][20][EF][DA] // repeat x3 (for total of 4 sites) - int i; - uint8_t xpt_site_id[4]; - uint8_t xpt_site_rp[4]; - uint8_t xpt_site_u1[4]; - uint8_t xpt_site_u2[4]; - for (i = 0; i < 4; i++) - { - xpt_site_id[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16+(i*16)], 5); - xpt_site_u1[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[21+(i*16)], 3); - xpt_site_rp[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[24+(i*16)], 4); - xpt_site_u2[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[28+(i*16)], 4); - } + // int i; + // uint8_t xpt_site_id[4]; + // uint8_t xpt_site_rp[4]; + // uint8_t xpt_site_u1[4]; + // uint8_t xpt_site_u2[4]; - fprintf (stderr, " Hytera XPT Site Id: %d - Free RPT: %d", xpt_site_id[0], xpt_site_rp[0]); - // fprintf (stderr, " RS1: %d RS2: %d", xpt_site_u1[0], xpt_site_u2[1]); //debug + // for (i = 0; i < 4; i++) + // { + // xpt_site_id[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16+(i*16)], 5); + // xpt_site_u1[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[21+(i*16)], 3); + // xpt_site_rp[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[24+(i*16)], 4); + // xpt_site_u2[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[28+(i*16)], 4); + // } - fprintf (stderr, "\n"); - fprintf (stderr, " XPT Adj Site(s): "); - for (i = 1; i < 4; i++) - { - if (xpt_site_id[i] != 0) - { - fprintf (stderr, "%d (%d) ", xpt_site_id[i], xpt_site_rp[i]); - // fprintf (stderr, "RS1: %d RS2: %d - ", xpt_site_u1[i], xpt_site_u2[i]); //debug - } - } + // fprintf (stderr, " Hytera XPT Site Id: %d - Free RPT: %d", xpt_site_id[0], xpt_site_rp[0]); + // // fprintf (stderr, " RS1: %d RS2: %d", xpt_site_u1[0], xpt_site_u2[1]); //debug + + // fprintf (stderr, "\n"); + // fprintf (stderr, " XPT Adj Site(s): "); + // for (i = 1; i < 4; i++) + // { + // if (xpt_site_id[i] != 0) + // { + // fprintf (stderr, "%d (%d) ", xpt_site_id[i], xpt_site_rp[i]); + // // fprintf (stderr, "RS1: %d RS2: %d - ", xpt_site_u1[i], xpt_site_u2[i]); //debug + // } + // } sprintf (state->dmr_branding_sub, "XPT "); diff --git a/src/dmr_flco.c b/src/dmr_flco.c index 47e1b51..e71887f 100644 --- a/src/dmr_flco.c +++ b/src/dmr_flco.c @@ -113,8 +113,8 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16); } - //Unknown CapMax/Moto Things - if (fid == 0x10 && (flco == 0x08 || flco == 0x28)) + //Unknown CapMax/Moto Things -- 0x14, 0x15, 0x16 are new ones on emb + if (fid == 0x10 && (flco == 0x08 || flco == 0x28 || flco == 0x14 || flco == 0x15 || flco == 0x16)) { //NOTE: fid 0x10 and flco 0x08 (emb) produces a lot of 'zero' bytes //this has been observed to happen often on CapMax systems, so I believe it could be some CapMax 'thing' @@ -221,7 +221,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C fprintf (stderr, "Hytera XPT "); if (reserved == 1) fprintf (stderr, "Group "); //according to observation else fprintf (stderr, "Private "); //according to observation - fprintf (stderr, "Call Grant "); + fprintf (stderr, "Call Protect "); fprintf (stderr, "%s", KYEL); fprintf (stderr, "F-Rpt %d", xpt_free+1); @@ -901,6 +901,9 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[]) //NOTE: on really busy systems, this free repeater assignment can lag due to the 4 TS requirment to get SLC fprintf (stderr, " SLCO Hytera XPT - Free RPT %d ", xpt_free+1); sprintf (state->dmr_branding_sub, "XPT "); + + //add string for ncurses terminal display + sprintf (state->dmr_site_parms, "Free RPT - %d ", xpt_free+1); } else fprintf (stderr, " SLCO Unknown - %d ", slco); diff --git a/src/dsd_dibit.c b/src/dsd_dibit.c index eb0851f..0f9b4e0 100644 --- a/src/dsd_dibit.c +++ b/src/dsd_dibit.c @@ -283,11 +283,11 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol) valid = 0; - //re-enabling -- heuristics may also work with P2, but cna't see any benefit, need more P2 C4FM samples for testing - if (state->synctype == 1 && opts->p25_trunk == 1) //|| state->synctype == 36 + //disabling again, causing issues with trunking + if (state->synctype == 1 && opts->p25_trunk == 1) { // Use the P25 heuristics if available - valid = estimate_symbol(state->rf_mod, &(state->inv_p25_heuristics), state->last_dibit, symbol, &dibit); + // valid = estimate_symbol(state->rf_mod, &(state->inv_p25_heuristics), state->last_dibit, symbol, &dibit); } if (valid == 0) @@ -347,11 +347,11 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol) valid = 0; - ////re-enabling, but only when trunking (should have uniform volume if not tampered with during setup) - if (state->synctype == 0 && opts->p25_trunk == 1) //|| state->synctype == 35 + //disabling again, causing issues with trunking + if (state->synctype == 0 && opts->p25_trunk == 1) { // Use the P25 heuristics if available - valid = estimate_symbol(state->rf_mod, &(state->p25_heuristics), state->last_dibit, symbol, &dibit); + // valid = estimate_symbol(state->rf_mod, &(state->p25_heuristics), state->last_dibit, symbol, &dibit); } if (valid == 0) diff --git a/src/dsd_import.c b/src/dsd_import.c index da7839c..7a39f8e 100644 --- a/src/dsd_import.c +++ b/src/dsd_import.c @@ -146,7 +146,7 @@ int csvChanImport(dsd_opts * opts, dsd_state * state) //channel map import return 0; } -int csvKeyImport(dsd_opts * opts, dsd_state * state) //multi-key support for NXDN +int csvKeyImport(dsd_opts * opts, dsd_state * state) //multi-key support { char filename[1024] = "filename.csv"; sprintf (filename, "%s", opts->key_in_file); @@ -160,7 +160,13 @@ int csvKeyImport(dsd_opts * opts, dsd_state * state) //multi-key support for NXD } int row_count = 0; int field_count = 0; - unsigned long long int keynumber; + + unsigned long long int keynumber = 0; + unsigned long long int keyvalue = 0; + + uint16_t hash = 0; + uint8_t hash_bits[24]; + memset (hash_bits, 0, sizeof(hash_bits)); while (fgets(buffer, BSIZE, fp)) { field_count = 0; @@ -173,11 +179,24 @@ int csvKeyImport(dsd_opts * opts, dsd_state * state) //multi-key support for NXD if (field_count == 0) { sscanf (field, "%lld", &keynumber); + if (keynumber > 0xFFFF) //if larger than 16-bits, get its hash instead + { + keynumber = keynumber & 0xFFFFFF; //truncate to 24-bits (max allowed) + for (int i = 0; i < 24; i++) + { + hash_bits[i] = ((keynumber << i) & 0x800000) >> 23; //load into array for CRC16 + } + hash = ComputeCrcCCITT16d (hash_bits, 24); + keynumber = hash & 0xFFFF; //make sure its no larger than 16-bits + fprintf (stderr, "Hashed "); + } + } if (field_count == 1) { - sscanf (field, "%lld", &state->rkey_array[keynumber]); + sscanf (field, "%lld", &keyvalue); + state->rkey_array[keynumber] = keyvalue & 0xFFFFFFFFFF; //doesn't exceed 40-bit value } field = strtok(NULL, ","); @@ -185,6 +204,7 @@ int csvKeyImport(dsd_opts * opts, dsd_state * state) //multi-key support for NXD } fprintf (stderr, "Key [%03lld] [%05lld]", keynumber, state->rkey_array[keynumber]); fprintf (stderr, "\n"); + hash = 0; } fclose(fp); diff --git a/src/dsd_main.c b/src/dsd_main.c index f91e870..d499864 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -978,8 +978,10 @@ usage () printf (" -H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8' \n"); printf ("\n"); printf (" -R Manually Enter dPMR or NXDN EHR Scrambler Key Value (Decimal Value)\n"); - printf (" -k Import NXDN Scrambler Key List from csv file.\n"); printf (" \n"); + printf (" -k Import Key List from csv file.\n"); + printf (" Only supports NXDN, DMR Basic Privacy and **tera 10-Char (decimal value) only. \n"); + printf (" (dPMR and **tera 32/64 char not supported, DMR uses TG value as key id -- EXPERIMENTAL!!). \n"); printf (" -4 Force DMR Privacy Key over FID and SVC bits \n"); printf ("\n"); printf (" Trunking Options:\n"); diff --git a/src/dsd_mbe.c b/src/dsd_mbe.c index 5678677..9c426ed 100644 --- a/src/dsd_mbe.c +++ b/src/dsd_mbe.c @@ -140,6 +140,10 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a unsigned long long int k; int x; + //24-bit TG to 16-bit hash + uint16_t hash = 0; + uint8_t hash_bits[24]; + memset (hash_bits, 0, sizeof(hash_bits)); for (i = 0; i < 88; i++) { @@ -291,6 +295,31 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a mbe_demodulateAmbe3600x2450Data (ambe_fr); state->errs2 = mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d); + //EXPERIMENTAL!! + //load basic privacy key number from array by the tg value (if not forced) + //currently only Moto BP and Hytera 10 Char BP (converted to decimal) + if (state->M == 0) + { + //see if we need to hash a value larger than 16-bits + hash = state->lasttg & 0xFFFFFF; + if (hash > 0xFFFF) //if greater than 16-bits + { + for (int i = 0; i < 24; i++) + { + hash_bits[i] = ((hash << i) & 0x800000) >> 23; //load into array for CRC16 + } + hash = ComputeCrcCCITT16d (hash_bits, 24); + hash = hash & 0xFFFF; //make sure its no larger than 16-bits + } + if (state->rkey_array[hash] != 0) + { + state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255 + state->K1 = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit + opts->dmr_mute_encL = 0; + } + // else opts->dmr_mute_encL = 1; //may cause issues for manual key entry (non-csv) + } + if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) || (state->K > 0 && state->M == 1) ) { @@ -393,6 +422,31 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a mbe_demodulateAmbe3600x2450Data (ambe_fr); state->errs2R = mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d); + //EXPERIMENTAL!! + //load basic privacy key number from array by the tg value (if not forced) + //currently only Moto BP and Hytera 10 Char BP (converted to decimal) + if (state->M == 0) + { + //see if we need to hash a value larger than 16-bits + hash = state->lasttgR & 0xFFFFFF; + if (hash > 0xFFFF) //if greater than 16-bits + { + for (int i = 0; i < 24; i++) + { + hash_bits[i] = ((hash << i) & 0x800000) >> 23; //load into array for CRC16 + } + hash = ComputeCrcCCITT16d (hash_bits, 24); + hash = hash & 0xFFFF; //make sure its no larger than 16-bits + } + if (state->rkey_array[hash] != 0) + { + state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255 + state->K1 = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit + opts->dmr_mute_encR = 0; + } + // else opts->dmr_mute_encR = 1; //may cause issues for manual key entry (non-csv) + } + if ( (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) || (state->K > 0 && state->M == 1) ) { diff --git a/src/nxdn_element.c b/src/nxdn_element.c index 062da73..2559758 100644 --- a/src/nxdn_element.c +++ b/src/nxdn_element.c @@ -233,6 +233,11 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa long int freq = 0; freq = nxdn_channel_to_frequency(opts, state, Channel); + //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[DestinationID] != 0) state->R = state->rkey_array[DestinationID]; + if (state->M == 1) state->nxdn_cipher_type = 0x1; + //check for control channel frequency in the channel map if not available if (opts->p25_trunk == 1 && state->p25_cc_freq == 0) { @@ -662,6 +667,18 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message) fprintf(stderr, "Src=%u - Dst/TG=%u ", SourceUnitID & 0xFFFF, DestinationID & 0xFFFF); fprintf (stderr, "%s", KNRM); + //check the rkey array for a scrambler key value + //check by keyid first, then by tgt id + //TGT ID and Key ID could clash though if csv or system has both with different keys + if (state->rkey_array[KeyID] != 0) state->R = state->rkey_array[KeyID]; + else if (state->rkey_array[DestinationID] != 0) state->R = state->rkey_array[DestinationID]; + + if (state->M == 1) + { + state->nxdn_cipher_type = 0x1; + CipherType = 0x1; + } + /* Print the "Cipher Type" */ if(CipherType != 0) { @@ -676,12 +693,6 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message) fprintf (stderr, "%s", KNRM); } - //check the rkey array for a scrambler key value - //check by keyid first, then by tgt id - //TGT ID and Key ID could clash though if csv or system has both - if (state->rkey_array[KeyID] != 0) state->R = state->rkey_array[KeyID]; - else if (state->rkey_array[DestinationID] != 0) state->R = state->rkey_array[DestinationID]; - else state->R = 0; if (state->nxdn_cipher_type == 0x01 && state->R > 0) //scrambler key value {