NXDN: DCALL Finish up data decryption methods;

NXDN: Fix Block Assembly on "idas" types, they do have the same blocking structure;

NXDN: Fix CRC32 and "total_bytes" when padding bytes are present;

NXDN: Add placeholder GPS function and reverse byte load for GPS location data found in certain DCALL messages;

DMR: Init AES keyloader arrays for PDU;

Misc cleanup and tweaks;
This commit is contained in:
lwvmobile
2026-03-13 23:43:43 -04:00
parent ad77f266c4
commit a6bdfa913f
4 changed files with 162 additions and 46 deletions

View File

@@ -1334,8 +1334,8 @@ void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message
void nxdn_decode_dst_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_adj_site(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void nxdn_sdcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void nxdn_sdcall_iv(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message);
void nxdn_dcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void nxdn_dcall_iv(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message);
int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message);
//Type-D SCCH Message Decoder
void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uint8_t direction);
@@ -1406,6 +1406,7 @@ void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
void lip_protocol_decoder (dsd_opts * opts, dsd_state * state, uint8_t * input);
void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int type);
void harris_lptt (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot, int phase);
void nxdn_gps_report(dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src);
void utf16_to_text (dsd_state * state, uint8_t wr, uint16_t len, uint8_t * input);
void utf8_to_text (dsd_state * state, uint8_t wr, uint16_t len, uint8_t * input);

View File

@@ -1028,9 +1028,9 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
R = state->rkey_array[state->payload_keyidR];
//loader for aes keys
uint8_t kaes[32];
uint8_t empt[32];
uint8_t maes[16];
uint8_t kaes[32]; memset(kaes, 0, sizeof(kaes));
uint8_t empt[32]; memset(empt, 0, sizeof(empt));
uint8_t maes[16]; memset(maes, 0, sizeof(maes));
for (i = 0; i < 8; i++)
{
kaes[i+0] = ((state->rkey_array[kid+0x000]) >> (56-(i*8))) & 0xFF;

View File

@@ -709,6 +709,7 @@ void decode_cellocator(dsd_opts * opts, dsd_state * state, uint8_t * input, int
//will need to establish a len value for data and contents
}
void decode_ars(dsd_opts * opts, dsd_state * state, uint8_t * input, int len)
{
//TODO: This
@@ -717,3 +718,12 @@ void decode_ars(dsd_opts * opts, dsd_state * state, uint8_t * input, int len)
UNUSED(input);
UNUSED(len);
}
void nxdn_gps_report(dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src)
{
//TODO: This
UNUSED(opts);
UNUSED(state);
UNUSED(input);
UNUSED(src);
}

View File

@@ -93,7 +93,7 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state, uint8_t Cr
//SDCALL_IV
case 0x3A:
nxdn_dcall_iv(opts, state, 2, ElementsContent);
nxdn_sdcall_iv(opts, state, 2, ElementsContent);
break;
//DCALL Header
@@ -177,6 +177,9 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state, uint8_t Cr
//reset block number
state->data_header_blocks[0] = 1;
//reset padding bytes
state->data_header_padding[0] = 0;
//reset delivery format (type)
state->data_header_format[0] = 0;
@@ -187,6 +190,7 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state, uint8_t Cr
state->payload_algid = 0;
state->payload_keyid = 0;
state->payload_mi = 0;
memset(state->aes_ivR, 0, sizeof(state->aes_ivR));
case 0x01: //VCALL
NXDN_decode_VCALL(opts, state, ElementsContent);
@@ -207,6 +211,9 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state, uint8_t Cr
//reset block number
state->data_header_blocks[0] = 1;
//reset padding bytes
state->data_header_padding[0] = 0;
//reset delivery format (type)
state->data_header_format[0] = 0;
@@ -217,6 +224,7 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state, uint8_t Cr
state->payload_algid = 0;
state->payload_keyid = 0;
state->payload_mi = 0;
memset(state->aes_ivR, 0, sizeof(state->aes_ivR));
//tune back to CC here - save about 1-2 seconds
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1)
@@ -2442,7 +2450,7 @@ void NXDN_decode_ALIAS_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Messag
} /* End NXDN_decode_ALIAS_ARIB() */
void nxdn_dcall_iv(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message)
void nxdn_sdcall_iv(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message)
{
UNUSED(opts);
@@ -2573,6 +2581,9 @@ void nxdn_sdcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message)
state->data_header_blocks[0] = block_count;
else state->data_header_blocks[0] = 1;
//set padding bytes (needed for ks application, crc check, valid len of data)
state->data_header_padding[0] = pad_bytes;
//set this is a valid header
state->data_header_valid[0] = 1;
@@ -2716,6 +2727,9 @@ void nxdn_dcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message)
state->data_header_blocks[0] = block_count;
else state->data_header_blocks[0] = 1;
//set padding bytes (needed for ks application, crc check, valid len of data)
state->data_header_padding[0] = pad_bytes;
//set this is a valid header
state->data_header_valid[0] = 1;
@@ -2734,11 +2748,6 @@ int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Mess
fprintf (stderr, "Data Call (%d/%d); ", pf_num, blk_num);
fprintf (stderr, "%s", KNRM);
//TODO: Will need to have a len of valid bytes of dcall data at this point, that depends on carrier type (cac, facch1, udch, udch2, etc)
//NOTE: Its possible this format only works with UDCH, or UDCH2 if using Type-D
//TODO: Copy / Append data in blocks to superframe for decode on last one received
//TODO: CRC Check on completed PDU? Is that available?
int byte_len = 20; //relevant number of user data bytes in UDCH
uint8_t idas = 0;
if (strcmp (state->nxdn_location_category, "Type-D") == 0) idas = 1;
@@ -2755,10 +2764,10 @@ int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Mess
//Set a pointer value to where this block fits into the PDU superframe structure (bits, not bytes)
int ptr = byte_len * 8 * (state->data_header_blocks[0] - blk_num);
int total_bytes = state->data_header_blocks[0]*byte_len;
if (type == 2) //reported blocks is 2, but data blocks consists of 2,1,0 in sample obtained, doesn't occur on IDAS, but on Type-C UPCH
total_bytes = (state->data_header_blocks[0]+1)*byte_len;
int total_bytes = (state->data_header_blocks[0]+1)*byte_len;
total_bytes -= state->data_header_padding[0];
//sanity check and warning
if (state->data_header_valid[0] == 0)
@@ -2774,28 +2783,91 @@ int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Mess
return -1;
}
if (total_bytes < 4) //this shouldn't even happen in code
{
fprintf (stderr, "Total Bytes After Padding Less Than Required; ");
state->data_header_valid[0] = 0;
return -1;
}
//Debug
// fprintf (stderr, "Type: %d; Byte Len: %02d; Total: %03d; Ptr: %04d;", state->data_header_format[0], byte_len, total_bytes, ptr);
fprintf (stderr, "Type: %d; Byte Len: %02d; Pad: %02d; Total: %03d; Ptr: %04d;", state->data_header_format[0], byte_len, state->data_header_padding[0], total_bytes, ptr);
//storage (bits, not bytes)
memcpy(state->dmr_pdu_sf[0]+ptr, Message+16, byte_len*8*sizeof(uint8_t));
//WIP: completed, evaluate, dump, zero out completed results
//Finished Assembly
if (blk_num == 0)
{
//WIP: KS creation and application, if applicable
//KS creation and application, if applicable
//TODO: May need to use state->RR for simultaneous data and voice call where both are potentially encrypted?
uint8_t ks[3000]; memset(ks, 0, sizeof(ks));
uint8_t ks[3000];
memset(ks, 0, sizeof(ks));
//TODO: check for key available, if loader
// if (state->payload_algid == 2)
// {
// if (state->keyloader == 1)
// {
//Check for key availability
unsigned long long int key = 0;
// }
// }
uint8_t aes_key[32];
memset(aes_key, 0, sizeof(aes_key));
uint8_t empt[32];
memset(empt, 0, sizeof(empt));
uint8_t aes_key_loaded = 0;
if (state->payload_algid != 0)
{
if (state->keyloader == 1 && state->payload_algid != 3)
{
//DES and Scrambler Key (key loader)
if (state->rkey_array[state->payload_keyid] != 0)
key = state->rkey_array[state->payload_keyid];
}
else if (state->keyloader == 0 && state->payload_algid != 3)
{
//DES and Scrambler Key (single load)
if (state->R != 0)
key = state->R;
}
else if (state->keyloader == 1 && state->payload_algid == 3)
{
//AES Key (key loader)
for (int i = 0; i < 8; i++)
{
aes_key[i+0] = ((state->rkey_array[state->payload_keyid+0x000]) >> (56-(i*8))) & 0xFF;
aes_key[i+8] = ((state->rkey_array[state->payload_keyid+0x101]) >> (56-(i*8))) & 0xFF;
aes_key[i+16] = ((state->rkey_array[state->payload_keyid+0x201]) >> (56-(i*8))) & 0xFF;
aes_key[i+24] = ((state->rkey_array[state->payload_keyid+0x301]) >> (56-(i*8))) & 0xFF;
//if kaes is loaded with a key, then flag on the key loaded variable
if (memcmp(aes_key, empt, sizeof(aes_key)) != 0)
{
key = state->rkey_array[state->payload_keyid+0x301];
aes_key_loaded = 1;
}
}
}
else if (state->keyloader == 0 && state->payload_algid == 3)
{
//AES Key (single load)
for (int i = 0; i < 8; i++)
{
aes_key[i+0] = (state->K1) >> (56-(i*8)) & 0xFF;
aes_key[i+8] = (state->K2) >> (56-(i*8)) & 0xFF;
aes_key[i+16] = (state->K3) >> (56-(i*8)) & 0xFF;
aes_key[i+24] = (state->K4) >> (56-(i*8)) & 0xFF;
}
//if kaes is loaded with a key, then flag on the key loaded variable
if (memcmp(aes_key, empt, sizeof(aes_key)) != 0)
{
key = state->K4;
aes_key_loaded = 1;
}
}
}
if (state->payload_algid != 0)
{
@@ -2805,31 +2877,31 @@ int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Mess
}
//scrambler
if (state->payload_algid == 1 && state->R != 0)
if (state->payload_algid == 1 && key != 0)
{
fprintf (stderr, " Key: %05lld;", state->R);
pdu_scrambler_keystream_creation(ks, state->R, total_bytes*8);
fprintf (stderr, " Key: %05lld;", key);
pdu_scrambler_keystream_creation(ks, key, total_bytes*8);
}
//DES
else if (state->payload_algid == 2 && state->R != 0)
else if (state->payload_algid == 2 && key != 0)
{
fprintf (stderr, " Key: %016llX;", state->R);
fprintf (stderr, " Key: %016llX;", key);
int nblocks = total_bytes/8;
if (total_bytes%8)
nblocks++;
uint8_t ks_bytes[nblocks*8]; memset(ks_bytes, 0, sizeof(ks_bytes));
des_multi_keystream_output (state->payload_mi, state->R, ks_bytes, 1, nblocks);
des_multi_keystream_output (state->payload_mi, key, ks_bytes, 1, nblocks);
unpack_byte_array_into_bit_array(ks_bytes, ks, nblocks*8);
}
//AES
else if (state->payload_algid == 3 && state->aes_key_loaded[1] == 1)
else if (state->payload_algid == 3 && aes_key_loaded == 1)
{
// fprintf (stderr, " KS: %016llX;", state->K4); //stub
fprintf (stderr, " KS: %016llX;", key); //key stub
int nblocks = total_bytes/16;
if (total_bytes%16)
nblocks++;
uint8_t ks_bytes[nblocks*16]; memset(ks_bytes, 0, sizeof(ks_bytes));
aes_ofb_keystream_output (state->aes_ivR, state->aes_key, ks_bytes, 2, nblocks);
aes_ofb_keystream_output (state->aes_ivR, aes_key, ks_bytes, 2, nblocks);
unpack_byte_array_into_bit_array(ks_bytes, ks, nblocks*16);
}
@@ -2860,23 +2932,52 @@ int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Mess
if (crc_ext == crc_chk)
{
if ((uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0], 8) == 0x06)
uint8_t opcode = (uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0], 8);
uint32_t reverse = (uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0], 24); //observed 3 bytes of zeroes
if (opcode == 0x06)
{
//String Data Dump for nmea if first byte is 0x06
fprintf (stderr, "\n NMEA: ");
//String Data Dump if first byte is 0x06 (NMEA sentences observed here)
fprintf (stderr, "\n ");
for (int i = 1; i < total_bytes; i++)
fprintf (stderr, "%c", (uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0]+(i*8), 8));
{
uint8_t ascii = (uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0]+(i*8), 8);
if (ascii >= 0x20 && ascii < 0x7F)
fprintf (stderr, "%c", ascii);
else break;
}
}
// else
// {
else if (reverse == 0)
{
//Reverse Bytes observed on location data (unknown format, AVL? Other GNSS?)
uint8_t reverse_bytes[total_bytes];
memset(reverse_bytes, 0, sizeof(reverse_bytes));
// //String Data Dump (not suitable unless this is text)
// fprintf (stderr, "\n String: ");
// for (int i = 0; i < total_bytes; i++)
// fprintf (stderr, "%c", (uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0]+(i*8), 8));
// }
//reverse bytes
int k = total_bytes-1-4;
for (int i = 0; i < total_bytes-4; i++)
{
reverse_bytes[i] = (uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0]+(k*8), 8);
k--;
}
//Debug Data Dump
fprintf (stderr, "\n REV: ");
for (int i = 0; i < total_bytes-4-4+1; i++) //sans the CRC and the leading zeroes
fprintf (stderr, "%02X", reverse_bytes[i]);
uint8_t reverse_byte_bits[total_bytes*8];
memset(reverse_byte_bits, 0, sizeof(reverse_byte_bits));
unpack_byte_array_into_bit_array(reverse_bytes, reverse_byte_bits, (total_bytes-4-4));
//GPS report on 0xFFFC? or is that just any odd Kenwood message?
if ((uint16_t)convert_bits_into_output(reverse_byte_bits, 16) == 0xFFFC)
nxdn_gps_report(opts, state, reverse_byte_bits+16, state->nxdn_last_rid); //src is probably reset on cac
}
else
{
//Anything else?
}
}
else
{
@@ -2890,6 +2991,9 @@ int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Mess
//reset block number
state->data_header_blocks[0] = 1;
//reset padding bytes
state->data_header_padding[0] = 0;
//reset delivery format (type)
state->data_header_format[0] = 0;
@@ -2900,6 +3004,7 @@ int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Mess
state->payload_algid = 0;
state->payload_keyid = 0;
state->payload_mi = 0;
memset(state->aes_ivR, 0, sizeof(state->aes_ivR));
}
return 0;