From ac1c658870d04d242d8c0fe18d8695fd47d7b53c Mon Sep 17 00:00:00 2001 From: lwvmobile Date: Fri, 10 Oct 2025 05:53:36 -0400 Subject: [PATCH] DMR: Initial Support for Kirisun "Advanced" and "Universal" Encryption Identifiers and Link Control; #319 --- src/dmr_flco.c | 12 +++++++-- src/dmr_le.c | 22 +++++++++++++++++ src/dmr_ms.c | 10 ++++++++ src/dmr_pi.c | 52 ++++++++++++++++++++++++++++++++++++++- src/dsd_ncurses_printer.c | 40 ++++++++++++++++++++++++++++++ 5 files changed, 133 insertions(+), 3 deletions(-) diff --git a/src/dmr_flco.c b/src/dmr_flco.c index 8c9fa0b..c5375ae 100644 --- a/src/dmr_flco.c +++ b/src/dmr_flco.c @@ -72,6 +72,13 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C so ^= 0x40; } + //Kirisun + if (*IrrecoverableErrors == 0 && CRCCorrect == 1 && fid == 0x0A) //&& (so & 0x40) == 0x40 + { + //disable late entry for DMRA, Flag 3 for future checks + opts->dmr_le = 3; + } + //read ahead a little to get this for the xpt flag if (*IrrecoverableErrors == 0 && flco == 0x09 && fid == 0x68) { @@ -310,8 +317,8 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C } //unknown other manufacturer or OTA ENC, etc. - //removed tait from the list, added hytera 0x08 - if (fid != 0 && fid != 0x68 && fid != 0x10 && fid != 0x08 && is_kenwood_sc == 0) + //removed tait from the list, added hytera 0x08, added Kirisun and KW Scrambler LC + if (fid != 0 && fid != 0x68 && fid != 0x10 && fid != 0x08 && fid != 0x0A && is_kenwood_sc == 0) { if (type == 1) fprintf (stderr, "%s \n", KYEL); if (type == 2) fprintf (stderr, "%s \n", KYEL); @@ -654,6 +661,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C } //should rework this back into the upper portion + if (fid == 0x0A) fprintf (stderr, "Kirisun "); if (fid == 0x68) fprintf (stderr, "Hytera "); if (is_xpt) fprintf (stderr, "XPT "); if (fid == 0x68 && flco == 0x00) diff --git a/src/dmr_le.c b/src/dmr_le.c index 64447d5..e2beeb7 100644 --- a/src/dmr_le.c +++ b/src/dmr_le.c @@ -390,6 +390,28 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power) //NOTE: on above, I belive that we need to check by opcode as well, as a CRC3 can have multiple collisions //so we need to exclude op/alg 0 and 3 from the check (does algID 0x03/0x23 even exist?) + if (opts->dmr_le == 3) + { + if (sbrc_hex != 0) //Kirisun Placeholder, first had 0x5F1, second batch had 0x5E1 instead + { + fprintf (stderr, "\n"); + fprintf (stderr, "%s", KCYN); + fprintf (stderr, " Slot %d", state->currentslot+1); + fprintf (stderr, " DMR LE SB Kirisun Encryption;"); + fprintf (stderr, "%s ", KNRM); + if (state->currentslot == 0) + { + if (state->payload_algid == 0) + state->payload_algid = 0x35; + } + else + { + if (state->payload_algidR == 0) + state->payload_algidR = 0x35; + } + } + } + if (opts->dmr_le == 1) { if (irr_err != 0) diff --git a/src/dmr_ms.c b/src/dmr_ms.c index 28cd5f2..6de13ce 100644 --- a/src/dmr_ms.c +++ b/src/dmr_ms.c @@ -167,6 +167,16 @@ void dmrMS (dsd_opts * opts, dsd_state * state) } + //Kirisun Check VC-F for Enc Identifiers -- has bad fec, unless its loaded differently + //SEE: https://patents.google.com/patent/CN102307075A/en?q=(kirisun)&q=(dmr)&oq=kirisun + // if (opts->dmr_le == 3 && vc == 6) + // { + // bool kokay = 0; + // // kokay = Golay_24_12_decode(syncdata); + // unsigned long long int kirisun = (unsigned long long int)ConvertBitIntoBytes(&syncdata[0], 48); + // fprintf (stderr, "Kiri: %012llX; Golay Okay: %d; \n", kirisun, kokay); + // } + for(i = 0; i < 8; i++) emb_pdu[i + 0] = syncdata[i]; for(i = 0; i < 8; i++) emb_pdu[i + 8] = syncdata[i + 40]; diff --git a/src/dmr_pi.c b/src/dmr_pi.c index 1925289..a79cdf4 100644 --- a/src/dmr_pi.c +++ b/src/dmr_pi.c @@ -12,7 +12,6 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors) { - UNUSED2(opts, CRCCorrect); uint8_t MFID = PI_BYTE[1]; @@ -26,6 +25,57 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC state->last_cc_sync_time = time(NULL); } + if (MFID == 0x0A && CRCCorrect == 1) //Kirisun + { + uint8_t so = PI_BYTE[2]; //in VLC and PI, this byte is 0x40, so thinking this could be SVC_OPT + uint8_t alg = PI_BYTE[0]; //observed 0x36 and 0x37 for Kirisun Advanced and Universal Privacy + // uint8_t kid = PI_BYTE[2]; //user info conveyed there is only 16 allotments for Key, and keys are channel specific, so no key id value + uint32_t target = ((unsigned long long int)PI_BYTE[7] << 16) | ((unsigned long long int)PI_BYTE[8] << 8) | ((unsigned long long int)PI_BYTE[9] << 0); + + //TODO: Use TGT value here to produce a key id via hashing it + uint8_t hash = target % 256; //more complex hash later + + //MI only appears to be 32-bit + unsigned long long int mi = ((unsigned long long int)PI_BYTE[3] << 24) | ((unsigned long long int)PI_BYTE[4] << 16) | + ((unsigned long long int)PI_BYTE[5] << 8) | ((unsigned long long int)PI_BYTE[6] << 0); + + if (state->currentslot == 0) + { + state->dmr_so = so; + state->payload_algid = alg; + state->payload_keyid = hash; + state->payload_mi = mi; + } + else + { + state->dmr_soR = so; + state->payload_algidR = alg; + state->payload_keyidR = hash; + state->payload_miR = mi; + } + + fprintf (stderr, "%s ", KYEL); + fprintf (stderr, "\n Slot %d", state->currentslot+1); + fprintf (stderr, " DMR PI H- ALG ID: %02X; KEY ID: %02X; MI(32): %08llX;", alg, hash, mi); + + fprintf (stderr, " Kirisun "); + if (alg == 0x36) + fprintf (stderr, "Advanced;"); + else if (alg == 0x37) + fprintf (stderr, "Universal;"); + else fprintf (stderr, "Unknown;"); + fprintf (stderr, "%s", KNRM); + + //disable late entry for DMRA, Flag 3 for future check of VC-F 48-bit region with Golay 24,12 Encoding + //SEE: https://patents.google.com/patent/CN102307075A/en?q=(kirisun)&q=(dmr)&oq=kirisun + //tested patent above with information provided, VC-F had a SB, but the 48-bit + //full value yielded bad Golay results, and user submitted info contradicts this patent for samples provided + //Late Entry MI does appear to work, however, and reports a good CRC for that as well, + //but the LFSR or method to get to the next MI or len of it is unknown as of yet. + opts->dmr_le = 3; + + } + if (MFID == 0x68) //Hytera Enhanced { if (state->currentslot == 0) diff --git a/src/dsd_ncurses_printer.c b/src/dsd_ncurses_printer.c index 93d06be..97fba72 100644 --- a/src/dsd_ncurses_printer.c +++ b/src/dsd_ncurses_printer.c @@ -1109,6 +1109,26 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw(" Key: %010llX", state->R); attron(COLOR_PAIR(3)); } + if (state->payload_algid == 0x36) + { + attron(COLOR_PAIR(1)); + printw("Kirisun Advanced"); + if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); } + attron(COLOR_PAIR(3)); + } + if (state->payload_algid == 0x37) + { + attron(COLOR_PAIR(1)); + printw("Kirisun Universal"); + if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); } + attron(COLOR_PAIR(3)); + } + if (state->payload_algid == 0x35) + { + attron(COLOR_PAIR(1)); + printw("Kirisun Encryption"); //0x35 is a placeholder if gained through late-entry method and alg is not known + attron(COLOR_PAIR(3)); + } printw ("\n"); @@ -1284,6 +1304,26 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw(" Key: %010llX", state->RR); attron(COLOR_PAIR(3)); } + if (state->payload_algidR == 0x36) + { + attron(COLOR_PAIR(1)); + printw("Kirisun Advanced"); + if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); } + attron(COLOR_PAIR(3)); + } + if (state->payload_algidR == 0x37) + { + attron(COLOR_PAIR(1)); + printw("Kirisun Universal"); + if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); } + attron(COLOR_PAIR(3)); + } + if (state->payload_algidR == 0x35) + { + attron(COLOR_PAIR(1)); + printw("Kirisun Encryption"); //0x35 is a placeholder if gained through late-entry method and alg is not known + attron(COLOR_PAIR(3)); + } printw ("\n");