DMR: RAS Implementation; #369

RAS Implementation includes converting RAS 6-24 character passwords into 56-bit RAS Effective Key Values and Performing RAS Permutations on RAS Enabled Burst Types to Calculate the Correct MAC attachment to validate messages;

Better Handling for RAS indicators found in BPTC 196,96 Reserved Bits and handling for RAS indicator bits found in embedded link control opcode byte;

Users may either enter the RAS password with the -$ option, or the 56-bit effective key value with the -y option.

Users will no longer need to use the -F option to bypass CRC errors on RAS enabled payloads when FEC does not explicitely fail.

Removed the old float audio -y option.
This commit is contained in:
lwvmobile
2026-09-01 13:29:55 -04:00
parent 198f0eacb5
commit cc6711fe4d
11 changed files with 815 additions and 107 deletions

View File

@@ -1,9 +1,9 @@
# Digital Speech Decoder - Florida Man Edition
DSD-FME is an evolution of the original DSD project from 'DSD Author' using the base code of [szechyjs](https://github.com/szechyjs/dsd "szechyjs"), some code and ideas from [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve"), [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25") and [Osmocom OP25](https://gitea.osmocom.org/op25/op25 "Osmocom OP25"), along with other snippets of code, information, and inspirations from other projects including [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDRTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), [OK-DMRlib](https://github.com/OK-DMR/ok-dmrlib "OK-DMRlib"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"), Eric Cottrell, SP5WWP and others. Finally, this is all brought together with original code to extend the fuctionality and add new features including NCurses Terminal and Menu system, Pulse Audio, TCP Direct Link Audio, RIGCTL, Trunking Features, LRRP/GPS Mapping, P25 Phase 2, EDACS, YSF, M17, OP25 Capture Bin compatability, etc. DSD-FME is primarily focused with Linux Desktop users in mind, so please understand that this version may not compile, compile easily, or run correctly in other environments.
DSD-FME is an evolution of the original DSD project from 'DSD Author' using the base code of [szechyjs](https://github.com/szechyjs/dsd "szechyjs"), some code and ideas from [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve"), [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25") and [Osmocom OP25](https://gitea.osmocom.org/op25/op25 "Osmocom OP25"), along with other snippets of code, information, and inspirations from other projects including [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDRTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), [OK-DMRlib](https://github.com/OK-DMR/ok-dmrlib "OK-DMRlib"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"), Eric Cottrell, SP5WWP, ilyacodes, Phillipw1084 and others. Finally, this is all brought together with original code to extend the fuctionality and add new features including NCurses Terminal and Menu system, Pulse Audio, TCP Direct Link Audio, RIGCTL, Trunking Features, LRRP/GPS Mapping, P25 Phase 2, EDACS, YSF, M17, OP25 Capture Bin compatability, etc. DSD-FME is primarily focused with Linux Desktop users in mind, so please understand that this version may not compile, compile easily, or run correctly in other environments.
This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu, Forts, thewraithe2008, RayAir, Cretu, ilyacodes, and others for the many hours of wav samples and information provided by them. Most importantly, HRH17, whose insight, information, samples, and willingness to let me remote into a computer half-way across the globe in order to test trunking features are what make DSD-FME what it has become. I'd also like to thank mrscanner2008 for providing an additional remote where additional NXDN Type-C, 'Idas' Type-D, and XPT decoding and trunking could be sorted out. Thanks to volo-zyko for cleaning up a lot of code. Thank you everybody.
This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu, Forts, thewraithe2008, RayAir, Cretu, and others for the many hours of wav samples and information provided by them. Most importantly, HRH17, whose insight, information, samples, and willingness to let me remote into a computer half-way across the globe in order to test trunking features are what make DSD-FME what it has become. I'd also like to thank mrscanner2008 for providing an additional remote where additional NXDN Type-C, 'Idas' Type-D, and XPT decoding and trunking could be sorted out. Thanks to volo-zyko for cleaning up a lot of code. Thank you everybody.
![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/audio_work/dsd-fme2.png)

View File

@@ -1,4 +1,4 @@
Build Version: AW 2026-36-g5e5c3c4
Build Version: AW 2026-37-g198f0ea
MBElib Version: 1.3.4
CODEC2 Support Enabled
@@ -62,7 +62,6 @@ Input/Output options:
-q Reverse Mute - Mute Unencrypted Voice and Unmute Encrypted Voice
-V <num> Enable TDMA Voice Synthesis on Slot 1 (1), Slot 2 (2), or Both (3); Default is 3;
-z Set TDMA Voice Slot Preference when using /dev/dsp audio output (prevent lag and stuttering)
-y Enable Experimental Pulse Audio Float Audio Output
-v <hex> Set Filtering Bitmap Options (Advanced Option)
1 1 1 1 (0xF): PBF/LPF/HPF/HPFD on
@@ -175,6 +174,12 @@ Advanced Decoder options:
-A <hex> Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value)
-$ <string> Enter RAS Password 6-24 Char Key Value (DMR) (String Value)
-$ Password123
-y <hex> Enter RAS 56-bit Effective Key Value (DMR) (14 Hex Value)
-y 6DF241BC4AE2EC
-, <string> Manually Enter and Enforce Auctus A6 (GoComm) 8 Char Key Value (DMR) (String Value)
-, Secure04

View File

@@ -918,6 +918,12 @@ typedef struct
//Remus DMR End Call Alert Beep
int dmr_end_alert[2]; //dmr TLC end call alert beep has already played once flag
//RAS Key Variables
// #define DEBUG_RAS
unsigned long long int ras_effective_key;
int ras_permutation_order[104];
uint8_t dmr_is_ras; //indicate a partial or complete RAS enabled system
//Bitmap Filtering Options
int audio_smoothing;
@@ -1358,11 +1364,13 @@ int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24]);
//BPTC (Block Product Turbo Code) functions
void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output);
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3]);
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3], uint8_t * is_ras);
uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77]);
uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32], uint32_t ParityCheckTypeOdd);
//Reed Solomon (12,9) functions
//Reed Solomon DMR functions
void rs_16_13_encode(uint8_t * input);
void rs_12_9_encode(uint8_t * input);
void rs_12_9_calc_syndrome(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome);
uint8_t rs_12_9_check_syndrome(rs_12_9_poly_t *syndrome);
rs_12_9_correct_errors_result_t rs_12_9_correct_errors(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome, uint8_t *errors_found);
@@ -1660,6 +1668,11 @@ void hytera_enhanced_rc4_setup(dsd_opts * opts, dsd_state * state, unsigned long
unsigned long long int hytera_lfsr(uint8_t * mi, uint8_t * taps, uint8_t len);
void hytera_enhanced_alg_refresh(dsd_state * state);
//RAS
uint32_t ras_mac_calculator(dsd_state * state, uint8_t * input, int input_len, int type);
void ras_effective_key_creation(dsd_state * state, char * input);
void ras_permute_order(uint32_t seed, int * order);
//LFSR to expand either a DMR 32-bit or P25/NXDN 64-bit MI into a 128-bit IV for AES
void lfsr_32_to_128(uint8_t * iv);
void lfsr_64_to_128(uint8_t * iv);

View File

@@ -176,7 +176,7 @@ void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output)
*
* @return The total number of irrecoverable Hamming check errors
*/
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3])
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3], uint8_t * is_ras)
{
uint32_t i, j, k;
uint8_t DataMatrix[13][15];
@@ -199,24 +199,6 @@ uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DM
}
}
/* Set to 0 R(0) to R(2) - See DMR standard chapter B1.1 BPTC (196,96) */
// DataMatrix[0][0] = 0; /* R(2) */
// DataMatrix[0][1] = 0; /* R(1) */
// DataMatrix[0][2] = 0; /* R(0) */
/* Init the Hamming (15,11,3) library
* Not needed because it has already been done
* in the "InitAllFecFunction()" function */
//Hamming_15_11_init();
/* Init the Hamming (13,9,3) library
* Not needed because it has already been done
* in the "InitAllFecFunction()" function */
//Hamming_13_9_init();
/* Init the Hamming error counter */
HammingIrrecoverableErrorNb = 0;
/* Process the the Hamming (15,11,3) code
* check on each line (first time).
* Do not check the 4th last lines */
@@ -268,7 +250,7 @@ uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DM
}
/* The first Hamming code check has maybe corrected bit
* witch can be use to correct other bit, so make the
* which can be use to correct other bit, so make the
* same operation twice */
/* Reset the number of irrecoverable errors */
@@ -359,9 +341,20 @@ uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DM
* Restricted Access System (RAS) information,
* So save these three bits after hamming correction
* See patent US 2013/0288643 A1 */
R[0] = DataMatrix[0][2]; /* Save R(0) */
R[1] = DataMatrix[0][1]; /* Save R(1) */
R[2] = DataMatrix[0][0]; /* Save R(2) */
//TODO: Get seperate validity for line 0 and column 0,1,2
if (HammingIrrecoverableErrorNb == 0)
{
R[0] = DataMatrix[0][2]; /* Save R(0) */
R[1] = DataMatrix[0][1]; /* Save R(1) */
R[2] = DataMatrix[0][0]; /* Save R(2) */
}
if (R[0] == 0 && R[1] == 0 && R[2] == 1)
{
*is_ras = 1;
}
else *is_ras = 0;
/* Return the number of irrecoverable Hamming errors */
return HammingIrrecoverableErrorNb;
@@ -404,11 +397,6 @@ uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDat
}
}
/* Init the Hamming (16,11,4) library
* Not needed because it has already been done
* in the "InitAllFecFunction()" function */
//Hamming_16_11_4_init();
/* Process the the Hamming (16,11,4) code
* check on each line.
* Do not check the last line (line nb 8) */
@@ -488,6 +476,15 @@ uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDat
/* Return the number of irrecoverable Hamming errors +
* the number of parity check error */
return (HammingIrrecoverableErrorNb + ParityCheckErrorNb);
//observation shows some parity check error is okay,
//as long as the hamming is completely okay, may be worth it
//to only check parity bits on columns
if (HammingIrrecoverableErrorNb == 0 && ParityCheckErrorNb < 2)
return (HammingIrrecoverableErrorNb);
else return (HammingIrrecoverableErrorNb + ParityCheckErrorNb);
} /* End BPTC_128x77_Extract_Data() */

468
src/crypt-md5.c Normal file
View File

@@ -0,0 +1,468 @@
//MD5 Code Originally Sourced From: https://github.com/Zunawe/md5-c
/*
* Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm
* and modified slightly to be functionally identical but condensed into control structures.
*/
#include "dsd.h"
typedef struct{
uint64_t size; // Size of input in bytes
uint32_t buffer[4]; // Current accumulation of hash
uint8_t input[64]; // Input to be used in the next step
uint8_t digest[16]; // Result of algorithm
}MD5Context;
/*
* Constants defined by the MD5 algorithm
*/
#define A 0x67452301
#define B 0xefcdab89
#define C 0x98badcfe
#define D 0x10325476
static uint32_t S[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
static uint32_t K[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
/*
* Padding used to make the size (in bits) of the input congruent to 448 mod 512
*/
static uint8_t PADDING[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/*
* Bit-manipulation functions defined by the MD5 algorithm
*/
#define F(X, Y, Z) ((X & Y) | (~X & Z))
#define G(X, Y, Z) ((X & Z) | (Y & ~Z))
#define H(X, Y, Z) (X ^ Y ^ Z)
#define I(X, Y, Z) (Y ^ (X | ~Z))
/*
* Rotates a 32-bit word left by n bits
*/
uint32_t rotateLeft(uint32_t x, uint32_t n){
return (x << n) | (x >> (32 - n));
}
/*
* Initialize a context
*/
void md5Init(MD5Context *ctx){
ctx->size = (uint64_t)0;
ctx->buffer[0] = (uint32_t)A;
ctx->buffer[1] = (uint32_t)B;
ctx->buffer[2] = (uint32_t)C;
ctx->buffer[3] = (uint32_t)D;
}
/*
* Step on 512 bits of input with the main MD5 algorithm.
*/
void md5Step(uint32_t *buffer, uint32_t *input){
uint32_t AA = buffer[0];
uint32_t BB = buffer[1];
uint32_t CC = buffer[2];
uint32_t DD = buffer[3];
uint32_t E;
unsigned int j;
for(unsigned int i = 0; i < 64; ++i){
switch(i / 16){
case 0:
E = F(BB, CC, DD);
j = i;
break;
case 1:
E = G(BB, CC, DD);
j = ((i * 5) + 1) % 16;
break;
case 2:
E = H(BB, CC, DD);
j = ((i * 3) + 5) % 16;
break;
default:
E = I(BB, CC, DD);
j = (i * 7) % 16;
break;
}
uint32_t temp = DD;
DD = CC;
CC = BB;
BB = BB + rotateLeft(AA + E + K[i] + input[j], S[i]);
AA = temp;
}
buffer[0] += AA;
buffer[1] += BB;
buffer[2] += CC;
buffer[3] += DD;
}
/*
* Add some amount of input to the context
*
* If the input fills out a block of 512 bits, apply the algorithm (md5Step)
* and save the result in the buffer. Also updates the overall size.
*/
void md5Update(MD5Context *ctx, uint8_t *input_buffer, size_t input_len){
uint32_t input[16];
unsigned int offset = ctx->size % 64;
ctx->size += (uint64_t)input_len;
// Copy each byte in input_buffer into the next space in our context input
for(unsigned int i = 0; i < input_len; ++i){
ctx->input[offset++] = (uint8_t)*(input_buffer + i);
// If we've filled our context input, copy it into our local array input
// then reset the offset to 0 and fill in a new buffer.
// Every time we fill out a chunk, we run it through the algorithm
// to enable some back and forth between cpu and i/o
if(offset % 64 == 0){
for(unsigned int j = 0; j < 16; ++j){
// Convert to little-endian
// The local variable `input` our 512-bit chunk separated into 32-bit words
// we can use in calculations
input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |
(uint32_t)(ctx->input[(j * 4) + 2]) << 16 |
(uint32_t)(ctx->input[(j * 4) + 1]) << 8 |
(uint32_t)(ctx->input[(j * 4)]);
}
md5Step(ctx->buffer, input);
offset = 0;
}
}
}
/*
* Pad the current input to get to 448 bytes, append the size in bits to the very end,
* and save the result of the final iteration into digest.
*/
void md5Finalize(MD5Context *ctx){
uint32_t input[16];
unsigned int offset = ctx->size % 64;
unsigned int padding_length = offset < 56 ? 56 - offset : (56 + 64) - offset;
// Fill in the padding and undo the changes to size that resulted from the update
md5Update(ctx, PADDING, padding_length);
ctx->size -= (uint64_t)padding_length;
// Do a final update (internal to this function)
// Last two 32-bit words are the two halves of the size (converted from bytes to bits)
for(unsigned int j = 0; j < 14; ++j){
input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |
(uint32_t)(ctx->input[(j * 4) + 2]) << 16 |
(uint32_t)(ctx->input[(j * 4) + 1]) << 8 |
(uint32_t)(ctx->input[(j * 4)]);
}
input[14] = (uint32_t)(ctx->size * 8);
input[15] = (uint32_t)((ctx->size * 8) >> 32);
md5Step(ctx->buffer, input);
// Move the result into digest (convert from little-endian)
for(unsigned int i = 0; i < 4; ++i){
ctx->digest[(i * 4) + 0] = (uint8_t)((ctx->buffer[i] & 0x000000FF));
ctx->digest[(i * 4) + 1] = (uint8_t)((ctx->buffer[i] & 0x0000FF00) >> 8);
ctx->digest[(i * 4) + 2] = (uint8_t)((ctx->buffer[i] & 0x00FF0000) >> 16);
ctx->digest[(i * 4) + 3] = (uint8_t)((ctx->buffer[i] & 0xFF000000) >> 24);
}
}
/*
* Functions that run the algorithm on the provided input and put the digest into result.
* result should be able to store 16 bytes.
*/
void md5String(char *input, uint8_t *result){
MD5Context ctx;
md5Init(&ctx);
md5Update(&ctx, (uint8_t *)input, strlen(input));
md5Finalize(&ctx);
memcpy(result, ctx.digest, 16);
}
void md5File(FILE *file, uint8_t *result){
char *input_buffer = malloc(1024);
size_t input_size = 0;
MD5Context ctx;
md5Init(&ctx);
while((input_size = fread(input_buffer, 1, 1024, file)) > 0){
md5Update(&ctx, (uint8_t *)input_buffer, input_size);
}
md5Finalize(&ctx);
free(input_buffer);
memcpy(result, ctx.digest, 16);
}
/**
* RAS 104-bit permutation
*
* Generates a permutation of bit positions 0..103 using the 32-bit seed
* (the last four active key bytes interpreted as a big-endian uint32).
*
* Algorithm:
* - Maintain a circular list of the still-available positions.
* - Repeatedly select (seed % remaining_count) steps from the current
* position, record that index, remove it, and continue from its successor.
*
* Parameters:
* seed - 32-bit seed (e.g. 0x92604BD5)
* order - output array of 104 integers. order[i] = source bit position
* that should be placed at output bit i.
*/
void ras_permute_order(uint32_t seed, int * order)
{
int remaining[104];
int count = 104;
int pos = 0; /* current index inside the remaining[] array */
for (int i = 0; i < 104; i++)
remaining[i] = i;
for (int i = 0; i < 104; i++)
{
unsigned idx = seed % (unsigned)count;
int actual = (pos + (int)idx) % count;
order[i] = remaining[actual];
#ifdef DEBUG_RAS
//debug
// fprintf (stderr, "\n I: %d; C: %d; P: %d; IDX: %u; ACT: %d; ORD: %d; ", i, count, pos, idx, actual, order[i]);
#endif
/* remove the chosen entry */
for (int j = actual; j < count - 1; j++)
remaining[j] = remaining[j + 1];
count--;
/* continue from the successor of the removed element */
if (count > 0)
pos = actual % count;
}
#ifdef DEBUG_RAS
//debug
// fprintf(stderr, "\n Order: ");
// for (int i = 0; i < 104; i++)
// fprintf (stderr, "%03d,", order[i]);
#endif
}
void apply_ras_permutation(const uint8_t * src, uint8_t * dst, const int * order)
{
for (int i = 0; i < 104; i++)
{
int src_bit = order[i];
int src_byte = src_bit / 8;
int src_off = 7 - (src_bit % 8); /* MSB of byte is lowest bit index */
int bit = (src[src_byte] >> src_off) & 1;
int dst_byte = i / 8;
int dst_off = 7 - (i % 8);
if (bit)
dst[dst_byte] |= (uint8_t)(1u << dst_off);
}
}
//Full RAS workflow, return value is the the calculated MAC attachment
//input is a pointer to byte-wise payload from dburst of RAS enabled payloads
uint32_t ras_mac_calculator(dsd_state * state, uint8_t * input, int input_len, int type)
{
if (state->ras_effective_key == 0)
return 0;
uint32_t mac = 0;
uint8_t ras_input[13];
memset (ras_input, 0, sizeof(ras_input));
for (int i = 0; i < input_len; i++)
ras_input[i] = input[i];
int shift = 48;
for (int i = input_len; i < 13; i++)
{
ras_input[i] = (state->ras_effective_key >> shift) & 0xFF;
shift -= 8;
}
#ifdef DEBUG_RAS
//debug input value
if (type != 2)
{
fprintf (stderr, "\n RAS Input: ");
for (int i = 0; i < 13; i++)
fprintf (stderr, "%02X", ras_input[i]);
}
#endif
uint8_t ras_output[16]; memset(ras_output, 0, sizeof(ras_output));
if (type != 2)
{
apply_ras_permutation(ras_input, ras_output, state->ras_permutation_order);
#ifdef DEBUG_RAS
//debug output value
fprintf (stderr, "\n RAS Output: ");
for (int i = 0; i < 13; i++)
fprintf (stderr, "%02X", ras_output[i]);
#endif
}
//Run Appropriate CRC or RS calculation on Permuted Output
uint8_t output_bits[104]; memset(output_bits, 0, sizeof(output_bits));
unpack_byte_array_into_bit_array(ras_output, output_bits, 13);
if (type == 0)
mac = ComputeCrcCCITT16d(output_bits, 13*8);
else if (type == 1)
{
rs_16_13_encode(ras_output);
for (int i = 0; i < 3; i++)
{
mac <<= 8;
mac |= ras_output[i+13];
}
}
else if (type == 2)
{
mac = ComputeCrc5Bit(input);
mac += state->ras_effective_key & 0x1F;
mac %= 31;
}
return mac;
}
void ras_effective_key_creation(dsd_state * state, char * input)
{
uint8_t md5_key[30];
memset (md5_key, 0, sizeof(md5_key));
uint16_t len = strlen((const char*)input);
//sanity check on provided char len
if (len > 24)
len = 24;
//load chars as uint8_t into key array
for (int i = 0; i < len; i++)
md5_key[i] = (uint8_t)input[i];
//debug
fprintf (stderr, "RAS Key String: %s; LEN: %d; HEX: ", input, len);
for (int i = 0; i < len; i++)
fprintf (stderr, "%02X ", md5_key[i]);
fprintf (stderr, "\n");
MD5Context ctx;
md5Init(&ctx);
md5Update(&ctx, md5_key, len);
md5Finalize(&ctx);
//debug dump the digest
fprintf (stderr, "MD5: ");
for (int i = 0; i < 16; i++)
fprintf (stderr, "%02X ", ctx.digest[i]);
fprintf (stderr, "\n");
//The following rules are still not entirely clear, however,
//the first one executed (last byte) has been observed to be
//true, the SEED values are more speculative, values of
//0 and 0x1F do not change the 5-bit checksum mod 31 (0x1F)
//Furthermore, it is still unclear how any rollover is handled,
//if adding 2 is byte inclusive (effectively resetting the byte)
//or if the value is flat 56-bit value and rollover occurs
//to the next value over, using best guess on this
//until further informatin comes along
//Below patent also suggest all bytes cannot equal zero
//and if so, the next portion of MD5 may be used,
//or some other method may be utilized to prevent fringe cases
//https://patents.google.com/patent/US20130288643A1
//Last Byte low5 not allowed to be 0x1F (reasons unclear, but observed true)
if ((ctx.digest[6] & 0x1F) == 0x1F)
{
fprintf (stderr, " Byte %d: %02X -> %02X; ", 7, ctx.digest[6], ctx.digest[6]+2);
ctx.digest[6] += 2;
}
//SEED low5 non-zero value (no change to 5-bit EMB checksum mod 31)
if ((ctx.digest[0] & 0x1F) == 0)
{
fprintf (stderr, " Byte %d: %02X -> %02X; ", 1, ctx.digest[0], ctx.digest[0]+2);
ctx.digest[0] += 2;
}
//SEED low5 plus 1F (no change to 5-bit EMB checksum mod 31)
else if ((ctx.digest[0] & 0x1F) == 0x1F)
{
fprintf (stderr, " Byte %d: %02X -> %02X; ", 1, ctx.digest[0], ctx.digest[0]+2);
ctx.digest[0] += 2;
}
unsigned long long int effective_key = 0;
for (int i = 0; i < 7; i++)
{
effective_key <<= 8;
effective_key |= ctx.digest[6-i];
}
uint32_t ras_seed = effective_key & 0xFFFFFFFF;
ras_permute_order(ras_seed, state->ras_permutation_order);
//debug
fprintf (stderr, "RAS Effective Key Value: %014llX;", effective_key);
state->ras_effective_key = effective_key;
fprintf (stderr, "\n");
}

View File

@@ -6,7 +6,7 @@
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
*
* LWVMOBILE
* 2023-12 DSD-FME Florida Man Edition
* 2026-09 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
//TODO: Test USBD LIP Decoder with Real World Samples (if/when available)
@@ -51,14 +51,14 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
//PDU Bytes and Bits
uint8_t DMR_PDU[25];
uint8_t DMR_PDU_bits[196];
uint8_t DMR_PDU_bits[200];
memset (DMR_PDU, 0, sizeof (DMR_PDU));
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
uint8_t R[3];
uint8_t BPTCReservedBits = 0;
uint8_t is_ras = 0;
uint8_t crc_original_validity = 0;
uint8_t is_ras = 0;
uint8_t bptc_res_bits[3];
memset (bptc_res_bits, 0, sizeof(bptc_res_bits));
uint8_t crc_original_validity = 0;
uint32_t crcmask = 0;
uint8_t crclen = 0;
@@ -246,28 +246,27 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
BPTCDeInterleaveDMRData(info, BPTCDeInteleavedData);
/* Extract the BPTC 196,96 DMR data */
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, R);
/* Fill the reserved bit (R(0)-R(2) of the BPTC(196,96) block) */
BPTCReservedBits = (R[0] & 0x01) | ((R[1] << 1) & 0x02) | ((R[2] << 2) & 0x04);
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, bptc_res_bits, &is_ras);
#ifdef DEBUG_RAS
//debug print
//fprintf (stderr, " RAS? %X - %d %d %d", BPTCReservedBits, R[0], R[1], R[2]);
if (bptc_res_bits[0] != 0 || bptc_res_bits[1] != 0 || bptc_res_bits[2] != 0)
fprintf (stderr, " BPTC R BITS: %d %d %d", bptc_res_bits[0], bptc_res_bits[1], bptc_res_bits[2]);
#endif
/* Convert the 96 bits BPTC data into 12 bytes */
k = 0;
for(i = 0; i < 12; i++)
{
BPTCDmrDataByte[i] = 0;
for(j = 0; j < 8; j++)
{
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] << 1;
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] | (BPTCDmrDataBit[k] & 0x01);
k++;
}
}
pack_bit_array_into_byte_array(BPTCDmrDataBit, BPTCDmrDataByte, 12);
/* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */
//make sure the system type isn't Hytera, but could just be bad decodes on bad sample
if (IrrecoverableErrors == 0 && BPTCDmrDataByte[1] == 0x68) is_ras = 0;
//make sure if this is link control, the FID is set to 0x10
if (IrrecoverableErrors == 0 && is_lc && BPTCDmrDataByte[1] != 0x10) is_ras = 0;
//make sure if this is a PI header, the FID is set to 0x10
if (IrrecoverableErrors == 0 && databurst == 0x00 && BPTCDmrDataByte[1] != 0x10) is_ras = 0;
/* Fill the CRC extracted */
CRCExtracted = 0;
for(i = 0; i < crclen; i++)
{
@@ -279,7 +278,20 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
CRCExtracted = CRCExtracted ^ crcmask;
/* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */
if (is_lc) CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
if (is_lc)
{
if (is_ras)
{
CRCComputed = ras_mac_calculator(state, BPTCDmrDataByte, 9, 1);
if (CRCComputed == CRCExtracted)
CRCCorrect = 1;
#ifdef DEBUG_RAS
fprintf (stderr, " EXT: %06X; MAC: %06X;", CRCExtracted, CRCComputed);
#endif
}
else CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
}
//set CRC to correct on unconfirmed 1/2 data blocks (for reporting due to no CRC available on these)
else if (state->data_conf_data[slot] == 0 && databurst == 0x7)
@@ -315,20 +327,23 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
//run CCITT on other data forms
else
{
CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
if (is_ras == 1)
{
CRCComputed = ras_mac_calculator(state, BPTCDmrDataByte, 10, 0);
#ifdef DEBUG_RAS
fprintf (stderr, " EXT: %04X; MAC: %04X;", CRCExtracted, CRCComputed);
#endif
}
else CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
else CRCCorrect = 0;
}
//set the 'RAS Flag', if no irrecoverable errors but bad crc, only when enabled by user (to prevent a lot of bad data CSBKs)
if (opts->aggressive_framesync == 0 && CRCCorrect == 0 && IrrecoverableErrors == 0 && BPTCReservedBits == 4) is_ras = 1;
//make sure the system type isn't Hytera, but could just be bad decodes on bad sample
if (BPTCDmrDataByte[1] == 0x68) is_ras = 0;
//if this is a RAS system, set the CRC to okay if irrecoverable errors are okay
//if we don't do this, then we can't view some data (CSBKs on RAS enabled systems)
if (is_ras == 1)
if (is_ras == 1 && state->ras_effective_key == 0)
{
crc_original_validity = CRCCorrect;
CRCCorrect = 1;
@@ -344,17 +359,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
}
/* Convert corrected x bytes into x*8 bits */
for(i = 0, j = 0; i < pdu_len; i++, j+=8)
{
BPTCDmrDataBit[j + 0] = (BPTCDmrDataByte[i+pdu_start] >> 7) & 0x01;
BPTCDmrDataBit[j + 1] = (BPTCDmrDataByte[i+pdu_start] >> 6) & 0x01;
BPTCDmrDataBit[j + 2] = (BPTCDmrDataByte[i+pdu_start] >> 5) & 0x01;
BPTCDmrDataBit[j + 3] = (BPTCDmrDataByte[i+pdu_start] >> 4) & 0x01;
BPTCDmrDataBit[j + 4] = (BPTCDmrDataByte[i+pdu_start] >> 3) & 0x01;
BPTCDmrDataBit[j + 5] = (BPTCDmrDataByte[i+pdu_start] >> 2) & 0x01;
BPTCDmrDataBit[j + 6] = (BPTCDmrDataByte[i+pdu_start] >> 1) & 0x01;
BPTCDmrDataBit[j + 7] = (BPTCDmrDataByte[i+pdu_start] >> 0) & 0x01;
}
unpack_byte_array_into_bit_array(BPTCDmrDataByte, BPTCDmrDataBit, pdu_len);
//convert to DMR_PDU and DMR_PDU_bits
for (i = 0; i < pdu_len; i++)
@@ -407,6 +412,16 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
/* Compute the 5 bit CRC */
CRCComputed = ComputeCrc5Bit(LC_DataBit);
//read ahead first two bytes, look for 1X10 or 2X10 pattern
uint16_t ras_test = (uint16_t)(ConvertBitIntoBytes(&LC_DataBit[0], 16) & 0x30FF);
if ((IrrecoverableErrors == 0) && (ras_test == 0x2010 || ras_test == 0x1010))
{
is_ras = 1;
if (state->ras_effective_key != 0)
CRCComputed = ras_mac_calculator(state, LC_DataBit, 9, 2);
}
else is_ras = 0;
if(CRCExtracted == CRCComputed) CRCCorrect = 1;
else CRCCorrect = 0;
@@ -593,19 +608,8 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
else fprintf (stderr, "Reserved %d ", usbd_st);
}
//set the original CRCCorrect back to its original value if the RAS flag was tripped
if (is_ras == 1) CRCCorrect = crc_original_validity;
//start printing relevant fec/crc/ras messages, don't print on idle or MBC continuation blocks (handled in dmr_block.c)
// if (IrrecoverableErrors == 0 && CRCCorrect == 1 && databurst != 0x09 && databurst != 0x05) fprintf(stderr, "(CRC OK)");
// if (IrrecoverableErrors == 0 && CRCCorrect == 0 && databurst != 0x09 && databurst != 0x05)
// {
// fprintf (stderr, "%s", KYEL);
// fprintf(stderr, " (FEC OK)");
// fprintf (stderr, "%s", KNRM);
// }
//set the original CRCCorrect back to its original value if the RAS flag was tripped (if no key value present)
if (is_ras == 1 && state->ras_effective_key == 0) CRCCorrect = crc_original_validity;
if (IrrecoverableErrors != 0 && databurst != 0x08 && databurst != 0x09) //&& databurst != 0x05
{
@@ -614,16 +618,25 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
fprintf (stderr, "%s", KNRM);
}
//print whether or not the 'RAS Field' bits are set to indicate RAS enabled (to be verified)
if (is_ras == 1)
//print whether or not the 'RAS Field' bit is set to indicate RAS
//detected for RAS enabled burst types and disposition if key available
if (IrrecoverableErrors == 0 && is_ras == 1 && (is_bptc || is_emb) && databurst != 0x05 && databurst != 0x07)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, " -RAS ");
//the value of this field seems to always, or usually, be 4, or just R[2] bit is set
if (opts->payload == 1) fprintf (stderr, "%X ", BPTCReservedBits);
fprintf (stderr, " {RAS ");
if (state->ras_effective_key != 0 && CRCComputed != CRCExtracted)
fprintf (stderr, "Fail} ");
else if (state->ras_effective_key != 0 && CRCComputed == CRCExtracted)
fprintf (stderr, "Okay} ");
else if (state->ras_effective_key == 0)
fprintf (stderr, "Skip} ");
fprintf (stderr, "%s", KNRM);
}
//might flicker the terminal on partially enabled systems, may need to find a better placement
if (IrrecoverableErrors == 0 && (is_bptc || is_emb) && databurst != 0x05 && databurst != 0x07)
state->dmr_is_ras = is_ras;
if (IrrecoverableErrors == 0 && CRCCorrect == 0 && is_ras == 0 && databurst != 0x09 && databurst != 0x05)
{
fprintf (stderr, "%s", KRED);
@@ -639,9 +652,24 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
fprintf (stderr, " DMR PDU Payload ");
for (i = 0; i < pdu_len; i++)
{
#ifdef DEBUG_RAS
fprintf (stderr, "%02X", DMR_PDU[i]);
#else
fprintf (stderr, "[%02X]", DMR_PDU[i]);
#endif
}
//RAS 5-bit - always same differential
//calculation should be 5-bit portion of RAS Effective Key's Last byte & 0x1F
//5-bit + CRCComputed % 31 = CRCExtracted
#ifdef DEBUG_RAS
if (opts->payload == 1 && is_emb && is_ras && CRCExtracted != CRCComputed && state->ras_effective_key == 0)
{
int32_t diff = ((int32_t)CRCExtracted - (int32_t)CRCComputed + 31) % 31;
fprintf (stderr, " CRC CMP: %02X EXT: %02X DIFF: %02X", CRCComputed, CRCExtracted, diff);
}
#endif
//debug print
// if (dbsn) fprintf (stderr, " SN %X", dbsn);
// fprintf (stderr, " CRC - EXT %X CMP %X", CRCExtracted, CRCComputed);

View File

@@ -32,6 +32,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
int is_alias = 0;
int is_gps = 0;
UNUSED(capsite);
int is_ras = 0;
//XPT 'Things'
int is_xpt = 0;
@@ -117,6 +118,17 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
if (slot == 0) state->dmr_flco = flco;
else state->dmr_flcoR = flco;
//debug output for potential RAS indicators on Moto Systems during EMB (0x2X and 0x1X indicators)
//TODO: Either leave handling of link control as is, or rework entire file to handle all the exceptions
//that have been added to it over the years.
if ( (flco & 0x30) && (fid == 0x10) && (type == 3) )
{
is_ras = 1;
UNUSED(is_ras);
// flco &= 0xCF; //mask off the RAS bits
//NOTE: Mask breaks below check on talker alias on RAS systems WIP: Fixing That
}
//FID 0x10 and FLCO 0x14, 0x15, 0x16 and 0x17 confirmed as Moto EMB Alias
//Can probably assume FID 0x10 FLCO 0x18 is Moto EMB GPS -- to be tested
if ( fid == 0x10 && (flco == 0x14 || flco == 0x15 || flco == 0x16 || flco == 0x17 || flco == 0x18) )
@@ -126,7 +138,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
}
//Embedded Talker Alias Header Only (format and len storage)
if ( (fid == 0 || fid == 0x68) && type == 3 && flco == 0x04)
if ((fid == 0 || fid == 0x68) && type == 3 && flco == 0x04)
{
is_alias = 1;
dmr_talker_alias_lc_header(opts, state, slot, lc_bits);
@@ -140,7 +152,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
}
//Embedded GPS
if ( (fid == 0 || fid == 0x68) && fid == 0 && type == 3 && flco == 0x08)
if ((fid == 0 || fid == 0x68) && type == 3 && flco == 0x08)
{
is_gps = 1;
dmr_embedded_gps(opts, state, lc_bits);
@@ -502,9 +514,9 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
//0x04 and 0x05 on a TLC seem to indicate a Cap + Private Call Terminator (perhaps one for each MS)
//0x07 on a VLC seems to indicate a Cap+ Private Call Header
//0x23 on the Embedded Voice Burst Sync seems to indicate a Cap+ or Cap+ TXI Private Call in progress
//0x20 on the Embedded Voice Burst Sync seems to indicate a Moto (non-specific) Group Call in progress
//its possible that both EMB FID 0x10 FLCO 0x20 and 0x23 are just Moto but non-specific (observed 0x20 on Tier 2)
//0x23 on the Embedded Voice Burst Sync seems to indicate a Cap+ or Cap+ TXI Private Call in progress (RAS)
//0x20 on the Embedded Voice Burst Sync seems to indicate a Moto (non-specific) Group Call in progress (RAS)
//its possible that both EMB FID 0x10 FLCO 0x20 and 0x23 are just Moto but non-specific (RAS)
if (fid == 0x68) sprintf (state->call_string[slot], " Hytera ");
@@ -1191,7 +1203,13 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
}
else if (slco == 0xF)
else if (slco == 0xE)
{
uint32_t update_tgt = (uint32_t)ConvertBitIntoBytes(&slco_bits[4], 24);
fprintf (stderr, " SLCO Cap Max Activity Update; TGT: %d;", update_tgt);
}
else if (slco == 0xF) //This opcode has also been observed on Cap Max Payload Channels
{
fprintf (stderr, " SLCO Capacity Plus Site: %d - Rest LSN: %d - RS: %02X", capsite, restchannel, cap_reserved);

View File

@@ -1234,6 +1234,11 @@
//Remus DMR End Call Alert Beep
state->dmr_end_alert[0] = 0;
state->dmr_end_alert[1] = 0;
//RAS
memset (state->ras_permutation_order, 0, sizeof(state->ras_permutation_order));
state->ras_effective_key = 0;
state->dmr_is_ras = 0;
sprintf (state->dmr_branding, "%s", "");
sprintf (state->dmr_branding_sub, "%s", "");
@@ -1452,7 +1457,7 @@
printf (" If using /dev/dsp input and output at 48k1, launch two instances of DSD-FME w -V 1 and -V 2 if needed\n");
#endif
printf (" -z Set TDMA Voice Slot Preference when using /dev/dsp audio output (prevent lag and stuttering)\n");
printf (" -y Enable Experimental Pulse Audio Float Audio Output\n");
// printf (" -y Enable Experimental Pulse Audio Float Audio Output\n");
printf (" -v <hex> Set Filtering Bitmap Options (Advanced Option)\n");
printf (" 1 1 1 1 (0xF): PBF/LPF/HPF/HPFD on\n");
printf ("\n");
@@ -1571,6 +1576,12 @@
printf (" \n");
printf (" -A <hex> Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value) \n");
printf (" \n");
printf (" -$ <string> Enter RAS Password 6-24 Char Key Value (DMR) (String Value) \n");
printf (" -$ Password123\n");
printf (" \n");
printf (" -y <hex> Enter RAS 56-bit Effective Key Value (DMR) (14 Hex Value) \n");
printf (" -y 6DF241BC4AE2EC\n");
printf (" \n");
printf (" -, <string> Manually Enter and Enforce Auctus A6 (GoComm) 8 Char Key Value (DMR) (String Value) \n");
printf (" -, Secure04\n");
printf (" \n");
@@ -1901,7 +1912,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:M:NQ:WrlZTF@:!:01:2:345:6:^:7:8_:9:Ek:I:J:O+:j:,:")) != -1)
while ((c = getopt (argc, argv, "~haepPqs: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+:j:,:$:y:")) != -1)
{
switch (c)
@@ -2025,6 +2036,19 @@
auctus_keystream_creation(&state, optarg);
break;
//RAS Effective Key Creation
case '$':
ras_effective_key_creation(&state, optarg);
break;
//RAS Effective Key Direct Set
case 'y':
sscanf (optarg, "%llX", &state.ras_effective_key);
state.ras_effective_key &= 0xFFFFFFFFFFFFFF; //truncate to 56-bits
ras_permute_order((state.ras_effective_key & 0xFFFFFFFF), state.ras_permutation_order);
fprintf (stderr,"DMR RAS 56-bit Effective Key: %llX\n", state.ras_effective_key);
break;
//Straight KS Generation
case 'S':
straight_mod_xor_keystream_creation(&state, optarg);
@@ -2035,10 +2059,11 @@
fprintf (stderr,"DMRA Late Entry Encryption Identifiers Disabled\n");
break;
case 'y': //use experimental 'float' audio output
opts.floating_point = 1; //enable floating point audio output
fprintf (stderr,"Enabling Experimental Floating Point Audio Output\n");
break;
//float audio experiment not going to be used going forward, very minimal benefit
// case 'y': //use experimental 'float' audio output
// opts.floating_point = 1; //enable floating point audio output
// fprintf (stderr,"Enabling Experimental Floating Point Audio Output\n");
// break;
case 'Y': //conventional scanner mode
opts.scanner_mode = 1; //enable scanner

View File

@@ -921,10 +921,16 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw ("Freq: %.06lf MHz", (double)state->p25_cc_freq/1000000);
}
if (state->dmr_is_ras == 1)
printw ("{RAS}");
}
else if (lls == 32 || lls == 33 || lls == 34)
{
printw ("DMR MS - DCC: %02i; ", state->dmr_color_code);
if (state->dmr_is_ras == 1)
printw ("{RAS}");
}
else if (lls == 0 || lls == 1) //P1
{

View File

@@ -314,3 +314,28 @@ rs_12_9_checksum_t *rs_12_9_calc_checksum(rs_12_9_codeword_t *codeword) {
}
return &rs_12_9_checksum;
}
//This will run a RS 12,9 encode on 9 bytes of input
//input is an array of 12 bytes or more
void rs_12_9_encode(uint8_t * input)
{
rs_12_9_codeword_t cw;
rs_12_9_checksum_t *cs;
//init cw
memset(cw.data, 0, 12*sizeof(uint8_t));
//init cs
memset(cs->bytes, 0, 3*sizeof(uint8_t));
//Copy 9 bytes of data input
memcpy(cw.data, input, 9);
//Calculate the 3 parity / checksum bytes
cs = rs_12_9_calc_checksum(&cw);
//Build the final 12-byte codeword
input[9] = cs->bytes[0];
input[10] = cs->bytes[1];
input[11] = cs->bytes[2];
}

123
src/rs-16-13.c Normal file
View File

@@ -0,0 +1,123 @@
/*
* RS(16,13) encoder
*
* 13 data bytes + 3 parity bytes = 16-byte codeword
*
* Uses the same generator polynomial and GF(256) tables as the
* DMR RS(12,9) implementation (DMR AI Spec / dmrshark / dsd):
*
* g(x) = x^3 + 0x40 x^2 + 0x38 x + 0x0e
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "dsd.h"
/* -----------------------------------------------------------------------
* Constants
* ----------------------------------------------------------------------- */
#define RS_DATASIZE 13
#define RS_CHECKSUMSIZE 3
#define RS_CODEWORDSIZE (RS_DATASIZE + RS_CHECKSUMSIZE) /* 16 */
/* -----------------------------------------------------------------------
* Galois Field tables (GF(256) – same as DMR RS(12,9))
* ----------------------------------------------------------------------- */
static const uint8_t galois_exp_table[256] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1D, 0x3A, 0x74, 0xE8, 0xCD, 0x87, 0x13, 0x26,
0x4C, 0x98, 0x2D, 0x5A, 0xB4, 0x75, 0xEA, 0xC9, 0x8F, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0,
0x9D, 0x27, 0x4E, 0x9C, 0x25, 0x4A, 0x94, 0x35, 0x6A, 0xD4, 0xB5, 0x77, 0xEE, 0xC1, 0x9F, 0x23,
0x46, 0x8C, 0x05, 0x0A, 0x14, 0x28, 0x50, 0xA0, 0x5D, 0xBA, 0x69, 0xD2, 0xB9, 0x6F, 0xDE, 0xA1,
0x5F, 0xBE, 0x61, 0xC2, 0x99, 0x2F, 0x5E, 0xBC, 0x65, 0xCA, 0x89, 0x0F, 0x1E, 0x3C, 0x78, 0xF0,
0xFD, 0xE7, 0xD3, 0xBB, 0x6B, 0xD6, 0xB1, 0x7F, 0xFE, 0xE1, 0xDF, 0xA3, 0x5B, 0xB6, 0x71, 0xE2,
0xD9, 0xAF, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0D, 0x1A, 0x34, 0x68, 0xD0, 0xBD, 0x67, 0xCE,
0x81, 0x1F, 0x3E, 0x7C, 0xF8, 0xED, 0xC7, 0x93, 0x3B, 0x76, 0xEC, 0xC5, 0x97, 0x33, 0x66, 0xCC,
0x85, 0x17, 0x2E, 0x5C, 0xB8, 0x6D, 0xDA, 0xA9, 0x4F, 0x9E, 0x21, 0x42, 0x84, 0x15, 0x2A, 0x54,
0xA8, 0x4D, 0x9A, 0x29, 0x52, 0xA4, 0x55, 0xAA, 0x49, 0x92, 0x39, 0x72, 0xE4, 0xD5, 0xB7, 0x73,
0xE6, 0xD1, 0xBF, 0x63, 0xC6, 0x91, 0x3F, 0x7E, 0xFC, 0xE5, 0xD7, 0xB3, 0x7B, 0xF6, 0xF1, 0xFF,
0xE3, 0xDB, 0xAB, 0x4B, 0x96, 0x31, 0x62, 0xC4, 0x95, 0x37, 0x6E, 0xDC, 0xA5, 0x57, 0xAE, 0x41,
0x82, 0x19, 0x32, 0x64, 0xC8, 0x8D, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0xDD, 0xA7, 0x53, 0xA6,
0x51, 0xA2, 0x59, 0xB2, 0x79, 0xF2, 0xF9, 0xEF, 0xC3, 0x9B, 0x2B, 0x56, 0xAC, 0x45, 0x8A, 0x09,
0x12, 0x24, 0x48, 0x90, 0x3D, 0x7A, 0xF4, 0xF5, 0xF7, 0xF3, 0xFB, 0xEB, 0xCB, 0x8B, 0x0B, 0x16,
0x2C, 0x58, 0xB0, 0x7D, 0xFA, 0xE9, 0xCF, 0x83, 0x1B, 0x36, 0x6C, 0xD8, 0xAD, 0x47, 0x8E, 0x01
};
static const uint8_t galois_log_table[256] = {
0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75,
4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113,
5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69,
29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166,
6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136,
54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64,
30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61,
202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87,
7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24,
227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46,
55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97,
242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162,
31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246,
108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90,
203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215,
79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175
};
/* -----------------------------------------------------------------------
* Galois multiplication
* ----------------------------------------------------------------------- */
static uint8_t galois_mult(uint8_t a, uint8_t b)
{
if (a == 0 || b == 0)
return 0;
return galois_exp_table[(galois_log_table[a] + galois_log_table[b]) % 255];
}
/* -----------------------------------------------------------------------
* RS(16,13) encoder – LFSR with the DMR generator polynomial
*
* genpoly coefficients (same as RS(12,9)):
* { 0x40, 0x38, 0x0e, 0x01 }
* ----------------------------------------------------------------------- */
void rs16_13_encode(const uint8_t data_in[RS_DATASIZE], uint8_t codeword[RS_CODEWORDSIZE])
{
/* Generator polynomial coefficients from DMR AI Spec */
static const uint8_t genpoly[] = { 0x40, 0x38, 0x0e, 0x01 };
uint8_t parity[3] = { 0, 0, 0 };
uint8_t i;
uint8_t feedback;
for (i = 0; i < RS_DATASIZE; i++) {
feedback = data_in[i] ^ parity[0];
parity[0] = parity[1] ^ galois_mult(genpoly[2], feedback);
parity[1] = parity[2] ^ galois_mult(genpoly[1], feedback);
parity[2] = galois_mult(genpoly[0], feedback);
}
/* Build systematic codeword: data || parity */
memcpy(codeword, data_in, RS_DATASIZE);
codeword[13] = parity[0];
codeword[14] = parity[1];
codeword[15] = parity[2];
}
//This will run a RS 16,13 encode on 13 bytes of input
//input is an array of 16 bytes or more
void rs_16_13_encode(uint8_t * input)
{
uint8_t output[16];
memset(output, 0, sizeof(output));
//Calculate the 3 parity / checksum bytes
rs16_13_encode(input, output);
//append the encoded parity to the input
input[13] = output[13];
input[14] = output[14];
input[15] = output[15];
}