39 Commits

Author SHA1 Message Date
lwvmobile
34753963b8 DMR: Add Keyloading for Hytera PDU Decryption based on Data Header Key ID first, then manually set key. #372 2026-11-07 20:22:23 -05:00
lwvmobile
544d576551 DMR: Fix Hytera OTA Encryption VLC Pseudo Call Information TGT Value on Slot 2. Verbage Tweaks; #373
DMR: Fix Kirisun "Ultra" Cipher Name in Code (Ultra, not Ultimate). Remove Unvalidated Code Bits regarding Kirisun; #319
2026-11-07 19:35:47 -05:00
lwvmobile
72f8c1aff7 Merge branch 'kirisun3' into aw_dev 2026-11-06 22:14:02 -05:00
lwvmobile
bfa4891965 DMR: Hytera Over the Air Encryption (Signalling Encryption) Support; #373
DMR: Fix Hytera PDU Decryption to operate at Block Level; (Fixes confirmed data); #372

DMR: Hytera Enhanced RC4 allows variable len output for OTA, PDU or Voice;

DMR: Fix Segfault in RS12,9 encoder function where freeing static memory caused a crash;

Misc Code tweaks to accomodate new features;
2026-11-06 21:22:38 -05:00
lwvmobile
daa47069bf DMR: Add More Kirisun Encryption Support; #319
Added Ultimate Cipher;
2026-10-30 10:42:34 -04:00
lwvmobile
587181f4de DMR: Add Hytera PDU Encryption Support; #372
DMR: UDP Comp Data Calls use Data header SRC and TGT values for Event History; Fixes Text Messages not populating Event History; Simplify Event String;
2026-10-21 11:48:59 -04:00
lwvmobile
eb8048f1d9 Merge branch 'cygwin_bat_fix' into aw_dev 2026-10-20 19:31:41 -04:00
lwvmobile
d8f9bd190b Merge pull request #371 from ryandrake08/rtltcp-input
Fix divide-by-zero and buffer overruns in getFrameSync()
2026-10-19 16:25:31 -04:00
Ryan Drake
6880087afe Fix buffer underruns in getFrameSync() 2026-10-19 12:52:19 -07:00
Ryan Drake
6106a608e3 Fix divide-by-zero when result_len is zero 2026-10-19 12:52:19 -07:00
lwvmobile
1aae6d34c9 CYGWIN: Fix default bat file date and time string to work independent of locale settings (uses robocopy); #370 2026-10-19 10:45:03 -04:00
lwvmobile
be57b5149b RTL_TCP: Add Ncurses Input Print; #368
Add Usage to docs;
2026-10-18 12:07:41 -04:00
lwvmobile
3e221f29f5 Merge pull request #369 from ryandrake08/rtltcp-input
rtl_tcp input support
2026-10-17 16:42:02 -04:00
Ryan Drake
4754f6a59b Include sys/socket.h for the UDP tuning socket 2026-10-17 11:08:40 -07:00
Ryan Drake
7027b145b8 Add interruptible timeouts for stalled rtl_tcp servers 2026-10-17 10:19:43 -07:00
Ryan Drake
f38b5976f3 Add rtl_tcp input: -i rtltcp:host:port:freq:gain:ppm:bw:sq:vol 2026-10-17 09:55:27 -07:00
lwvmobile
5244c85774 DMR: Fix Kirisun Keystream Creation Per Slot; #319
Use Full Name of Each Kirisun Cipher in Code;
2026-10-17 09:21:46 -04:00
lwvmobile
4005c9b313 DMR: Add More Kirisun Encryption Support; #319
Ciphers added include "Enhanced" "Reinforced" and "Strengthened"; Placeholders for remaining ciphers;
2026-10-16 11:04:42 -04:00
lwvmobile
72b3e5efe4 Fix Typo 2026-10-13 20:10:08 -04:00
lwvmobile
a3a6fb999a DMR: Tait Talker Alias Single Block Embedded LC; #367 2026-10-13 19:58:53 -04:00
lwvmobile
37855b817b DMR: Fix MassReg reg_address value and tweaks; #366 2026-10-09 19:42:45 -04:00
lwvmobile
4fe32db7f5 Merge pull request #364 from eremeevdev/docs/macos-install-notes
Add macOS Homebrew Build Notes to Install Notes;
2026-09-26 16:47:03 -04:00
Aleksey Eremeev
6819f9010e Add macOS Homebrew Build Notes to Install Notes; 2026-09-26 22:25:45 +03:00
lwvmobile
719d9709e6 Merge pull request #363 from xdab/perf/use-symbol-top2
use_symbol: replace per-symbol full sort with single-pass top-2 selection
2026-09-22 17:58:43 -04:00
xdab
26e4a619f6 use_symbol: replace per-symbol full sort with single-pass top-2 selection 2026-09-22 22:41:21 +02:00
lwvmobile
0838a7f7ba DMR: Fix Copy and Paste Bug with Caltta BP Unmute and Doc Typos; #360 2026-09-21 22:18:24 -04:00
lwvmobile
a6ea60b345 Merge pull request #362 from xdab/perf/sync-hunt-topn
Frame Sync: Replace per-symbol full sort with single-pass top-5 selection
2026-09-21 18:30:55 -04:00
xdab
9232d66fcb Frame Sync: Replace per-symbol full sort with single-pass top-5 selection
The frame sync hunt loop sorted the entire symbol amplitude window
(lbuf2, t_max entries) with qsort on every iteration only to read
three ranked values off each end. Replace the copy + qsort with a
single O(n) pass maintaining the five smallest and five largest
values in two fixed 5-slot arrays, reading the exact same ranks.

Rank equivalence: lmn[] ascending = sorted[0..], lmx[] descending =
sorted[..t_max-1], so both the short (t_max < 13) and long rank
formulas map 1:1. The now-unused lbuf2 scratch buffer is removed.

Decode output verified byte-identical (timestamps stripped) against
DMR, dPMR, D-Star and YSF sample captures, including runs with a
10 second no-carrier lead-in.
2026-09-21 20:48:33 +02:00
lwvmobile
15515158b5 DMR: Add Support for Callta BP 10/32/64; #360
DMR: Add optional discard value to -S straight_xor keystream application;
2026-09-20 04:31:06 -04:00
lwvmobile
fa4a332582 DMR: Add Special Use Cases for RAS Passwords that use a # or a $ in the help and complete usage doc; #359 2026-09-10 16:32:24 -04:00
lwvmobile
4139b19bfe DMR: RAS MD5 Hash Fringe Case Correction; #359 2026-09-07 15:19:30 -04:00
lwvmobile
cd5f0f4a8e DMR: Remove Condition where VLC and TLC without FID 0x10 did not calculate RAS MAC attachments; Misc Tweaks to dburst RAS Indicator; #359
DMR: Tweak FLCO to remove 0x23 as a Cap+ Private Call when it is only a RAS indicator on a normal Private Call; Clarify Comments;

DMR: Tweak RAS MD5 loading to allow for a correction against a flat value (if needed in future based on observation);

DMR: Misc Tweaks to Vertex Standard Key Loading; #316
2026-09-06 17:57:42 -04:00
lwvmobile
fb3dc75c47 RAS MD5 Tweaks; #359
Disable Fist Byte Correction on MD5 password hash following observations based on real world system;

Improve BPTC127x77 Decoder Logic;

Tweak Debug code to show checksum diff if there is no actual diff;
2026-09-04 21:36:45 -04:00
lwvmobile
f2254f413a Vertex Standard 10 Char and 64 Char Encryption Support; #316
Special mode of 0:0 to apply the inversion to the interlevaed frame if present without application of a keystream for analysis;

Misc Tweaks to VTX STD ENC Header and VTX TMS message;

TG Value loading of VTX 10 char keys available only by #define VERTEX_KEY40_BY_TG which will override the Moto BP loadout;
2026-09-04 15:47:56 -04:00
lwvmobile
c97400a704 RTL: Retry Tuning if Error; #358
This is just a simple spam of the rtlsdr_set_center_freq function if that function returns a negative value indicating failure. Similar retries are present for gain and ppm set as well.

This is primarily to address issues that have been noted to arise on USB 2.0 ports on some AM4 motherboard / chipset configurations, prevalently, Ryzen CPUs with the 3XX, 4XX, and 5XX motherboards. This may also address other misc issues due to bad USB cables (within reason) as long as a retry will suffice to allow proper tuning;

Upstream rtl-sdr repo was updated to 2.0.3 previously as well to allow for the V4L version dongles to work correctly when compiled in Cygwin. Linux users will need to update their own rtl-sdr versions;
2026-09-01 14:38:11 -04:00
lwvmobile
cc6711fe4d 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.
2026-09-01 13:29:55 -04:00
lwvmobile
198f0eacb5 DMR: Auctus / GoComm Encryption Support w/ Forced Application; #346
Use -, Secure04 to use this mode;
2026-08-14 03:05:21 -04:00
lwvmobile
5e5c3c4555 MBE: Tweaks to DMRA Late Entry IV (change run order to align w/ superframe) for SDRTrunk .mbe files; #298
MBE: Fixes for non forced DMRA supported decryption modes (with supplied IV + expansion in mbe file, etc);

MBE: Support for DMRA DES Mode (rare and untested for .mbe files);
2026-07-20 23:02:17 -04:00
lwvmobile
272f17bfa3 MBE: Add DMRA Late Entry IV and Forced AES Alg Decryption support (w/ known key) for SDRTrunk .mbe files; #298 2026-07-20 11:31:31 -04:00
37 changed files with 5705 additions and 757 deletions

View File

@@ -134,6 +134,7 @@ FILE(GLOB HEADERS include/*.h include/*.hpp)
if(NOT RTLSDR_FOUND) if(NOT RTLSDR_FOUND)
list(REMOVE_ITEM SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/rtl_sdr_fm.cpp) list(REMOVE_ITEM SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/rtl_sdr_fm.cpp)
list(REMOVE_ITEM SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/rtl_sdr_tcp.c)
endif(NOT RTLSDR_FOUND) endif(NOT RTLSDR_FOUND)
configure_file("src/git_ver.c.in" "${CMAKE_CURRENT_BINARY_DIR}/git_ver.c" @ONLY) configure_file("src/git_ver.c.in" "${CMAKE_CURRENT_BINARY_DIR}/git_ver.c" @ONLY)

View File

@@ -1,9 +1,9 @@
# Digital Speech Decoder - Florida Man Edition # 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) ![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/audio_work/dsd-fme2.png)

View File

@@ -108,6 +108,46 @@ LD_LIBRARY_PATH=/usr/local/lib
export LD_LIBRARY_PATH export LD_LIBRARY_PATH
``` ```
### macOS Builds (Homebrew)
Building on macOS (Apple Silicon) using [Homebrew](https://brew.sh "Homebrew") for dependencies has been tested working, but keep in mind this project is primarily focused on Linux, so macOS support should be considered experimental. Unlike the Linux steps above, macOS has no `ldconfig`, so simply skip that step wherever it appears below.
Install dependencies first:
```
brew install cmake git libsndfile pulseaudio rtl-sdr codec2 ncurses pkg-config
```
Note: Homebrew's ncurses is keg-only (not symlinked into /opt/homebrew), so we will pass an extra CMake flag when configuring DSD-FME below so it can be found.
MBELib is considered a requirement in this build. You must read this notice prior to continuing. [MBElib Patent Notice](https://github.com/lwvmobile/mbelib#readme "MBElib Patent Notice")
```
git clone https://github.com/lwvmobile/mbelib
cd mbelib
git checkout ambe_tones
mkdir build
cd build
cmake ..
make -j `sysctl -n hw.ncpu`
sudo make install
cd ../..
```
Finish by cloning and building DSD-FME (note the added -DCMAKE_PREFIX_PATH flag for keg-only ncurses):
```
git clone https://github.com/lwvmobile/dsd-fme
cd dsd-fme
mkdir build
cd build
cmake .. -DCMAKE_PREFIX_PATH=$(brew --prefix ncurses)
make -j `sysctl -n hw.ncpu`
sudo make install
```
Pulse Audio is still the default input/output in DSD-FME, so start the Homebrew Pulse Audio daemon before running dsd-fme (it will also restart at login): `brew services start pulseaudio`. To confirm your RTL-SDR dongle is detected, run `rtl_test` (ships with Homebrew's rtl-sdr package); unlike Linux, there is no driver blacklist to deal with on macOS.
### Windows Cygwin Builds ### Windows Cygwin Builds
Cygwin builds now have an experimental semi-automatic installer, to run, follow steps below: Cygwin builds now have an experimental semi-automatic installer, to run, follow steps below:

View File

@@ -1,4 +1,4 @@
Build Version: AW 2026-30-g43d63b2 Build Version: AW 2026-47-ga6ea60b
MBElib Version: 1.3.4 MBElib Version: 1.3.4
CODEC2 Support Enabled CODEC2 Support Enabled
@@ -21,6 +21,7 @@ Input/Output options:
pulse:6 or pulse:virtual_sink2.monitor for pulse audio signal input on virtual_sink2 (see -O) pulse:6 or pulse:virtual_sink2.monitor for pulse audio signal input on virtual_sink2 (see -O)
rtl for rtl dongle (Default Values -- see below) rtl for rtl dongle (Default Values -- see below)
rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below) rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below)
rtltcp:host:port:freq:gain:ppm:bw:sq:vol for rtl dongle on an rtl_tcp server (see below)
tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355) tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355)
tcp:192.168.7.5:7355 for custom address and port tcp:192.168.7.5:7355 for custom address and port
filename.bin for OP25/FME capture bin files filename.bin for OP25/FME capture bin files
@@ -62,7 +63,6 @@ Input/Output options:
-q Reverse Mute - Mute Unencrypted Voice and Unmute Encrypted Voice -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; -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) -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) -v <hex> Set Filtering Bitmap Options (Advanced Option)
1 1 1 1 (0xF): PBF/LPF/HPF/HPFD on 1 1 1 1 (0xF): PBF/LPF/HPF/HPFD on
@@ -78,6 +78,11 @@ RTL-SDR options:
vol <num> RTL-SDR Sample 'Volume' Multiplier (default = 2)(1,2,3) vol <num> RTL-SDR Sample 'Volume' Multiplier (default = 2)(1,2,3)
Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T
Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2 Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2
Usage: rtltcp:host:port:freq:gain:ppm:bw:sq:vol
host <name> rtl_tcp Server Hostname or IP Address (default = localhost)
port <num> rtl_tcp Server Port (default = 1234)
freq, gain, ppm, bw, sq, vol as above
Example: dsd-fme -fs -i rtltcp:192.168.1.10:1234:450M:26:-2:8 -C connect_plus_chan.csv -T
Decoder options: Decoder options:
-fa Auto Detection -fa Auto Detection
@@ -101,7 +106,6 @@ Decoder options:
-fE Decode only EDACS EA/ProVoice with ESK 0xA0* -fE Decode only EDACS EA/ProVoice with ESK 0xA0*
-fm Decode only dPMR* -fm Decode only dPMR*
-l Disable DMR, dPMR, NXDN, M17 input filtering -l Disable DMR, dPMR, NXDN, M17 input filtering
-u <num> Unvoiced speech quality (default=3)
-xx Expect non-inverted X2-TDMA signal -xx Expect non-inverted X2-TDMA signal
-xr Expect inverted DMR signal -xr Expect inverted DMR signal
-xd Expect inverted ICOM dPMR signal -xd Expect inverted ICOM dPMR signal
@@ -138,11 +142,20 @@ Advanced Decoder options:
-H '736B9A9C5645288B 243AD5CB8701EF8A' -H '736B9A9C5645288B 243AD5CB8701EF8A'
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8' -H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
-H <hex> Manually Enter AES-128, AES-256, or Kirisun-256 Hex Key (see example below) -H <hex> Manually Enter AES-128, AES-256, or Kirisun 128 or 256 Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars. Encapulate in Single Quotation Marks; Space every 16 chars.
-H '736B9A9C5645288B 243AD5CB8701EF8A' -H '736B9A9C5645288B 243AD5CB8701EF8A'
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8' -H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
-H <hex> Manually Enter Caltta 10/32/64 Char Basic Privacy Hex Key (see example below)
-H C:40:1234567890
-H C:128:736B9A9C5645288B243AD5CB8701EF8A'
-H C:256:20029736A5D91042C923EB0697484433005EFC58A1905195E28E9C7836AA2DB8'
-H <hex> Manually Enter Hytera OTA Encryption 32 Char Hex Key (see example below)
-H O:0:0 no keystream application, but check for Hytera OTA Encryption and DMRA EP Late Entry IV.
-H O:128:17bfe59b29a06136fb7d3811fd1a4904 for 128-bit RC4 OTA Key Setup
-R <dec> Manually Enter dPMR or NXDN EHR Scrambler Key Value (Decimal Value) -R <dec> Manually Enter dPMR or NXDN EHR Scrambler Key Value (Decimal Value)
-1 <hex> Manually Enter RC4 or DES Key Value (DMR, P25, NXDN) (Hex Value) -1 <hex> Manually Enter RC4 or DES Key Value (DMR, P25, NXDN) (Hex Value)
@@ -175,6 +188,23 @@ Advanced Decoder options:
-A <hex> Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value) -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) (a-z A-Z, 0-9, $ # _ -)
-$ Password123
Special Use Cases involving # and $, use parenthesis and put \ in front of all $ signs
-$ "######"
-$ "\$\$\$\$\$\$"
-y <hex> Enter RAS 56-bit Effective Key Value (DMR) (14 Char Hex Value)
-y 6DF241BC4AE2EC
-, <string> Manually Enter and Enforce Auctus A6 (GoComm) 8 Char Key Value (DMR) (String Value)
-, Secure04
-u <string> Manually Enter Vertex Standard 10 or 64 Char Hex Key
-u 10:1234567891 or 64:AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDD
-u 0:0 no KS application, but apply the inversion to the interleave (clear errors on 256)
-S <str> Manually Enter and Enforce Generic Static Keystream -> Length and BYTE PACKED / ALIGNED String for AMBE (up to 882 bits) -S <str> Manually Enter and Enforce Generic Static Keystream -> Length and BYTE PACKED / ALIGNED String for AMBE (up to 882 bits)
For Example, enter 16-bit Keystream 0909 as: For Example, enter 16-bit Keystream 0909 as:
-S 16:0909 -S 16:0909
@@ -184,6 +214,8 @@ Advanced Decoder options:
-S 49:ED0AED4AED4AED4A -S 49:ED0AED4AED4AED4A
For Example, Baofeng 49-bit Keystream for 56-bit Custom Encryption as: For Example, Baofeng 49-bit Keystream for 56-bit Custom Encryption as:
-S 49:11AA22BB33CC44 -S 49:11AA22BB33CC44
For Example, Caltta 40-bit Keystream w/ optional 7-bit discard as:
-S 40:1234567890:7
-k <file> Import Key List from csv file (Decimal Format) -- Lower Case 'k'. -k <file> Import Key List from csv file (Decimal Format) -- Lower Case 'k'.
Only supports NXDN, DMR Basic Privacy (decimal value). Only supports NXDN, DMR Basic Privacy (decimal value).
@@ -214,7 +246,7 @@ Advanced Decoder options:
-p Disable Tune to Private Calls (DMR TIII, P25, NXDN Type-C and Type-D) -p Disable Tune to Private Calls (DMR TIII, P25, NXDN Type-C and Type-D)
-E Disable Tune to Group Calls (DMR TIII, Con+, Cap+, P25, NXDN Type-C, and Type-D) -E Disable Tune to Group Calls (DMR TIII, Con+, Cap+, P25, NXDN Type-C, and Type-D)
-e Enable Tune to Data Calls (DMR TIII, Cap+, NXDN Type-C) -e Enable Tune to Data Calls (DMR TIII, Cap+, NXDN Type-C)
(NOTE: No Clear Distinction between Cap+ Private Voice Calls and Data Calls -- Both enabled with Data Calls (NOTE: No Clear Distinction between Cap+ Private Voice Calls and Data Calls -- Both enabled with Data Calls)
-I <dec> Specify TG to Hold During Trunking (DMR, P25, NXDN Type-C Trunking) -I <dec> Specify TG to Hold During Trunking (DMR, P25, NXDN Type-C Trunking)
-U <port> Enable RIGCTL/TCP; Set TCP Port for RIGCTL. (4532 on SDR++) -U <port> Enable RIGCTL/TCP; Set TCP Port for RIGCTL. (4532 on SDR++)
-B <Hertz> Set RIGCTL Setmod Bandwidth in Hertz (0 - default - OFF) -B <Hertz> Set RIGCTL Setmod Bandwidth in Hertz (0 - default - OFF)

View File

@@ -22,27 +22,20 @@
@REM set options to pass to dsd-fme @REM set options to pass to dsd-fme
set "options= -N -Z " set "options= -N -Z "
@REM Set Date Time for log (sourced from: https://stackoverflow.com/questions/1192476/format-date-and-time-in-a-windows-batch-script) @REM Create Folder and Set Date Time for logs
@echo off @echo off
REM "%date: =0%" replaces spaces with zeros mkdir logs
set d=%date: =0% @REM Locale-independent date/time for log filenames (yyyyMMdd_HHmmss)
REM "set yyyy=%d:~-4%" pulls the last 4 characters @REM Works on older Windows without wmic or PowerShell
set yyyy=%d:~-4% for /f "tokens=1-6 delims=/: " %%a in ('robocopy "|" . /njh ^| find ":"') do (
set mm=%d:~4,2% set "YYYY=%%a"
set dd=%d:~7,2% set "MM=%%b"
set dow=%d:~0,3% set "DD=%%c"
set d=%yyyy%%mm%%dd% set "HH=%%d"
set "Min=%%e"
set t=%TIME: =0% set "Sec=%%f"
REM "%t::=%" removes semi-colons )
REM Instead of above, you could use "%t::=-%" to set "datetimestr=%YYYY%%MM%%DD%_%HH%%Min%%Sec%"
REM replace semi-colons with hyphens (or any
REM non-special character)
set t=%t::=%
set t=%t:.=%
set datetimestr=%d%_%t%
@REM @echo Long date time str = %datetimestr%
@REM random number is used so that two log files don't share the same name @REM random number is used so that two log files don't share the same name
set rnd=%RANDOM% set rnd=%RANDOM%

View File

@@ -372,6 +372,9 @@ typedef struct
int rtl_bandwidth; int rtl_bandwidth;
int rtl_started; int rtl_started;
long int rtl_rms; long int rtl_rms;
int rtltcp_enabled;
char rtltcp_hostname[1024];
int rtltcp_portno;
int monitor_input_audio; int monitor_input_audio;
int analog_only; int analog_only;
int pulse_raw_rate_in; int pulse_raw_rate_in;
@@ -649,6 +652,14 @@ typedef struct
//xl specific, we need to know if the ESS is from HDU, or from LDU2 //xl specific, we need to know if the ESS is from HDU, or from LDU2
int xl_is_hdu; int xl_is_hdu;
//Vertex Standard Keys
// #define VERTEX_KEY40_BY_TG
// #define VERTEX_KEY256_BY_TG
char vtx256_key[64];
unsigned long long int vtx40_key;
unsigned long long int vtx256_ekey;
uint8_t vtx_key_loaded;
unsigned int debug_audio_errors; unsigned int debug_audio_errors;
unsigned int debug_audio_errorsR; unsigned int debug_audio_errorsR;
unsigned int debug_header_errors; unsigned int debug_header_errors;
@@ -918,6 +929,26 @@ typedef struct
//Remus DMR End Call Alert Beep //Remus DMR End Call Alert Beep
int dmr_end_alert[2]; //dmr TLC end call alert beep has already played once flag 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
//Hytera OTA Key Variables
// #define DEBUG_HOTA
uint8_t hota_enc_check;
uint8_t hota_key_loaded;
uint8_t dmr_is_hota;
int hota_bit_shift;
int hota_byte_shifts[12];
uint8_t hota_rc4_key[16];
uint8_t hota_rc4_ks[80];
uint8_t hota_lc_ctx[2][4];
uint8_t hota_ctx_ok[2];
int hota_sbrc_idx1[2];
int hota_sbrc_idx2[2];
//Bitmap Filtering Options //Bitmap Filtering Options
int audio_smoothing; int audio_smoothing;
@@ -1004,6 +1035,7 @@ typedef struct
//generic ks //generic ks
int straight_ks; int straight_ks;
int straight_mod; int straight_mod;
int straight_discard;
uint8_t static_ks_bits[2][882]; uint8_t static_ks_bits[2][882];
int static_ks_counter[2]; int static_ks_counter[2];
@@ -1200,7 +1232,6 @@ void printFrameInfo (dsd_opts * opts, dsd_state * state);
void processFrame (dsd_opts * opts, dsd_state * state); void processFrame (dsd_opts * opts, dsd_state * state);
void printFrameSync (dsd_opts * opts, dsd_state * state, char *frametype, int offset, char *modulation); void printFrameSync (dsd_opts * opts, dsd_state * state, char *frametype, int offset, char *modulation);
int getFrameSync (dsd_opts * opts, dsd_state * state); int getFrameSync (dsd_opts * opts, dsd_state * state);
int comp (const void *a, const void *b);
void noCarrier (dsd_opts * opts, dsd_state * state); void noCarrier (dsd_opts * opts, dsd_state * state);
void initOpts (dsd_opts * opts); void initOpts (dsd_opts * opts);
void initState (dsd_state * state); void initState (dsd_state * state);
@@ -1358,11 +1389,14 @@ int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24]);
//BPTC (Block Product Turbo Code) functions //BPTC (Block Product Turbo Code) functions
void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output); 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_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); uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32], uint32_t ParityCheckTypeOdd);
uint32_t BPTC_128x77_Check_Errors(uint8_t InputDataMatrix[8][16]);
//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); 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); 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); 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);
@@ -1375,6 +1409,7 @@ uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes,
uint8_t ComputeCrc5Bit(uint8_t * DMRData); uint8_t ComputeCrc5Bit(uint8_t * DMRData);
uint16_t ComputeCrc9Bit(uint8_t * DMRData, uint32_t NbData); uint16_t ComputeCrc9Bit(uint8_t * DMRData, uint32_t NbData);
uint32_t ComputeCrc32Bit(uint8_t * DMRData, uint32_t NbData); uint32_t ComputeCrc32Bit(uint8_t * DMRData, uint32_t NbData);
uint32_t ComputeCrc32BitV(uint8_t * DMRData, uint32_t NbData);
//new simplified dmr functions //new simplified dmr functions
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst); void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst);
@@ -1496,6 +1531,11 @@ void LFSRP(dsd_state * state);
void LFSRN (char * BufferIn, char * BufferOut, dsd_state * state); void LFSRN (char * BufferIn, char * BufferOut, dsd_state * state);
void LFSR64(dsd_state * state); void LFSR64(dsd_state * state);
//IV Reversals using LFSR tap +1 arrangements
uint64_t reverse_lfsr_32a_to_len(uint8_t * iv, int16_t len);
uint64_t reverse_lfsr_32d_to_len(uint8_t * iv, int16_t len);
uint64_t reverse_lfsr_64_to_len(dsd_opts * opts, uint8_t * iv, int16_t len);
void Hamming_7_4_init(); void Hamming_7_4_init();
void Hamming_7_4_encode(unsigned char *origBits, unsigned char *encodedBits); void Hamming_7_4_encode(unsigned char *origBits, unsigned char *encodedBits);
bool Hamming_7_4_decode(unsigned char *rxBits); bool Hamming_7_4_decode(unsigned char *rxBits);
@@ -1638,7 +1678,7 @@ void aes_cfb_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * in,
void aes_ctr_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type); void aes_ctr_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type);
void aes_ctr_bitwise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type); void aes_ctr_bitwise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type);
//Tytera / Retevis / Anytone / Kenwood / Misc DMR Encryption Modes //Tytera / Retevis / Anytone / Kenwood / Vertex / Baofeng / Misc DMR Encryption Modes
void tyt16_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24], int fnum); void tyt16_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24], int fnum);
void tyt_ep_aes_keystream_creation(dsd_state * state, char * input); void tyt_ep_aes_keystream_creation(dsd_state * state, char * input);
void tyt_ap_pc4_keystream_creation(dsd_state * state, char * input); void tyt_ap_pc4_keystream_creation(dsd_state * state, char * input);
@@ -1648,18 +1688,43 @@ void anytone_bp_keystream_creation(dsd_state * state, char * input);
void baofeng_ap_pc5_keystream_creation(dsd_state *state, char *input); void baofeng_ap_pc5_keystream_creation(dsd_state *state, char *input);
void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24]); void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24]);
void straight_mod_xor_keystream_creation(dsd_state * state, char * input); void straight_mod_xor_keystream_creation(dsd_state * state, char * input);
void caltta_bp_keystream_creation(dsd_state * state, char * input);
void auctus_keystream_creation(dsd_state * state, char * input);
void vertex_keystream_setup(dsd_state * state, char * input);
void vertex_40_keystream_creation(dsd_state * state, unsigned long long int key_value);
void vertex_256_keystream_creation(dsd_state * state, char * key_string);
void vtx256_ambe2_interleave_inversion(char ambe_fr[4][24]);
//Kirisun //Kirisun LFSR and General Keystream Creator
uint32_t kirisun_lfsr(unsigned long long int mi); uint32_t kirisun_lfsr(unsigned long long int mi);
void kirisun_adv_keystream_creation(dsd_state *state); void kirisun_keystream_creation(dsd_state * state);
void kirisun_uni_keystream_creation(dsd_state *state);
//Hytera Enhanced //Hytera Enhanced
void hytera_enhanced_rc4_setup(dsd_opts * opts, dsd_state * state, unsigned long long int key_value, unsigned long long int mi_value); void hytera_enhanced_rc4_setup(dsd_opts * opts, dsd_state * state, unsigned long long int key_value, unsigned long long int mi_value, int len);
unsigned long long int hytera_lfsr(uint8_t * mi, uint8_t * taps, uint8_t len); unsigned long long int hytera_lfsr(uint8_t * mi, uint8_t * taps, uint8_t len);
void hytera_enhanced_alg_refresh(dsd_state * state); 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);
//Hytera OTA Encryption
void hytera_ota_keystream_setup(dsd_state * state, char * input);
void hytera_ota_decrypt_bptc_96(dsd_state * state, uint8_t * input, uint8_t type);
void hytera_ota_decrypt_emb_lc(dsd_state * state, uint8_t input[8][16]);
void hytera_ota_search_emb_lc(dsd_state * state, uint8_t input[8][16]);
void hytera_ota_decrypt_sb_rc(dsd_state * state, uint8_t * input);
void hytera_ota_search_sb_rc(dsd_state * state, uint8_t * input, int ptype);
void hytera_create_lc_ctx(dsd_state * state, uint8_t * input);
void hytera_ota_unit_testing(dsd_state * state);
//Hytera Decryption for Data Blocks
uint8_t hytera_bp_data_decrypt(dsd_state * state, uint8_t * input, int len);
uint8_t hytera_ep_data_decrypt(dsd_opts * opts, dsd_state * state, uint8_t * input, int len);
//LFSR to expand either a DMR 32-bit or P25/NXDN 64-bit MI into a 128-bit IV for AES //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); void lfsr_64_to_128(uint8_t * iv);
void LFSR128(dsd_state * state); void LFSR128(dsd_state * state);
void LFSR128n(dsd_state * state); void LFSR128n(dsd_state * state);
@@ -1679,6 +1744,21 @@ void rtlsdr_sighandler();
void rtl_dev_tune(dsd_opts * opts, long int frequency); void rtl_dev_tune(dsd_opts * opts, long int frequency);
long int rtl_return_rms(); long int rtl_return_rms();
void rtl_clean_queue(); void rtl_clean_queue();
//rtl_tcp client (rtl_sdr_tcp.c)
int rtltcp_open(const char *hostname, int portno);
int rtlsdr_or_tcp_close(rtlsdr_dev_t *dev);
int rtlsdr_or_tcp_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len);
int rtlsdr_or_tcp_cancel_async(rtlsdr_dev_t *dev);
int rtlsdr_or_tcp_reset_buffer(rtlsdr_dev_t *dev);
int rtlsdr_or_tcp_get_tuner_gains(rtlsdr_dev_t *dev, int *gains);
int rtlsdr_or_tcp_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq);
int rtlsdr_or_tcp_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate);
int rtlsdr_or_tcp_set_direct_sampling(rtlsdr_dev_t *dev, int on);
int rtlsdr_or_tcp_set_offset_tuning(rtlsdr_dev_t *dev, int on);
int rtlsdr_or_tcp_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual);
int rtlsdr_or_tcp_set_tuner_gain(rtlsdr_dev_t *dev, int gain);
int rtlsdr_or_tcp_set_freq_correction(rtlsdr_dev_t *dev, int ppm);
#endif #endif

View File

@@ -167,223 +167,295 @@ void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output)
/* /*
* @brief : This function extract the 96 bits of a deinteleaved 196 bits * BPTC (196,96) extract — ETSI TS 102 361-1 B.1.1
* buffer using BPTC (196,96) * Two-pass row Hamming(15,11) + column Hamming(13,9).
* * Debug: define BPTC_196x96_DEBUG 1 for row/col correction chatter.
* @param InputDeInteleavedData : Pointer of DMR input data deinterleaved (196 bytes)
*
* @param DMRDataExtracted : Pointer where the DMR data will be written (96 bytes)
*
* @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])
// #define BPTC_196x96_DEBUG 1
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; uint32_t i, j, k;
uint8_t DataMatrix[13][15]; uint8_t DataMatrix[13][15];
uint8_t LineUncorrected[15]; uint8_t LineUncorrected[15];
uint8_t LineBefore[15];
uint8_t LineCorrected[11]; uint8_t LineCorrected[11];
uint8_t ColumnUncorrected[13]; uint8_t ColumnUncorrected[13];
uint8_t ColumnBefore[13];
uint8_t ColumnCorrected[9]; uint8_t ColumnCorrected[9];
uint32_t HammingIrrecoverableErrorNb = 0; uint32_t HammingIrrecoverableErrorNb = 0;
uint32_t row_corr_p1 = 0, col_corr_p1 = 0;
uint32_t row_fail_p1 = 0, col_fail_p1 = 0;
uint32_t row_corr_p2 = 0, col_corr_p2 = 0;
uint32_t row_fail_p2 = 0, col_fail_p2 = 0;
/* First step : Reconstitute the BPTC 15x11 matrix UNUSED(row_corr_p1); UNUSED(col_corr_p1);
* Note : Input data shall be deinterleaved */ UNUSED(row_fail_p1); UNUSED(col_fail_p1);
k = 1; /* Discard R(3) bit - See DMR standard chapter B1.1 BPTC (196,96) */ UNUSED(row_corr_p2); UNUSED(col_corr_p2);
for(i = 0; i < 13; i++) UNUSED(row_fail_p2); UNUSED(col_fail_p2);
#if BPTC_196x96_DEBUG
fprintf (stderr, "\n");
#endif
/* Rebuild 13×15 matrix; discard R(3) at index 0 — DMR B.1.1 */
k = 1;
for (i = 0; i < 13; i++)
{ {
for(j = 0; j < 15; j++) for (j = 0; j < 15; j++)
{ {
/* Only the LSBit of the byte is stored */
DataMatrix[i][j] = (InputDeInteleavedData[k] & 1); DataMatrix[i][j] = (InputDeInteleavedData[k] & 1);
k++; k++;
} }
} }
/* Set to 0 R(0) to R(2) - See DMR standard chapter B1.1 BPTC (196,96) */ /* ========== Pass 1: row Hamming(15,11) on rows 0..8 ========== */
// DataMatrix[0][0] = 0; /* R(2) */ for (i = 0; i < 9; i++)
// 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 */
for(i = 0; i < 9; i++)
{ {
/* Get a full line */ for (j = 0; j < 15; j++)
for(j = 0; j < 15; j++)
{ {
LineUncorrected[j] = DataMatrix[i][j]; LineUncorrected[j] = DataMatrix[i][j];
LineBefore[j] = LineUncorrected[j];
} }
/* Apply Hamming (15,11,3) code correction */ if (Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
if(Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
{ {
HammingIrrecoverableErrorNb++; HammingIrrecoverableErrorNb++;
} row_fail_p1++;
#if BPTC_196x96_DEBUG
/* Re-inject the line in the matrix (only the util data [11 bit], fprintf(stderr, " BPTC196 P1 ROW FAIL row=%u rx=", (unsigned)i);
* not the Hamming part [4 bit]) */ for (j = 0; j < 15; j++)
for(j = 0; j < 11; j++) fprintf(stderr, "%u", LineBefore[j] & 1);
{ fprintf(stderr, "\n");
DataMatrix[i][j] = LineCorrected[j]; #endif
} /* Keep original data bits on failure */
} for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineBefore[j];
/* Process the the Hamming (15,11,3) code
* check on each column/row (first time) */
for(i = 0; i < 15; i++)
{
/* Get a full column */
for(j = 0; j < 13; j++)
{
ColumnUncorrected[j] = DataMatrix[j][i];
}
/* Apply Hamming (15,11,3) code correction */
if(Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
{
HammingIrrecoverableErrorNb++;
}
/* Re-inject the column in the matrix (only the util data [11 bit],
* not the Hamming part [4 bit]) */
for(j = 0; j < 9; j++)
{
DataMatrix[j][i] = ColumnCorrected[j];
}
}
/* The first Hamming code check has maybe corrected bit
* witch can be use to correct other bit, so make the
* same operation twice */
/* Reset the number of irrecoverable errors */
HammingIrrecoverableErrorNb = 0;
/* Process the the Hamming (15,11,3) code
* check on each line (second time).
* Do not check the 4th last lines */
for(i = 0; i < 9; i++)
{
/* Get a full line */
for(j = 0; j < 15; j++)
{
LineUncorrected[j] = DataMatrix[i][j];
}
/* Apply Hamming (15,11,3) code correction */
if(Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
{
/* Row/Line contains irrecoverable errors */
HammingIrrecoverableErrorNb++;
} }
else else
{ {
/* Row/Line contains correct data */ /* rxBits were corrected in place; note any flip */
} for (j = 0; j < 15; j++)
{
/* Re-inject the line in the matrix (only the util data [11 bit], if ((LineBefore[j] & 1) != (LineUncorrected[j] & 1))
* not the Hamming part [4 bit]) */ {
for(j = 0; j < 11; j++) row_corr_p1++;
{ #if BPTC_196x96_DEBUG
DataMatrix[i][j] = LineCorrected[j]; fprintf(stderr,
" BPTC196 P1 ROW CORRECTED row=%u bit=%u %u->%u\n",
(unsigned)i, (unsigned)j,
(unsigned)(LineBefore[j] & 1),
(unsigned)(LineUncorrected[j] & 1));
#endif
}
}
for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineCorrected[j];
} }
} }
#if BPTC_196x96_DEBUG
if (row_fail_p1 || row_corr_p1)
fprintf(stderr, " BPTC196 P1 ROW summary: fail=%u corrected_bits=%u\n",
(unsigned)row_fail_p1, (unsigned)row_corr_p1);
#endif
/* ========== Pass 1: column Hamming(13,9) on cols 0..14 ========== */
/* Process the the Hamming (15,11,3) code for (i = 0; i < 15; i++)
* check on each column/row (second time) */
for(i = 0; i < 15; i++)
{ {
/* Get a full column */ for (j = 0; j < 13; j++)
for(j = 0; j < 13; j++)
{ {
ColumnUncorrected[j] = DataMatrix[j][i]; ColumnUncorrected[j] = DataMatrix[j][i];
ColumnBefore[j] = ColumnUncorrected[j];
} }
/* Apply Hamming (15,11,3) code correction */ if (Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
if(Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
{ {
/* Row/Line contains irrecoverable errors */
HammingIrrecoverableErrorNb++; HammingIrrecoverableErrorNb++;
col_fail_p1++;
#if BPTC_196x96_DEBUG
fprintf(stderr, " BPTC196 P1 COL FAIL col=%u rx=", (unsigned)i);
for (j = 0; j < 13; j++)
fprintf(stderr, "%u", ColumnBefore[j] & 1);
fprintf(stderr, "\n");
#endif
for (j = 0; j < 9; j++)
DataMatrix[j][i] = ColumnBefore[j];
} }
else else
{ {
/* Row/Line contains correct data */ for (j = 0; j < 13; j++)
} {
if ((ColumnBefore[j] & 1) != (ColumnUncorrected[j] & 1))
/* Re-inject the column in the matrix (only the util data [11 bit], {
* not the Hamming part [4 bit]) */ col_corr_p1++;
for(j = 0; j < 9; j++) #if BPTC_196x96_DEBUG
{ fprintf(stderr,
DataMatrix[j][i] = ColumnCorrected[j]; " BPTC196 P1 COL CORRECTED col=%u bit=%u %u->%u\n",
(unsigned)i, (unsigned)j,
(unsigned)(ColumnBefore[j] & 1),
(unsigned)(ColumnUncorrected[j] & 1));
#endif
}
}
for (j = 0; j < 9; j++)
DataMatrix[j][i] = ColumnCorrected[j];
} }
} }
#if BPTC_196x96_DEBUG
if (col_fail_p1 || col_corr_p1)
fprintf(stderr, " BPTC196 P1 COL summary: fail=%u corrected_bits=%u\n",
(unsigned)col_fail_p1, (unsigned)col_corr_p1);
#endif
/* Extract the DMR data (96 bit) from the matrix */ /* Second pass can fix more after the first cascade */
/* First line : Do not take R(2), R(1) and R(0) */ HammingIrrecoverableErrorNb = 0;
/* ========== Pass 2: rows ========== */
for (i = 0; i < 9; i++)
{
for (j = 0; j < 15; j++)
{
LineUncorrected[j] = DataMatrix[i][j];
LineBefore[j] = LineUncorrected[j];
}
if (Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
{
HammingIrrecoverableErrorNb++;
row_fail_p2++;
#if BPTC_196x96_DEBUG
fprintf(stderr, " BPTC196 P2 ROW FAIL row=%u rx=", (unsigned)i);
for (j = 0; j < 15; j++)
fprintf(stderr, "%u", LineBefore[j] & 1);
fprintf(stderr, "\n");
#endif
for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineBefore[j];
}
else
{
for (j = 0; j < 15; j++)
{
if ((LineBefore[j] & 1) != (LineUncorrected[j] & 1))
{
row_corr_p2++;
#if BPTC_196x96_DEBUG
fprintf(stderr,
" BPTC196 P2 ROW CORRECTED row=%u bit=%u %u->%u\n",
(unsigned)i, (unsigned)j,
(unsigned)(LineBefore[j] & 1),
(unsigned)(LineUncorrected[j] & 1));
#endif
}
}
for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineCorrected[j];
}
}
#if BPTC_196x96_DEBUG
if (row_fail_p2 || row_corr_p2)
fprintf(stderr, " BPTC196 P2 ROW summary: fail=%u corrected_bits=%u\n",
(unsigned)row_fail_p2, (unsigned)row_corr_p2);
#endif
/* ========== Pass 2: columns ========== */
for (i = 0; i < 15; i++)
{
for (j = 0; j < 13; j++)
{
ColumnUncorrected[j] = DataMatrix[j][i];
ColumnBefore[j] = ColumnUncorrected[j];
}
if (Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
{
HammingIrrecoverableErrorNb++;
col_fail_p2++;
#if BPTC_196x96_DEBUG
fprintf(stderr, " BPTC196 P2 COL FAIL col=%u rx=", (unsigned)i);
for (j = 0; j < 13; j++)
fprintf(stderr, "%u", ColumnBefore[j] & 1);
fprintf(stderr, "\n");
#endif
for (j = 0; j < 9; j++)
DataMatrix[j][i] = ColumnBefore[j];
}
else
{
for (j = 0; j < 13; j++)
{
if ((ColumnBefore[j] & 1) != (ColumnUncorrected[j] & 1))
{
col_corr_p2++;
#if BPTC_196x96_DEBUG
fprintf(stderr,
" BPTC196 P2 COL CORRECTED col=%u bit=%u %u->%u\n",
(unsigned)i, (unsigned)j,
(unsigned)(ColumnBefore[j] & 1),
(unsigned)(ColumnUncorrected[j] & 1));
#endif
}
}
for (j = 0; j < 9; j++)
DataMatrix[j][i] = ColumnCorrected[j];
}
}
#if BPTC_196x96_DEBUG
if (col_fail_p2 || col_corr_p2)
fprintf(stderr, " BPTC196 P2 COL summary: fail=%u corrected_bits=%u\n",
(unsigned)col_fail_p2, (unsigned)col_corr_p2);
fprintf(stderr,
" BPTC196 FINAL irrecoverable=%u P1(row_f=%u col_f=%u row_c=%u col_c=%u) "
"P2(row_f=%u col_f=%u row_c=%u col_c=%u)\n",
(unsigned)HammingIrrecoverableErrorNb,
(unsigned)row_fail_p1, (unsigned)col_fail_p1,
(unsigned)row_corr_p1, (unsigned)col_corr_p1,
(unsigned)row_fail_p2, (unsigned)col_fail_p2,
(unsigned)row_corr_p2, (unsigned)col_corr_p2);
#endif
/* Extract 96 data bits */
k = 0; k = 0;
for (i = 3; i < 11; i++)
for(i = 3; i < 11; i++)
{ {
DMRDataExtracted[k] = DataMatrix[0][i]; DMRDataExtracted[k++] = DataMatrix[0][i];
k++; }
for (i = 1; i < 9; i++)
{
for (j = 0; j < 11; j++)
DMRDataExtracted[k++] = DataMatrix[i][j];
} }
/* Next lines */ if (HammingIrrecoverableErrorNb == 0)
for(i = 1; i < 9; i++)
{ {
for(j = 0; j < 11; j++) R[0] = DataMatrix[0][2]; /* R(0) */
{ R[1] = DataMatrix[0][1]; /* R(1) */
DMRDataExtracted[k] = DataMatrix[i][j]; R[2] = DataMatrix[0][0]; /* R(2) */
k++;
}
} }
/* R(0) to R(2) may be used to transport some if (R[0] == 0 && R[1] == 0 && R[2] == 1)
* Restricted Access System (RAS) information, *is_ras = 1;
* So save these three bits after hamming correction else
* See patent US 2013/0288643 A1 */ *is_ras = 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) */
/* Return the number of irrecoverable Hamming errors */
return HammingIrrecoverableErrorNb; return HammingIrrecoverableErrorNb;
} /* End BPTC_196x96_Extract_Data() */ }
/* /*
* @brief : This function extract the 77 bits of a deinteleaved 128 bits * BPTC (128,77) extract with iterative Hamming + parity-guided repair
* buffer using BPTC (128,77). * and diagnostic logging.
* See ETSI TS 102 361-1 B.2.1
* *
* @note : See DMR standard ETSI TS 102 361-1 chapter B.2.1 for details * Enable/disable chatter with BPTC_128x77_DEBUG (1 = on).
*
* @param InputDeInteleavedData : Pointer of DMR input data deinterleaved (128 bytes
act as a 16 x 8 byte data matrix)
*
* @param DMRDataExtracted : Pointer where the DMR data will be written (77 bytes)
* DMRDataExtracted[0..71] = Util data (hamming corrected)
* DMRDataExtracted[72..76] = 5 bit of CRC extracted (to be checked)
*
* @return 0 = All is OK
* Other = Error - Irrecoverable Hamming parity check error number
*/ */
// #define BPTC_128x77_DEBUG 1
uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77]) uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77])
{ {
uint32_t i, j, k; uint32_t i, j, k;
@@ -394,102 +466,193 @@ uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDat
uint32_t ParityCheckErrorNb = 0; uint32_t ParityCheckErrorNb = 0;
uint32_t NbOfOne; uint32_t NbOfOne;
/* First step : Reconstitute the BPTC 16x8 matrix */ uint8_t row_bad[7];
for(i = 0; i < 8; i++) uint8_t col_bad[16];
#if BPTC_128x77_DEBUG
fprintf (stderr, "\n");
#endif
for (i = 0; i < 8; i++)
{ {
for(j = 0; j < 16; j++) for (j = 0; j < 16; j++)
{
/* Only the LSBit of the byte is stored */
DataMatrix[i][j] = (InputDataMatrix[i][j] & 1); DataMatrix[i][j] = (InputDataMatrix[i][j] & 1);
}
} }
/* Init the Hamming (16,11,4) library /* ---------- Pass 1: Hamming (16,11,4) on rows 0..6 ---------- */
* Not needed because it has already been done memset(row_bad, 0, sizeof(row_bad));
* in the "InitAllFecFunction()" function */ for (i = 0; i < 7; i++)
//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) */
for(i = 0; i < 7; i++)
{ {
/* Get a full line */ for (j = 0; j < 16; j++)
for(j = 0; j < 16; j++)
{
LineUncorrected[j] = DataMatrix[i][j]; LineUncorrected[j] = DataMatrix[i][j];
}
/* Apply Hamming (16,11,4) code correction */ if (Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false)
if(Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false)
{ {
HammingIrrecoverableErrorNb++; HammingIrrecoverableErrorNb++;
row_bad[i] = 1;
#if BPTC_128x77_DEBUG
fprintf(stderr, " BPTC128 P1 Hamming FAIL row=%u rx=", (unsigned)i);
for (j = 0; j < 16; j++)
fprintf(stderr, "%u", LineUncorrected[j] & 1);
fprintf(stderr, "\n");
#endif
for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineUncorrected[j];
} }
else
/* Re-inject the line in the matrix (only the util data [11 bit],
* not the Hamming part [4 bit]) */
for(j = 0; j < 11; j++)
{ {
DataMatrix[i][j] = LineCorrected[j]; for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineCorrected[j];
} }
} }
#if BPTC_128x77_DEBUG
if (HammingIrrecoverableErrorNb)
fprintf(stderr, " BPTC128 P1 Hamming irrecoverable rows=%u\n",
(unsigned)HammingIrrecoverableErrorNb);
#endif
/* Extract the DMR data (77 bit) from the matrix */ /* ---------- Column even-parity check (row 7) ---------- */
k = 0; memset(col_bad, 0, sizeof(col_bad));
/* 2 first lines */
for(i = 0; i < 2; i++)
{
for(j = 0; j < 11; j++)
{
DMRDataExtracted[k] = DataMatrix[i][j];
k++;
}
}
/* 5 Next lines */
for(i = 2; i < 7; i++)
{
for(j = 0; j < 10; j++)
{
DMRDataExtracted[k] = DataMatrix[i][j];
k++;
}
}
/* 5 bit of CRC */
for(i = 2; i < 7; i++)
{
DMRDataExtracted[k] = DataMatrix[i][10];
k++;
}
/* Verify the data integrity by checking
* all column parity bit */
ParityCheckErrorNb = 0; ParityCheckErrorNb = 0;
for(i = 0; i < 16; i++) for (i = 0; i < 16; i++)
{ {
NbOfOne = 0; NbOfOne = 0;
for (j = 0; j < 7; j++)
/* Get a full column */
for(j = 0; j < 7; j++)
{
NbOfOne += DataMatrix[j][i]; NbOfOne += DataMatrix[j][i];
}
/* Check the parity bit (number of "1" if ((NbOfOne % 2) != DataMatrix[7][i])
* must be even) */
if((NbOfOne % 2) != DataMatrix[7][i])
{ {
ParityCheckErrorNb++; ParityCheckErrorNb++;
col_bad[i] = 1;
#if BPTC_128x77_DEBUG
fprintf(stderr,
" BPTC128 P1 parity FAIL col=%u sum7=%u parity_bit=%u (expect even)\n",
(unsigned)i, (unsigned)NbOfOne, (unsigned)DataMatrix[7][i]);
#endif
}
}
#if BPTC_128x77_DEBUG
if (ParityCheckErrorNb)
fprintf(stderr, " BPTC128 P1 parity bad columns=%u\n",
(unsigned)ParityCheckErrorNb);
#endif
/* ---------- Parity-guided single-bit repair ---------- */
{
uint32_t n_bad_rows = 0, n_bad_cols = 0;
uint32_t br = 0, bc = 0;
for (i = 0; i < 7; i++)
if (row_bad[i]) { n_bad_rows++; br = i; }
for (i = 0; i < 16; i++)
if (col_bad[i]) { n_bad_cols++; bc = i; }
#if BPTC_128x77_DEBUG
fprintf(stderr,
" BPTC128 repair candidates: bad_rows=%u bad_cols=%u",
(unsigned)n_bad_rows, (unsigned)n_bad_cols);
if (n_bad_rows == 1)
fprintf(stderr, " row=%u", (unsigned)br);
if (n_bad_cols == 1)
fprintf(stderr, " col=%u", (unsigned)bc);
fprintf(stderr, "\n");
#endif
if (n_bad_rows == 1 && n_bad_cols == 1 && bc < 11)
{
DataMatrix[br][bc] ^= 1;
row_bad[br] = 0;
#if BPTC_128x77_DEBUG
fprintf(stderr,
" BPTC128 parity-guided FLIP at row=%u col=%u -> %u\n",
(unsigned)br, (unsigned)bc, (unsigned)DataMatrix[br][bc]);
#endif
}
#if BPTC_128x77_DEBUG
else if (n_bad_rows || n_bad_cols)
{
fprintf(stderr,
" BPTC128 no single-intersection flip "
"(need exactly 1 bad row and 1 data col)\n");
}
#endif
}
/* ---------- Pass 2: Hamming (16,11,4) again ---------- */
HammingIrrecoverableErrorNb = 0;
for (i = 0; i < 7; i++)
{
for (j = 0; j < 16; j++)
LineUncorrected[j] = DataMatrix[i][j];
if (Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false)
{
HammingIrrecoverableErrorNb++;
#if BPTC_128x77_DEBUG
fprintf(stderr, " BPTC128 P2 Hamming FAIL row=%u rx=", (unsigned)i);
for (j = 0; j < 16; j++)
fprintf(stderr, "%u", LineUncorrected[j] & 1);
fprintf(stderr, "\n");
#endif
for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineUncorrected[j];
}
else
{
for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineCorrected[j];
}
}
#if BPTC_128x77_DEBUG
if (HammingIrrecoverableErrorNb)
fprintf(stderr, " BPTC128 P2 Hamming irrecoverable rows=%u\n",
(unsigned)HammingIrrecoverableErrorNb);
#endif
/* ---------- Final column parity recount ---------- */
ParityCheckErrorNb = 0;
for (i = 0; i < 16; i++)
{
NbOfOne = 0;
for (j = 0; j < 7; j++)
NbOfOne += DataMatrix[j][i];
if ((NbOfOne % 2) != DataMatrix[7][i])
{
ParityCheckErrorNb++;
#if BPTC_128x77_DEBUG
fprintf(stderr,
" BPTC128 P2 parity FAIL col=%u sum7=%u parity_bit=%u\n",
(unsigned)i, (unsigned)NbOfOne, (unsigned)DataMatrix[7][i]);
#endif
} }
} }
/* Return the number of irrecoverable Hamming errors + #if BPTC_128x77_DEBUG
* the number of parity check error */ fprintf(stderr,
return (HammingIrrecoverableErrorNb + ParityCheckErrorNb); " BPTC128 result: Hamming_err=%u parity_err=%u total=%u\n",
} /* End BPTC_128x77_Extract_Data() */ (unsigned)HammingIrrecoverableErrorNb,
(unsigned)ParityCheckErrorNb,
(unsigned)(HammingIrrecoverableErrorNb + ParityCheckErrorNb));
#endif
/* ---------- Extract 77 bits ---------- */
k = 0;
for (i = 0; i < 2; i++)
{
for (j = 0; j < 11; j++)
DMRDataExtracted[k++] = DataMatrix[i][j];
}
for (i = 2; i < 7; i++)
{
for (j = 0; j < 10; j++)
DMRDataExtracted[k++] = DataMatrix[i][j];
}
for (i = 2; i < 7; i++)
DMRDataExtracted[k++] = DataMatrix[i][10];
return (HammingIrrecoverableErrorNb + ParityCheckErrorNb);
}
/* /*
* @brief : This function extract the 32 bits of a deinteleaved 32 bits * @brief : This function extract the 32 bits of a deinteleaved 32 bits
@@ -577,6 +740,52 @@ uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDat
} /* End BPTC_16x2_Extract_Data() */ } /* End BPTC_16x2_Extract_Data() */
uint32_t BPTC_128x77_Check_Errors(uint8_t InputDataMatrix[8][16])
{
uint32_t i, j;
uint32_t ham_bad = 0, ham_fix = 0, par_err = 0;
uint8_t line[16], before[16], decoded[11];
uint32_t ones;
int changed;
for (i = 0; i < 7; i++)
{
for (j = 0; j < 16; j++)
{
before[j] = (InputDataMatrix[i][j] & 1);
line[j] = before[j];
}
if (Hamming_16_11_4_decode(line, decoded, 1) == false)
{
ham_bad++;
}
else
{
changed = 0;
for (j = 0; j < 16; j++)
{
if (line[j] != before[j])
{
changed = 1;
break;
}
}
if (changed)
ham_fix++;
}
}
for (i = 0; i < 16; i++)
{
ones = 0;
for (j = 0; j < 7; j++)
ones += (InputDataMatrix[j][i] & 1);
if ((ones % 2) != (InputDataMatrix[7][i] & 1))
par_err++;
}
return ham_bad + ham_fix + par_err;
}
/* End of file */ /* End of file */

View File

@@ -39,6 +39,7 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include "dsd.h" #include "dsd.h"
#include "bp.h"
#define AES_BLOCKLEN 16 #define AES_BLOCKLEN 16
unsigned Nb = 4; unsigned Nb = 4;
@@ -510,6 +511,57 @@ void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
} }
//Vertex Standard "AES" Encryption - convert AES key into a round key at 256 Nb,Nk,Nr values
//then select entries from the BP table to invert and convert to a keystream
void vertex_256_keystream_creation(dsd_state * state, char * key_string)
{
Nb = 4;
Nk = 8;
Nr = 14;
uint8_t key[32];
memset (key, 0, sizeof(key));
parse_raw_user_string(key_string, key);
struct AES_ctx ctx;
KeyExpansion(ctx.RoundKey, key);
uint64_t vtx_key = 0;
//use the bytes from round key output to select 4 BP key 16-bit words
for (int i = 224; i < 228; i++)
{
vtx_key <<= 16;
vtx_key |= BPK[ctx.RoundKey[i]];
}
//invert value
vtx_key = ~vtx_key;
fprintf (stderr,"Vertex Round Key Bytes: ");
for (uint16_t i = 224; i < 228; i++)
fprintf (stderr, "%02X ", ctx.RoundKey[i]);
fprintf (stderr, "Vertex Keystream: %016llX;", (unsigned long long int)vtx_key);
fprintf (stderr, "\n");
uint8_t vtx_bytes[8];
memset (vtx_bytes, 0, sizeof(vtx_bytes));
for (int i = 0; i < 8; i++)
vtx_bytes[i] = (vtx_key >> (56-(i*8))) & 0xFF;
unpack_byte_array_into_bit_array(vtx_bytes, state->static_ks_bits[0], 8);
unpack_byte_array_into_bit_array(vtx_bytes, state->static_ks_bits[1], 8);
//hex representation of the effective keystream value
state->vtx256_ekey = convert_bits_into_output(state->static_ks_bits[0], 64);
state->vtx_key_loaded = 1;
}
//byte-wise output of AES OFB Keystream //byte-wise output of AES OFB Keystream
//input iv is a 16-byte uint8_t array of initialization vector //input iv is a 16-byte uint8_t array of initialization vector
//input key is up to 32-byte uint8_t array of key value //input key is up to 32-byte uint8_t array of key value

View File

@@ -287,7 +287,7 @@ void des_cipher (uint8_t * main_key, uint8_t * input_register, uint8_t * output_
memcpy (left, initial_permutation+0, sizeof(left)); memcpy (left, initial_permutation+0, sizeof(left));
memcpy (right, initial_permutation+4, sizeof(right)); memcpy (right, initial_permutation+4, sizeof(right));
//16 fiestel rounds //16 feistel rounds
for (uint8_t f = 0; f < 16; f++) for (uint8_t f = 0; f < 16; f++)
{ {
@@ -339,7 +339,102 @@ void des_cipher (uint8_t * main_key, uint8_t * input_register, uint8_t * output_
memcpy (left, right, sizeof(left)); memcpy (left, right, sizeof(left));
memcpy (right, rperm, sizeof(right)); memcpy (right, rperm, sizeof(right));
} //end fiestel rounds f } //end feistel rounds f
//combine right and left into the pre_end_permutation array
memcpy (pre_end_permutation+0, right, sizeof(right));
memcpy (pre_end_permutation+4, left, sizeof(left));
//carry out the final permuation to get the completed output_register for this cipher iteration
permute (pre_end_permutation, output_register, final_register_permutation, 0, 64);
}
//kirisun variation on this cipher w/ preconfigured unique key schedule
void ksun_des_cipher (uint8_t * key, uint8_t * input_register, uint8_t * output_register, uint8_t de)
{
//starting and ending permutations
uint8_t initial_permutation[8]; memset(initial_permutation, 0, sizeof(initial_permutation));
uint8_t pre_end_permutation[8]; memset(pre_end_permutation, 0, sizeof(pre_end_permutation));
//intermediate left, right, and expansion/selection arrays
uint8_t left[4]; memset(left, 0, sizeof(left));
uint8_t right[4]; memset(right, 0, sizeof(right));
uint8_t rperm[4]; memset(rperm, 0, sizeof(rperm));
uint8_t exp[6]; memset(exp, 0, sizeof(exp));
uint8_t sel[4]; memset(sel, 0, sizeof(sel));
//key sets
uint8_t Ks[16][6]; memset(Ks, 0, sizeof(Ks));
//file the key into the keysets
for (int i = 0; i < 16; i++)
{
for (int j = 0; j < 6; j++)
Ks[i][j] = key[(i*6) + j];
}
//permute the input_register with the ip (initial_register_permutation) table
permute (input_register, initial_permutation, initial_register_permutation, 0, 64);
//copy the initial permutation to left and right half arrays
memcpy (left, initial_permutation+0, sizeof(left));
memcpy (right, initial_permutation+4, sizeof(right));
//16 feistel rounds
for (uint8_t f = 0; f < 16; f++)
{
//reset rperm and exp arrays
memset(rperm, 0, sizeof(rperm));
memset(exp, 0, sizeof(exp));
//expand right half from 32-bit to 48-bit
permute (right, exp, message_expansion, 0, 48);
//apply current iteration keyset to the expanded array (encryption cycles Ks forward, decryption goes in reverse)
if (de == 1)
{
for (uint8_t i = 0; i < 6; i++)
exp[i] ^= Ks[f][i];
}
else
{
for (uint8_t i = 0; i < 6; i++)
exp[i] ^= Ks[15-f][i];
}
//extract 6 bits for each sbox swap from the expanded array
uint8_t e1 = exp[0] >> 2;
uint8_t e2 = ((exp[0] & 0x3) << 4) | (exp[1] >> 4);
uint8_t e3 = ((exp[1] & 0xF) << 2) | (exp[2] >> 6);
uint8_t e4 = exp[2] & 0x3F;
uint8_t e5 = exp[3] >> 2;
uint8_t e6 = ((exp[3] & 0x3) << 4) | (exp[4] >> 4);
uint8_t e7 = ((exp[4] & 0xF) << 2) | (exp[5] >> 6);
uint8_t e8 = exp[5] & 0x3F;
//select 32-bits of each 48-bit expansion via modified S Box Look Up Tables
//swap the 6 bits for 4 bits via SLUT tables and OR them together into a selection array
sel[0] = SLUT1[e1] | SLUT2[e2];
sel[1] = SLUT3[e3] | SLUT4[e4];
sel[2] = SLUT5[e5] | SLUT6[e6];
sel[3] = SLUT7[e7] | SLUT8[e8];
//permute the selected 32-bits into a new right half
permute (sel, rperm, right_half_permutation, 0, 32);
//xor new right half against left half
for (uint8_t i = 0; i < 4; i++)
rperm[i] ^= left[i];
//swap old right half to left, set new right half to rperm
memcpy (left, right, sizeof(left));
memcpy (right, rperm, sizeof(right));
} //end feistel rounds f
//combine right and left into the pre_end_permutation array //combine right and left into the pre_end_permutation array
memcpy (pre_end_permutation+0, right, sizeof(right)); memcpy (pre_end_permutation+0, right, sizeof(right));

View File

@@ -1,4 +1,5 @@
#include "dsd.h" #include "dsd.h"
#include "dmr_const.h"
void ken_dmr_scrambler_keystream_creation(dsd_state * state, char * input) void ken_dmr_scrambler_keystream_creation(dsd_state * state, char * input)
{ {
@@ -92,6 +93,9 @@ void straight_mod_xor_keystream_creation(dsd_state * state, char * input)
} }
else goto END_KS; else goto END_KS;
//debug
// fprintf (stderr, "\ncurr: %s\n", curr);
uint8_t ks_bytes[112]; uint8_t ks_bytes[112];
memset (ks_bytes, 0, sizeof(ks_bytes)); memset (ks_bytes, 0, sizeof(ks_bytes));
parse_raw_user_string(curr, ks_bytes); parse_raw_user_string(curr, ks_bytes);
@@ -122,5 +126,229 @@ void straight_mod_xor_keystream_creation(dsd_state * state, char * input)
if (curr == NULL) if (curr == NULL)
fprintf (stderr, "Straight KS String Malformed! No KS Created!\n"); fprintf (stderr, "Straight KS String Malformed! No KS Created!\n");
else
{
curr = strtok(NULL, ":"); //optional discard value
if (curr != NULL)
{
len = 0;
sscanf (curr, "%hd", &len);
if (len != 0 && len < 8)
{
state->straight_discard = (int)len;
fprintf (stderr, "Optional Straight XOR Keystream Discard: %d; \n", len);
}
}
}
}
void caltta_bp_keystream_creation(dsd_state * state, char * input)
{
uint16_t len = 0;
char * curr;
curr = strtok(input, ":"); //should be len (mod) of key (decimal)
if (curr != NULL)
sscanf (curr, "%hd", &len);
else goto END_KS;
//len sanity check, can't be greater than 882
if (len > 256)
len = 256;
curr = strtok(NULL, ":"); //should be key in hex
if (curr != NULL)
{
//continue
}
else goto END_KS;
uint8_t ks_bytes[112];
memset (ks_bytes, 0, sizeof(ks_bytes));
parse_raw_user_string(curr, ks_bytes);
uint8_t ks_bits[896];
memset(ks_bits, 0, sizeof(ks_bits));
uint16_t unpack_len = len / 8;
if (len % 8)
unpack_len++;
unpack_byte_array_into_bit_array(ks_bytes, ks_bits, unpack_len);
for (uint16_t i = 0; i < len; i++)
{
state->static_ks_bits[0][i] = ks_bits[i];
state->static_ks_bits[1][i] = ks_bits[i];
}
fprintf (stderr,"Caltta BP %d-bit Keystream: ", len);
for (uint16_t i = 0; i < unpack_len; i++)
fprintf (stderr, "%02X", ks_bytes[i]);
fprintf (stderr, "\n");
state->straight_ks = 0x26;
state->straight_mod = (int)len;
END_KS:
if (curr == NULL)
fprintf (stderr, "Caltta BP KS String Malformed! No KS Created!\n");
}
void hytera_bp_keystream_creation(dsd_state * state, char * input)
{
uint16_t len = 0;
char * curr;
curr = strtok(input, ":"); //should be len (mod) of key (decimal)
if (curr != NULL)
sscanf (curr, "%hd", &len);
else goto END_KS;
//len sanity check, can't be greater than 882
if (len > 256)
len = 256;
curr = strtok(NULL, ":"); //should be key in hex
if (curr != NULL)
{
//continue
}
else goto END_KS;
uint8_t ks_bytes[112];
memset (ks_bytes, 0, sizeof(ks_bytes));
parse_raw_user_string(curr, ks_bytes);
uint8_t ks_bits[896];
memset(ks_bits, 0, sizeof(ks_bits));
uint16_t unpack_len = len / 8;
if (len % 8)
unpack_len++;
unpack_byte_array_into_bit_array(ks_bytes, ks_bits, unpack_len);
for (uint16_t i = 0; i < len; i++)
{
state->static_ks_bits[0][i] = ks_bits[i];
state->static_ks_bits[1][i] = ks_bits[i];
}
fprintf (stderr,"Hytera BP %d-bit Keystream: ", len);
for (uint16_t i = 0; i < unpack_len; i++)
fprintf (stderr, "%02X", ks_bytes[i]);
state->straight_ks = 0x68;
state->straight_mod = (int)len;
END_KS:
if (curr == NULL)
fprintf (stderr, "Hytera BP KS String Malformed! No KS Created!\n");
}
//connect systems 72-bit (9-byte) Extended Encryption //TODO: Move this later
void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24])
{
char interleaved[72];
memset (interleaved, 0, sizeof(interleaved));
//interleave the frame
const int *w, *x, *y, *z;
w = rW; x = rX; y = rY; z = rZ;
for (int8_t i = 0; i < 36; i++)
{
interleaved[(i*2)+0] = ambe_fr[*w][*x];
interleaved[(i*2)+1] = ambe_fr[*y][*z];
w++;
x++;
y++;
z++;
}
uint8_t ks_bytes[9]; memset(ks_bytes, 0, sizeof(ks_bytes));
uint8_t ks[72]; memset(ks, 0, sizeof(ks));
//keys are loaded in reverse byte order, stored in state->static_ks_bits[0]
for (int i = 0; i < 9; i++)
ks_bytes[i] = state->static_ks_bits[0][8-i];
//convert byte array into a bit array
unpack_byte_array_into_bit_array(ks_bytes, ks, 9);
//apply keystream to interleave
for (int8_t i = 0; i < 72; i++)
interleaved[i] ^= ks[71-i];
//deinterleave back into ambe_fr frame
w = rW; x = rX; y = rY; z = rZ;
int k = 0;
for (int8_t i = 0; i < 36; i++)
{
ambe_fr[*w][*x] = interleaved[k++];
ambe_fr[*y][*z] = interleaved[k++];
w++;
x++;
y++;
z++;
}
}
void vtx256_ambe2_interleave_inversion(char ambe_fr[4][24])
{
uint32_t silence_check = 0;
for (int i = 0; i < 20; i++)
{
silence_check <<= 1;
silence_check |= (uint8_t)ambe_fr[0][23-i];
}
if (silence_check == 0xF8014)
goto END_INV;
char interleaved[72];
memset (interleaved, 0, sizeof(interleaved));
//interleave the frame
const int *w, *x, *y, *z;
w = rW; x = rX; y = rY; z = rZ;
for (int8_t i = 0; i < 36; i++)
{
interleaved[(i*2)+0] = ambe_fr[*w][*x];
interleaved[(i*2)+1] = ambe_fr[*y][*z];
w++;
x++;
y++;
z++;
}
//invert the bits in the 49 to 71 position
for (int8_t i = 49; i < 72; i++)
interleaved[i] ^= 1;
//deinterleave back into ambe_fr frame
w = rW; x = rX; y = rY; z = rZ;
int k = 0;
for (int8_t i = 0; i < 36; i++)
{
ambe_fr[*w][*x] = interleaved[k++];
ambe_fr[*y][*z] = interleaved[k++];
w++;
x++;
y++;
z++;
}
END_INV: ; //do nothing
} }

View File

@@ -1,6 +1,9 @@
#include "dsd.h" #include "dsd.h"
#include "pc4.h" #include "pc4.h"
//function prototype linked in crypt-des.c
void ksun_des_cipher (uint8_t * key, uint8_t * input_register, uint8_t * output_register, uint8_t de);
/* === Global MD2II buffers === */ /* === Global MD2II buffers === */
int x1, x2, i, n1global; int x1, x2, i, n1global;
unsigned char h2[n1]; unsigned char h2[n1];
@@ -174,8 +177,69 @@ void keystream37(unsigned char *key, uint64_t frame, unsigned char *output)
} }
//dsd-fme -fs -Z -i '/SSD_STORAGE/2025_DEV/Tytera BP EP AP Master Folder/kirisun/cap-clear.wav/cap-33.wav' -H '3333333333333333 3333333333333333 3333333333333333 3333333333333333' #define ROTATE_LEFT(VALUE, SHIFT) \
void kirisun_uni_keystream_creation(dsd_state *state) (((VALUE) << ((SHIFT) & 63)) | ((VALUE) >> (64 - ((SHIFT) & 63))))
#define ROTATE_RIGHT(VALUE, SHIFT) \
(((VALUE) >> ((SHIFT) & 63)) | ((VALUE) << (64 - ((SHIFT) & 63))))
void keystream35(uint8_t * input_register, unsigned char * key)
{
uint64_t round = 0x9E3779B97F4A7C15;
uint64_t round_key[2]; memset(round_key, 0, sizeof(round_key));
uint64_t state[2]; memset(state, 0, sizeof(state));
//load input_register into state as two 64-bit chunks
for (int i = 0; i < 8; i++)
{
state[0] <<= 8;
state[0] |= input_register[i+0];
state[1] <<= 8;
state[1] |= input_register[i+8];
}
//load key into round_key as two 64-bit chunks
for (int i = 0; i < 8; i++)
{
round_key[0] <<= 8;
round_key[0] |= key[i+0];
round_key[1] <<= 8;
round_key[1] |= key[i+8];
}
for (int i = 0; i < 64; i++)
{
round_key[0] += round;
round_key[1] -= round;
state[0] ^= round_key[0];
state[0] += state[1];
state[0] = ROTATE_LEFT(state[0], state[1]);
state[1] = ROTATE_RIGHT(state[1], state[0]);
state[1] -= state[0];
state[1] ^= round_key[1];
}
uint64_t temp = state[0];
state[0] = state[1];
state[1] = temp;
//reload input_register with current state values
for (int i = 0; i < 8; i++)
input_register[i+0] = (state[0] >> (56 - (i * 8))) & 0xFF;
for (int i = 0; i < 8; i++)
input_register[i+8] = (state[1] >> (56 - (i * 8))) & 0xFF;
}
//dsd-fme -fs -Z -i 'SSD_STORAGE/2025_DEV/Tytera BP EP AP Master Folder/kirisun/cap-clear.wav/cap-33.wav' -H '3333333333333333 3333333333333333 3333333333333333 3333333333333333'
void kirisun_universal_keystream_creation(dsd_state *state)
{ {
uint8_t slot = 0; uint8_t slot = 0;
if (state->currentslot == 0) if (state->currentslot == 0)
@@ -292,8 +356,8 @@ void kirisun_uni_keystream_creation(dsd_state *state)
} }
//dsd-fme -fs -Z -i '/SSD_STORAGE/2025_DEV/Tytera BP EP AP Master Folder/kirisun/better_samples_tg1/kirisun_advanced_tg1.wav' -H 'DC1A7E9F9BF312DB F45010CEC5F7A53A C407D0BFA803617B E426A7254DA9390D' //dsd-fme -fs -Z -i 'SSD_STORAGE/2025_DEV/Tytera BP EP AP Master Folder/kirisun/better_samples_tg1/kirisun_advanced_tg1.wav' -H 'DC1A7E9F9BF312DB F45010CEC5F7A53A C407D0BFA803617B E426A7254DA9390D'
void kirisun_adv_keystream_creation(dsd_state *state) void kirisun_advanced_keystream_creation(dsd_state *state)
{ {
uint8_t slot = 0; uint8_t slot = 0;
@@ -415,3 +479,764 @@ void kirisun_adv_keystream_creation(dsd_state *state)
} }
} }
//dsd-fme -fs -Z -i 'SSD_STORAGE/2025_DEV/Tytera BP EP AP Master Folder/kirisun/enhanced-key-12.wav/enhanced-key-12.wav' -H 'C607727B8BECC3CB 7DD174F5526BB546'
void kirisun_enhanced_keystream_creation(dsd_state *state)
{
uint8_t slot = 0;
if (state->currentslot == 0)
slot = 0;
else slot = 1;
uint32_t lfsr = 0;
if (state->currentslot == 0)
lfsr = (uint32_t)state->payload_mi;
else lfsr = (uint32_t)state->payload_miR;
unsigned char userkeyhexa[32]; memset(userkeyhexa, 0, sizeof(userkeyhexa));
unsigned char realkey[32]; memset(realkey, 0, sizeof(realkey));
unsigned char lfsrhexa[4]; memset(lfsrhexa, 0, sizeof(lfsrhexa));
uint8_t ks_bytes[128]; memset(ks_bytes, 0, sizeof(ks_bytes));
lfsrhexa[0] = (lfsr >> 24) & 0xFF;
lfsrhexa[1] = (lfsr >> 16) & 0xFF;
lfsrhexa[2] = (lfsr >> 8) & 0xFF;
lfsrhexa[3] = (lfsr >> 0) & 0xFF;
uint64_t K1 = state->A1[slot];
uint64_t K2 = state->A2[slot];
if (K1 != 0 && K2 != 0)
state->aes_key_loaded[slot] = 1;
else state->aes_key_loaded[slot] = 0;
if (state->aes_key_loaded[slot] == 1)
{
u64_to_bytes_be(K1, &userkeyhexa[0]);
u64_to_bytes_be(K2, &userkeyhexa[8]);
// printf("=== userkey input (16 bytes) ===\n");
// print_hex(userkeyhexa,16);
/* --- MD2II first pass --- */
n1global=16;
unsigned char h4[n1]; memset(h4, 0, sizeof(h4));
MD2II_init();
MD2II_hashing(userkeyhexa,16);
MD2II_end(h4);
for(int i=0;i<16;i++) realkey[i]=h4[i];
// printf("=== realkey final (16 bytes) ===\n");
// print_hex(realkey,16);
n1global=16;
MD2II_init();
MD2II_hashing(lfsrhexa,4);
MD2II_hashing(realkey,16);
MD2II_end(h4);
uint8_t input_register[16]; memset(input_register, 0, sizeof(input_register));
//copy h4 to the input_register for the cipher
memcpy(input_register, h4, 16);
/* --- MD2II with n1global=32 --- */
n1global=32;
MD2II_init();
MD2II_hashing(lfsrhexa,4);
MD2II_hashing(realkey,16);
MD2II_end(h4);
//copy h4 to the tab for the cipher
uint8_t tab[16]; memset(tab, 0, sizeof(tab));
memcpy(tab, h4, 16);
// printf("=== Input Register (16 bytes) ===\n");
// print_hex(input_register,16);
// printf("=== Tab (16 bytes) ===\n");
// print_hex(tab,16);
/* --- keystream output --- */
uint8_t output[128]; memset(output, 0, sizeof(output));
//discard round
keystream35(input_register, tab);
for (int i = 0; i < 8; i++)
{
keystream35(input_register, tab);
memcpy(output+(i*16), input_register, 16);
}
//store ks_bytes to storage
for (int i = 0; i < 128; i++)
ks_bytes[i] = output[i];
if (slot == 0)
memcpy(state->ks_octetL, ks_bytes, sizeof(ks_bytes));
else memcpy(state->ks_octetR, ks_bytes, sizeof(ks_bytes));
//debug ks_bytes
// fprintf (stderr, "KS: ");
// for (int i = 0; i < 128; i++)
// {
// if ((i != 0) && ((i%16)==0))
// fprintf (stderr, "\n ");
// fprintf (stderr, "%02X ", ks_bytes[i]);
// }
// fprintf (stderr, "\n");
}
}
//dsd-fme -fs -Z -i 'SSD_STORAGE/2025_DEV/Tytera BP EP AP Master Folder/kirisun/kirisun_files/reinforced-key-9.wav' -H 'C607727B8BECC3CB 7DD174F5526BB546 B3DEA1C111DC1994 69243120CDB6F99A'
void kirisun_reinforced_keystream_creation(dsd_state *state)
{
uint8_t slot = 0;
if (state->currentslot == 0)
slot = 0;
else slot = 1;
uint32_t lfsr = 0;
if (state->currentslot == 0)
lfsr = (uint32_t)state->payload_mi;
else lfsr = (uint32_t)state->payload_miR;
unsigned char userkeyhexa[32]; memset(userkeyhexa, 0, sizeof(userkeyhexa));
unsigned char realkey[32]; memset(realkey, 0, sizeof(realkey));
unsigned char lfsrhexa[4]; memset(lfsrhexa, 0, sizeof(lfsrhexa));
uint8_t ks_bytes[128]; memset(ks_bytes, 0, sizeof(ks_bytes));
lfsrhexa[0] = (lfsr >> 24) & 0xFF;
lfsrhexa[1] = (lfsr >> 16) & 0xFF;
lfsrhexa[2] = (lfsr >> 8) & 0xFF;
lfsrhexa[3] = (lfsr >> 0) & 0xFF;
uint64_t K1 = state->A1[slot];
uint64_t K2 = state->A2[slot];
uint64_t K3 = state->A3[slot];
uint64_t K4 = state->A4[slot];
if (K1 != 0 && K2 != 0 && K3 != 0 && K4 != 0)
state->aes_key_loaded[slot] = 1;
else state->aes_key_loaded[slot] = 0;
if (state->aes_key_loaded[slot] == 1)
{
u64_to_bytes_be(K1, &userkeyhexa[0]);
u64_to_bytes_be(K2, &userkeyhexa[8]);
u64_to_bytes_be(K3, &userkeyhexa[16]);
u64_to_bytes_be(K4, &userkeyhexa[24]);
for(int i=0;i<32;i++) realkey[i]=userkeyhexa[i];
// printf("=== userkey input (32 bytes) ===\n");
// print_hex(userkeyhexa,32);
/* --- MD2II first pass --- */
n1global=32;
unsigned char h4[n1]; memset(h4, 0, sizeof(h4));
MD2II_init();
MD2II_hashing(realkey,32);
MD2II_end(h4);
for(int i=0;i<32;i++) realkey[i]=h4[i];
// printf("=== realkey final (32 bytes) ===\n");
// print_hex(realkey,32);
n1global=8;
MD2II_init();
MD2II_hashing(lfsrhexa,4);
MD2II_hashing(realkey,32);
MD2II_end(h4);
// printf("h4[0..7]: ");
// print_hex(h4,8);
uint64_t internal_state=0;
for(int i=0;i<8;i++)
internal_state=(internal_state<<8)+(h4[i]);
// printf("internal_state=%016llX\n",(unsigned long long)internal_state);
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy internal state value to the input_register for the modified DES cipher
memcpy(input_register, h4, 8);
/* --- MD2II with n1global=96 --- */
n1global=96;
MD2II_init();
MD2II_hashing(lfsrhexa,4);
MD2II_hashing(realkey,32);
MD2II_end(h4);
// printf("realkey (96 bytes):\n");
// print_hex(h4,96);
/* --- keystream output --- */
uint8_t output[128]; memset(output, 0, sizeof(output));
//run discard round first
ksun_des_cipher(h4, input_register, output_register, 1);
memcpy(input_register, output_register, 8); //internal_state = des_reinforced(internal_state, encrypt);
memset(output_register, 0, sizeof(output_register));
//begin creation of actual keystream now
for (int i = 0; i < 16; i++)
{
//the internal state is the output feedback mode
ksun_des_cipher(h4, input_register, output_register, 1);
memcpy(output+(i*8), output_register, 8);
memcpy(input_register, output_register, 8);
memset(output_register, 0, sizeof(output_register));
}
// printf("keystream output (128 bytes):\n");
// print_hex(output,128);
//store ks_bytes to storage
for (int i = 0; i < 128; i++)
ks_bytes[i] = output[i];
if (slot == 0)
memcpy(state->ks_octetL, ks_bytes, sizeof(ks_bytes));
else memcpy(state->ks_octetR, ks_bytes, sizeof(ks_bytes));
//debug ks_bytes
// fprintf (stderr, "KS: ");
// for (int i = 0; i < 128; i++)
// {
// if ((i != 0) && ((i%16)==0))
// fprintf (stderr, "\n ");
// fprintf (stderr, "%02X ", ks_bytes[i]);
// }
// fprintf (stderr, "\n");
}
}
//dsd-fme -fs -Z -i 'SSD_STORAGE/2025_DEV/Tytera BP EP AP Master Folder/kirisun/kirisun_files/strengthened-key-7.wav' -H 'C607727B8BECC3CB 7DD174F5526BB546 B3DEA1C111DC1994 69243120CDB6F99A'
void kirisun_strengthened_keystream_creation(dsd_state *state)
{
uint8_t slot = 0;
if (state->currentslot == 0)
slot = 0;
else slot = 1;
uint32_t lfsr = 0;
if (state->currentslot == 0)
lfsr = (uint32_t)state->payload_mi;
else lfsr = (uint32_t)state->payload_miR;
unsigned char userkeyhexa[32]; memset(userkeyhexa, 0, sizeof(userkeyhexa));
unsigned char realkey[32]; memset(realkey, 0, sizeof(realkey));
unsigned char lfsrhexa[4]; memset(lfsrhexa, 0, sizeof(lfsrhexa));
uint8_t ks_bytes[128]; memset(ks_bytes, 0, sizeof(ks_bytes));
uint8_t global_key[288]; memset (global_key, 0, sizeof(global_key));
lfsrhexa[0] = (lfsr >> 24) & 0xFF;
lfsrhexa[1] = (lfsr >> 16) & 0xFF;
lfsrhexa[2] = (lfsr >> 8) & 0xFF;
lfsrhexa[3] = (lfsr >> 0) & 0xFF;
uint64_t K1 = state->A1[slot];
uint64_t K2 = state->A2[slot];
uint64_t K3 = state->A3[slot];
uint64_t K4 = state->A4[slot];
if (K1 != 0 && K2 != 0 && K3 != 0 && K4 != 0)
state->aes_key_loaded[slot] = 1;
else state->aes_key_loaded[slot] = 0;
if (state->aes_key_loaded[slot] == 1)
{
u64_to_bytes_be(K1, &userkeyhexa[0]);
u64_to_bytes_be(K2, &userkeyhexa[8]);
u64_to_bytes_be(K3, &userkeyhexa[16]);
u64_to_bytes_be(K4, &userkeyhexa[24]);
for(int i=0;i<32;i++) realkey[i]=userkeyhexa[i];
// printf("=== userkey input (32 bytes) ===\n");
// print_hex(userkeyhexa,32);
/* --- MD2II first pass --- */
n1global=32;
unsigned char h4[288]; memset(h4, 0, sizeof(h4));
MD2II_init();
MD2II_hashing(realkey,32);
MD2II_end(h4);
for(int i=0;i<32;i++) realkey[i]=h4[i];
// printf("=== realkey final (32 bytes) ===\n");
// print_hex(realkey,32);
n1global=288;
MD2II_init();
MD2II_hashing(realkey, 32);
MD2II_end(h4);
for (int i=0;i<288;i++) global_key[i]=h4[i];
// printf("=== global key (288 bytes) ===\n");
// print_hex(realkey,288);
n1global=8;
MD2II_init();
MD2II_hashing(lfsrhexa,4);
MD2II_hashing(realkey,32);
MD2II_end(h4);
// printf("h4[0..7]: ");
// print_hex(h4,8);
uint64_t internal_state=0;
for(int i=0;i<8;i++)
internal_state=(internal_state<<8)+(h4[i]);
// printf("internal_state=%016llX\n",(unsigned long long)internal_state);
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy internal state value to the input_register for the modified DES cipher
memcpy(input_register, h4, 8);
/* --- MD2II with n1global=96 --- */
n1global=96;
MD2II_init();
MD2II_hashing(lfsrhexa,4);
MD2II_hashing(realkey,32);
MD2II_end(h4);
// printf("realkey (96 bytes):\n");
// print_hex(h4,96);
/* --- keystream output --- */
uint8_t output[128]; memset(output, 0, sizeof(output));
//run discard round first
ksun_des_cipher(global_key+0, input_register, output_register, 1);
memcpy(input_register, output_register, 8); //internal_state = des_reinforced(internal_state, encrypt);
memset(output_register, 0, sizeof(output_register));
ksun_des_cipher(global_key+96, input_register, output_register, 0);
memcpy(input_register, output_register, 8); //internal_state = des_reinforced(internal_state, encrypt);
memset(output_register, 0, sizeof(output_register));
ksun_des_cipher(global_key+192, input_register, output_register, 1);
memcpy(input_register, output_register, 8); //internal_state = des_reinforced(internal_state, encrypt);
memset(output_register, 0, sizeof(output_register));
//begin creation of actual keystream now
for (int i = 0; i < 16; i++)
{
//the internal state is the output feedback mode
ksun_des_cipher(global_key+0, input_register, output_register, 1);
memcpy(input_register, output_register, 8); //internal_state = des_reinforced(internal_state, encrypt);
memset(output_register, 0, sizeof(output_register));
ksun_des_cipher(global_key+96, input_register, output_register, 0);
memcpy(input_register, output_register, 8); //internal_state = des_reinforced(internal_state, encrypt);
memset(output_register, 0, sizeof(output_register));
ksun_des_cipher(global_key+192, input_register, output_register, 1);
memcpy(input_register, output_register, 8); //internal_state = des_reinforced(internal_state, encrypt);
//copy out last output_register to output
memcpy(output+(i*8), output_register, 8);
memset(output_register, 0, sizeof(output_register));
}
// printf("keystream output (128 bytes):\n");
// print_hex(output,128);
//store ks_bytes to storage
for (int i = 0; i < 128; i++)
ks_bytes[i] = output[i];
if (slot == 0)
memcpy(state->ks_octetL, ks_bytes, sizeof(ks_bytes));
else memcpy(state->ks_octetR, ks_bytes, sizeof(ks_bytes));
//debug ks_bytes
// fprintf (stderr, "KS: ");
// for (int i = 0; i < 128; i++)
// {
// if ((i != 0) && ((i%16)==0))
// fprintf (stderr, "\n ");
// fprintf (stderr, "%02X ", ks_bytes[i]);
// }
// fprintf (stderr, "\n");
}
}
void kirisun_low_keystream_creation(dsd_state *state)
{
UNUSED(state);
}
void kirisun_middle_keystream_creation(dsd_state *state)
{
UNUSED(state);
}
void kirisun_high_keystream_creation(dsd_state *state)
{
UNUSED(state);
}
void kirisun_top_keystream_creation(dsd_state *state)
{
UNUSED(state);
}
#define BLOCK_BYTES 512
#define MEMORY_BYTES 4608
static uint8_t memory_area[MEMORY_BYTES];
static uint8_t *substitution_area = memory_area;
static uint8_t *state_area = memory_area + 2048;
static uint8_t *feedback_area = memory_area + 3584;
static uint8_t *temporary_area = memory_area + 4096;
static size_t processed_bytes;
static uint8_t feedback_value;
static const uint8_t substitution_maps[16][16] = {
{ 0x4, 0xA, 0x9, 0x2, 0xD, 0x8, 0x0, 0xE, 0x6, 0xB, 0x1, 0xC, 0x7, 0xF, 0x5, 0x3 },
{ 0xE, 0xB, 0x4, 0xC, 0x6, 0xD, 0xF, 0xA, 0x2, 0x3, 0x8, 0x1, 0x0, 0x7, 0x5, 0x9 },
{ 0x5, 0x8, 0x1, 0xD, 0xA, 0x3, 0x4, 0x2, 0xE, 0xF, 0xC, 0x7, 0x6, 0x0, 0x9, 0xB },
{ 0x7, 0xD, 0xA, 0x1, 0x0, 0x8, 0x9, 0xF, 0xE, 0x4, 0x6, 0xC, 0xB, 0x2, 0x5, 0x3 },
{ 0x6, 0xC, 0x7, 0x1, 0x5, 0xF, 0xD, 0x8, 0x4, 0xA, 0x9, 0xE, 0x0, 0x3, 0xB, 0x2 },
{ 0x4, 0xB, 0xA, 0x0, 0x7, 0x2, 0x1, 0xD, 0x3, 0x6, 0x8, 0x5, 0x9, 0xC, 0xF, 0xE },
{ 0xD, 0xB, 0x4, 0x1, 0x3, 0xF, 0x5, 0x9, 0x0, 0xA, 0xE, 0x7, 0x6, 0x8, 0x2, 0xC },
{ 0x1, 0xF, 0xD, 0x0, 0x5, 0x7, 0xA, 0x4, 0x9, 0x2, 0x3, 0xE, 0x6, 0xB, 0x8, 0xC },
{ 0xC, 0x4, 0x6, 0x2, 0xA, 0x5, 0xB, 0x9, 0xE, 0x8, 0xD, 0x7, 0x0, 0x3, 0xF, 0x1 },
{ 0x6, 0x8, 0x2, 0x3, 0x9, 0xA, 0x5, 0xC, 0x1, 0xE, 0x4, 0x7, 0xB, 0xD, 0x0, 0xF },
{ 0xB, 0x3, 0x5, 0x8, 0x2, 0xF, 0xA, 0xD, 0xE, 0x1, 0x7, 0x4, 0xC, 0x9, 0x6, 0x0 },
{ 0xC, 0x8, 0x2, 0x1, 0xD, 0x4, 0xF, 0x6, 0x7, 0x0, 0xA, 0x5, 0x3, 0xE, 0x9, 0xB },
{ 0x7, 0xF, 0x5, 0xA, 0x8, 0x1, 0x6, 0xD, 0x0, 0x9, 0x3, 0xE, 0xB, 0x4, 0x2, 0xC },
{ 0x5, 0xD, 0xF, 0x6, 0x9, 0x2, 0xC, 0xA, 0xB, 0x7, 0x8, 0x1, 0x4, 0x3, 0xE, 0x0 },
{ 0x8, 0xE, 0x2, 0x5, 0x6, 0x9, 0x1, 0xC, 0xF, 0x4, 0xB, 0x0, 0xD, 0xA, 0x3, 0x7 },
{ 0x1, 0x7, 0xE, 0xD, 0x0, 0x5, 0x8, 0x3, 0x4, 0xF, 0xA, 0x6, 0x9, 0xC, 0xB, 0x2 }
};
static const uint8_t nonlinear_map[256] = {
13, 199, 11, 67, 237, 193, 164, 77, 115, 184, 141, 222, 73,
38, 147, 36, 150, 87, 21, 104, 12, 61, 156, 101, 111, 145,
119, 22, 207, 35, 198, 37, 171, 167, 80, 30, 219, 28, 213,
121, 86, 29, 214, 242, 6, 4, 89, 162, 110, 175, 19, 157,
3, 88, 234, 94, 144, 118, 159, 239, 100, 17, 182, 173, 238,
68, 16, 79, 132, 54, 163, 52, 9, 58, 57, 55, 229, 192,
170, 226, 56, 231, 187, 158, 70, 224, 233, 245, 26, 47, 32,
44, 247, 8, 251, 20, 197, 185, 109, 153, 204, 218, 93, 178,
212, 137, 84, 174, 24, 120, 130, 149, 72, 180, 181, 208, 255,
189, 152, 18, 143, 176, 60, 249, 27, 227, 128, 139, 243, 253,
59, 123, 172, 108, 211, 96, 138, 10, 215, 42, 225, 40, 81,
65, 90, 25, 98, 126, 154, 64, 124, 116, 122, 5, 1, 168,
83, 190, 131, 191, 244, 240, 235, 177, 155, 228, 125, 66, 43,
201, 248, 220, 129, 188, 230, 62, 75, 71, 78, 34, 31, 216,
254, 136, 91, 114, 106, 46, 217, 196, 92, 151, 209, 133, 51,
236, 33, 252, 127, 179, 69, 7, 183, 105, 146, 97, 39, 15,
205, 112, 200, 166, 223, 45, 48, 246, 186, 41, 148, 140, 107,
76, 85, 95, 194, 142, 50, 49, 134, 23, 135, 169, 221, 210,
203, 63, 165, 82, 161, 202, 53, 14, 206, 232, 103, 102, 195,
117, 250, 99, 0, 74, 160, 241, 2, 113};
void prepare_tables(void)
{
for (unsigned int index = 0; index < 256; ++index) {
substitution_area[7 * 256 + index] = (uint8_t)((substitution_maps[15][index >> 4] << 4) | substitution_maps[14][index & 15]);
substitution_area[6 * 256 + index] = (uint8_t)((substitution_maps[13][index >> 4] << 4) | substitution_maps[12][index & 15]);
substitution_area[5 * 256 + index] = (uint8_t)((substitution_maps[11][index >> 4] << 4) | substitution_maps[10][index & 15]);
substitution_area[4 * 256 + index] = (uint8_t)((substitution_maps[9][index >> 4] << 4) | substitution_maps[8][index & 15]);
substitution_area[3 * 256 + index] = (uint8_t)((substitution_maps[7][index >> 4] << 4) | substitution_maps[6][index & 15]);
substitution_area[2 * 256 + index] = (uint8_t)((substitution_maps[5][index >> 4] << 4) | substitution_maps[4][index & 15]);
substitution_area[1 * 256 + index] = (uint8_t)((substitution_maps[3][index >> 4] << 4) | substitution_maps[2][index & 15]);
substitution_area[index] = (uint8_t)((substitution_maps[1][index >> 4] << 4) | substitution_maps[0][index & 15]);
}
}
void reset_state(void)
{
processed_bytes = 0;
feedback_value = 0;
memset(feedback_area, 0, 512);
memset(state_area, 0, 1536);
}
void md2IIult(const uint8_t input[], size_t length)
{
size_t input_position = 0;
while (length != 0) {
for (; length && processed_bytes < BLOCK_BYTES; --length, ++processed_bytes) {
uint8_t current_byte = input[input_position++];
state_area[processed_bytes + BLOCK_BYTES] = current_byte;
state_area[processed_bytes + 2 * BLOCK_BYTES] = (uint8_t)(current_byte ^ state_area[processed_bytes]);
feedback_area[processed_bytes] ^= nonlinear_map[current_byte ^ feedback_value];
feedback_value = feedback_area[processed_bytes];
}
if (processed_bytes == BLOCK_BYTES) {
unsigned int accumulator = 0;
processed_bytes = 0;
for (unsigned int round = 0; round < BLOCK_BYTES + 2; ++round) {
for (unsigned int index = 0; index < 3 * BLOCK_BYTES; ++index) {
state_area[index] ^= nonlinear_map[accumulator];
accumulator = state_area[index];
}
accumulator = (accumulator + round) & 255U;
}
}
}
}
void finalize_state(uint8_t output[BLOCK_BYTES])
{
size_t remaining_bytes = BLOCK_BYTES - processed_bytes;
memset(temporary_area, (int)remaining_bytes, remaining_bytes);
md2IIult(temporary_area, remaining_bytes);
md2IIult(feedback_area, BLOCK_BYTES);
memcpy(output, state_area, BLOCK_BYTES);
}
void derive_schedule(const uint8_t digest[BLOCK_BYTES], uint8_t schedule[BLOCK_BYTES])
{
size_t digest_position = 0;
memset(schedule, 0, BLOCK_BYTES);
for (unsigned int block = 0; block < 64; ++block) {
uint64_t value = 0;
for (unsigned int index = 0; index < 8; ++index) value = (value << 8) | digest[digest_position++];
memcpy(schedule + block * 8, &value, sizeof(value));
}
}
static uint64_t transform_word(uint64_t value)
{
uint32_t upper_word = (uint32_t)(value >> 32);
uint32_t lower_word = (uint32_t)value;
upper_word = ((uint32_t)substitution_area[3 * 256 + ((upper_word >> 24) & 255u)] << 24) |
((uint32_t)substitution_area[2 * 256 + ((upper_word >> 16) & 255u)] << 16) |
((uint32_t)substitution_area[1 * 256 + ((upper_word >> 8) & 255u)] << 8) |
(uint32_t)substitution_area[upper_word & 255u];
lower_word = ((uint32_t)substitution_area[7 * 256 + ((lower_word >> 24) & 255u)] << 24) |
((uint32_t)substitution_area[6 * 256 + ((lower_word >> 16) & 255u)] << 16) |
((uint32_t)substitution_area[5 * 256 + ((lower_word >> 8) & 255u)] << 8) |
(uint32_t)substitution_area[4 * 256 + (lower_word & 255u)];
value = ((uint64_t)upper_word << 32) | lower_word;
return (value << 11) | (value >> (64 - 11));
}
void encode_block(const uint64_t input[2], uint64_t output[2], const uint8_t schedule[BLOCK_BYTES])
{
uint64_t left_word = input[0], right_word = input[1];
for (unsigned int round = 0; round < 32; ++round) {
uint64_t round_key;
memcpy(&round_key, schedule + round * 16, sizeof(round_key));
right_word ^= transform_word(left_word + round_key);
memcpy(&round_key, schedule + round * 16 + 8, sizeof(round_key));
left_word ^= transform_word(right_word + round_key);
}
output[0] = right_word;
output[1] = left_word;
}
void decode_block(const uint64_t input[2], uint64_t output[2], const uint8_t schedule[BLOCK_BYTES])
{
uint64_t left_word = input[0], right_word = input[1];
for (int round = 31; round >= 0; --round) {
uint64_t round_key;
memcpy(&round_key, schedule + round * 16 + 8, sizeof(round_key));
right_word ^= transform_word(left_word + round_key);
memcpy(&round_key, schedule + round * 16, sizeof(round_key));
left_word ^= transform_word(right_word + round_key);
}
output[0] = right_word;
output[1] = left_word;
}
void kirisun_ultra_keystream_creation(dsd_state *state)
{
uint8_t slot = 0;
if (state->currentslot == 0)
slot = 0;
else slot = 1;
uint32_t lfsr = 0;
if (state->currentslot == 0)
lfsr = (uint32_t)state->payload_mi;
else lfsr = (uint32_t)state->payload_miR;
unsigned char userkeyhexa[32]; memset(userkeyhexa, 0, sizeof(userkeyhexa));
unsigned char realkey[32]; memset(realkey, 0, sizeof(realkey));
unsigned char lfsrhexa[4]; memset(lfsrhexa, 0, sizeof(lfsrhexa));
uint8_t ks_bytes[128]; memset(ks_bytes, 0, sizeof(ks_bytes));
lfsrhexa[0] = (lfsr >> 24) & 0xFF;
lfsrhexa[1] = (lfsr >> 16) & 0xFF;
lfsrhexa[2] = (lfsr >> 8) & 0xFF;
lfsrhexa[3] = (lfsr >> 0) & 0xFF;
uint64_t K1 = state->A1[slot];
uint64_t K2 = state->A2[slot];
uint64_t K3 = state->A3[slot];
uint64_t K4 = state->A4[slot];
if (K1 != 0 && K2 != 0 && K3 != 0 && K4 != 0)
state->aes_key_loaded[slot] = 1;
else state->aes_key_loaded[slot] = 0;
if (state->aes_key_loaded[slot] == 1)
{
u64_to_bytes_be(K1, &userkeyhexa[0]);
u64_to_bytes_be(K2, &userkeyhexa[8]);
u64_to_bytes_be(K3, &userkeyhexa[16]);
u64_to_bytes_be(K4, &userkeyhexa[24]);
for(int i=0;i<32;i++) realkey[i]=userkeyhexa[i];
// printf("=== userkey input (32 bytes) ===\n");
// print_hex(userkeyhexa,32);
/* --- MD2II first pass --- */
n1global=32;
unsigned char h4[n1]; memset(h4, 0, sizeof(h4));
MD2II_init();
MD2II_hashing(realkey,32);
MD2II_end(h4);
for(int i=0;i<32;i++) realkey[i]=h4[i];
// printf("=== realkey final (32 bytes) ===\n");
// print_hex(realkey,32);
unsigned char digest[512]; memset(digest, 0, sizeof(digest));
unsigned char schedule[512]; memset(schedule, 0, sizeof(schedule));
prepare_tables();
reset_state();
md2IIult(realkey, 32);
finalize_state(digest);
derive_schedule(digest, schedule);
n1global=32;
MD2II_init();
MD2II_hashing(lfsrhexa,4);
MD2II_hashing(realkey,32);
MD2II_end(h4);
// printf("h4[0..7]: ");
// print_hex(h4,8);
//the state machine is comprised of two 64-bit words that
//when output become the bytes of the keystream
uint64_t internal_state[2]; memset(internal_state, 0, sizeof(internal_state));
for (int i = 0; i < 8; i++)
{
internal_state[0] <<= 8;
internal_state[0] |= (uint64_t)h4[i];
internal_state[1] <<= 8;
internal_state[1] |= (uint64_t)h4[i+8];
}
/* --- keystream output --- */
uint8_t output[128]; memset(output, 0, sizeof(output));
for (int i = 0; i < 8; i++)
{
encode_block(internal_state, internal_state, schedule);
output[(i*16)+0] = (internal_state[0] >> 56) & 0xFF;
output[(i*16)+1] = (internal_state[0] >> 48) & 0xFF;
output[(i*16)+2] = (internal_state[0] >> 40) & 0xFF;
output[(i*16)+3] = (internal_state[0] >> 32) & 0xFF;
output[(i*16)+4] = (internal_state[0] >> 24) & 0xFF;
output[(i*16)+5] = (internal_state[0] >> 16) & 0xFF;
output[(i*16)+6] = (internal_state[0] >> 8) & 0xFF;
output[(i*16)+7] = (internal_state[0] >> 0) & 0xFF;
output[(i*16)+8] = (internal_state[1] >> 56) & 0xFF;
output[(i*16)+9] = (internal_state[1] >> 48) & 0xFF;
output[(i*16)+10] = (internal_state[1] >> 40) & 0xFF;
output[(i*16)+11] = (internal_state[1] >> 32) & 0xFF;
output[(i*16)+12] = (internal_state[1] >> 24) & 0xFF;
output[(i*16)+13] = (internal_state[1] >> 16) & 0xFF;
output[(i*16)+14] = (internal_state[1] >> 8) & 0xFF;
output[(i*16)+15] = (internal_state[1] >> 0) & 0xFF;
}
// printf("keystream output (128 bytes):\n");
// print_hex(output,128);
//store ks_bytes to storage
for (int i = 0; i < 128; i++)
ks_bytes[i] = output[i];
if (slot == 0)
memcpy(state->ks_octetL, ks_bytes, sizeof(ks_bytes));
else memcpy(state->ks_octetR, ks_bytes, sizeof(ks_bytes));
//debug ks_bytes
// fprintf (stderr, "KS: ");
// for (int i = 0; i < 128; i++)
// {
// if ((i != 0) && ((i%16)==0))
// fprintf (stderr, "\n ");
// fprintf (stderr, "%02X ", ks_bytes[i]);
// }
// fprintf (stderr, "\n");
}
}
//General Keystream Creator for any known Kirisun cipher
void kirisun_keystream_creation(dsd_state * state)
{
uint8_t algid = 0;
if (state->currentslot == 0)
algid = state->payload_algid;
else algid = state->payload_algidR;
if (algid == 0x31)
kirisun_reinforced_keystream_creation(state);
else if (algid == 0x32)
kirisun_low_keystream_creation(state);
else if (algid == 0x33)
kirisun_middle_keystream_creation(state);
else if (algid == 0x34)
kirisun_high_keystream_creation(state);
else if (algid == 0x35)
kirisun_enhanced_keystream_creation(state);
else if (algid == 0x36)
kirisun_advanced_keystream_creation(state);
else if (algid == 0x37)
kirisun_universal_keystream_creation(state);
else if (algid == 0x38)
kirisun_strengthened_keystream_creation(state);
else if (algid == 0x39)
kirisun_top_keystream_creation(state);
else if (algid == 0x3A)
kirisun_ultra_keystream_creation(state);
}

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

@@ -0,0 +1,458 @@
//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");
//collect 7 bytes of the MD5 Hash to create the flat key value
unsigned long long int flat_key = 0;
for (int i = 0; i < 7; i++)
{
flat_key <<= 8;
flat_key |= ctx.digest[i];
}
//On real world testing, it was discovered that by selecting a password (VluByDZhkPenguinkeeper) which has
//an MD5 partial hash of FFFFFFFFFEDFFF, the value actually placed into the radio was FFFFFFFFFEE001
//this indicates the entire value will roll-over, and not just a byte rollvoer (mod)
//The known rule is that the last 5 bits of the flat key value (prior to reverse)
//is that it cannot equal 0x1F (all 1 bits), if so, a correction of +2 is
//performaed onto a flat value, which increments the entire value appropriately
//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
if ((flat_key & 0x1F) == 0x1F)
{
fprintf (stderr, "Flat Correction: %014llX -> %014llX;\n", flat_key, flat_key+2);
flat_key += 2;
}
//byte-reverse the flat_key into the effective_key
unsigned long long int effective_key = 0;
for (int i = 0; i < 7; i++)
{
effective_key <<= 8;
effective_key |= (flat_key >> (i*8)) & 0xFF;
}
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");
}

File diff suppressed because it is too large Load Diff

View File

@@ -307,55 +307,3 @@ void tyt_ep_aes_keystream_creation(dsd_state * state, char * input)
state->tyt_ep = 1; state->tyt_ep = 1;
} }
//connect systems 72-bit (9-byte) Extended Encryption //TODO: Move this later
void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24])
{
char interleaved[72];
memset (interleaved, 0, sizeof(interleaved));
//interleave the frame
const int *w, *x, *y, *z;
w = rW; x = rX; y = rY; z = rZ;
for (int8_t i = 0; i < 36; i++)
{
interleaved[(i*2)+0] = ambe_fr[*w][*x];
interleaved[(i*2)+1] = ambe_fr[*y][*z];
w++;
x++;
y++;
z++;
}
uint8_t ks_bytes[9]; memset(ks_bytes, 0, sizeof(ks_bytes));
uint8_t ks[72]; memset(ks, 0, sizeof(ks));
//keys are loaded in reverse byte order, stored in state->static_ks_bits[0]
for (int i = 0; i < 9; i++)
ks_bytes[i] = state->static_ks_bits[0][8-i];
//convert byte array into a bit array
unpack_byte_array_into_bit_array(ks_bytes, ks, 9);
//apply keystream to interleave
for (int8_t i = 0; i < 72; i++)
interleaved[i] ^= ks[71-i];
//deinterleave back into ambe_fr frame
w = rW; x = rX; y = rY; z = rZ;
int k = 0;
for (int8_t i = 0; i < 36; i++)
{
ambe_fr[*w][*x] = interleaved[k++];
ambe_fr[*y][*z] = interleaved[k++];
w++;
x++;
y++;
z++;
}
}

View File

@@ -187,6 +187,7 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
else if (p_mfid == 0x68) sprintf (mfid_string, "%s", "Hytera"); else if (p_mfid == 0x68) sprintf (mfid_string, "%s", "Hytera");
else if (p_mfid == 0x08) sprintf (mfid_string, "%s", "Hytera"); else if (p_mfid == 0x08) sprintf (mfid_string, "%s", "Hytera");
else if (p_mfid == 0x06) sprintf (mfid_string, "%s", "Trid/Mot"); else if (p_mfid == 0x06) sprintf (mfid_string, "%s", "Trid/Mot");
else if (p_mfid == 0x77) sprintf (mfid_string, "%s", "VTX STD");
else if (p_mfid == 0x00) sprintf (mfid_string, "%s", "Standard"); else if (p_mfid == 0x00) sprintf (mfid_string, "%s", "Standard");
else sprintf (mfid_string, "%s", "Other"); else sprintf (mfid_string, "%s", "Other");
} }
@@ -378,38 +379,77 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
if (p_sap != 1 && p_mfid == 0x77) if (p_sap != 1 && p_mfid == 0x77)
{ {
fprintf (stderr, "\n Vertex Standard PDU ENC Header:"); fprintf (stderr, "\n Vertex Standard PDU ENC Header;");
fprintf (stderr, " MFID: %02X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 8));
//Unsure of how similar this is to the DMRA Enc Header, but using //This Extended Header only seems to exist to signal encryption,
//guess work and best judgment from single sample of Vertex Enhanced //but is otherwise mostly empty aside from a FID and single 0x01 value
//[4F][77][01][00][00][00][00][00][00][00][XX][XX]
//main issue is, I don't know if the 0x01 signalled is a key id (which is known to be 0x01) //Further analysis shows that using different key types on different channels
//or if it is an encryption alg id (alg may have to be inferred based on what is loaded in radio) //does not change the byte values of this header, and what is reported is that
//the Vertex Standard radios only allow one encryption type and key per channel.
//set to 0x100 so it won't trigger any weird flags, but still has a non-zero value to be checked later //set to 0x100 so it won't trigger any weird flags, but still has a non-zero value to be checked later
if (state->currentslot == 0) state->dmr_so = 0x100; if (state->currentslot == 0) state->dmr_so = 0x100;
else state->dmr_soR = 0x100; else state->dmr_soR = 0x100;
if (state->currentslot == 0)
state->payload_keyid = (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8);
else state->payload_keyidR = (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8);
fprintf (stderr, " Key ID: %02X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8));
//going to use algid of 0x7 to signal a vertex standard enc method //going to use algid of 0x7 to signal a vertex standard enc method
if (state->currentslot == 0) if (state->currentslot == 0)
state->payload_algid = 0x7; state->payload_algid = 0x7;
else state->payload_algidR = 0x7; else state->payload_algidR = 0x7;
//unknown if MI, or IV is present on this header (need the AES256 Vertex PDU to compare)
if (state->currentslot == 0)
state->payload_mi = (unsigned long long int)ConvertBitIntoBytes(&dheader_bits[48], 32);
else state->payload_miR = (unsigned long long int)ConvertBitIntoBytes(&dheader_bits[48], 32);
//reset ks start value //reset ks start value
state->data_ks_start[slot] = 0; state->data_ks_start[slot] = 0;
} }
else if (p_sap != 1 && p_mfid == 0x68)
{
//TODO: Assume Alg, Key ID, IV and other values present in this header
//if using Hytera Enhanced RC4, otherwise, may use DMRA header
//ALG 1 = BP (observed), ALG 2 = Hytera Enhanced (not observed, but speculation)
/*
15:44:00 Sync: +DMR MS/DM MODE/MONO | Color Code=01 | DATA
Slot 1 Data Header - Extended - SAP 03 [UDP Comp] - MFID 68 [Hytera]
Unknown Extended Header: 0101000000000000
DMR PDU Payload [3F][68][01][01][00][00][00][00][00][00][E6][71] <-- key 1
16:06:21 Sync: +DMR MS/DM MODE/MONO | Color Code=02 | DATA
Slot 1 Data Header - Extended - SAP 03 [UDP Comp] - MFID 68 [Hytera]
Hytera PDU ENC Header;
DMR PDU Payload [3F][68][01][02][00][00][00][00][00][00][3E][F3] <--key 2
*/
uint8_t alg = (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8);
uint8_t key = (uint8_t)ConvertBitIntoBytes(&dheader_bits[24], 8);
uint64_t mi = (uint64_t)ConvertBitIntoBytes(&dheader_bits[32], 40);
fprintf (stderr, "\n Hytera PDU ENC Header: MFID: %02X; ALG: %02X; Key ID: %02X;", p_mfid, alg, key);
if (mi != 0) fprintf (stderr, "MI: %010llX", (unsigned long long int)mi);
//set to 0x100 so it won't trigger any weird flags, but still has a non-zero value to be checked later
if (state->currentslot == 0) state->dmr_so = 0x100;
else state->dmr_soR = 0x100;
//going to use algid of 0x68*alg to signal a Hytera enc method
//should be 0x68 for bp, or 0xD0 for enhanced
if (state->currentslot == 0)
state->payload_algid = 0x68 * alg;
else state->payload_algidR = 0x68 * alg;
if (state->currentslot == 0)
state->payload_keyid = key;
else state->payload_keyidR = key;
if (state->currentslot == 0)
state->payload_mi = mi;
else state->payload_miR = mi;
//reset ks start value
state->data_ks_start[slot] = 0;
}
else if (p_sap != 1 && p_mfid == 0x10) else if (p_sap != 1 && p_mfid == 0x10)
{ {
@@ -1047,11 +1087,11 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
kaes[i+8] = ((state->rkey_array[kid+0x101]) >> (56-(i*8))) & 0xFF; kaes[i+8] = ((state->rkey_array[kid+0x101]) >> (56-(i*8))) & 0xFF;
kaes[i+16] = ((state->rkey_array[kid+0x201]) >> (56-(i*8))) & 0xFF; kaes[i+16] = ((state->rkey_array[kid+0x201]) >> (56-(i*8))) & 0xFF;
kaes[i+24] = ((state->rkey_array[kid+0x301]) >> (56-(i*8))) & 0xFF; kaes[i+24] = ((state->rkey_array[kid+0x301]) >> (56-(i*8))) & 0xFF;
//if kaes is loaded with a key, then flag on the key loaded variable
if (memcmp(kaes, empt, sizeof(kaes)) != 0) akl = 1;
} }
//if kaes is loaded with a key, then flag on the key loaded variable
if (memcmp(kaes, empt, sizeof(kaes)) != 0) akl = 1;
//if still not akl, see if there is anything manually loaded into K1, K2, K3, K4 instead //if still not akl, see if there is anything manually loaded into K1, K2, K3, K4 instead
if (akl == 0) if (akl == 0)
{ {
@@ -1089,6 +1129,8 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
if (alg == 4) fprintf (stderr, " AES128;"); if (alg == 4) fprintf (stderr, " AES128;");
if (alg == 5) fprintf (stderr, " AES256;"); if (alg == 5) fprintf (stderr, " AES256;");
if (alg == 7) fprintf (stderr, " VTX STD;"); if (alg == 7) fprintf (stderr, " VTX STD;");
if (alg == 0x68) fprintf (stderr, " Hytera BP;");
if (alg == 0xD0) fprintf (stderr, " Hytera EP;");
if (R && alg != 0) fprintf (stderr, " Key: %010llX;", R); if (R && alg != 0) fprintf (stderr, " Key: %010llX;", R);
//generate 128-bit IV from 32-bit MI //generate 128-bit IV from 32-bit MI
@@ -1115,6 +1157,65 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
decrypted_pdu = 1; decrypted_pdu = 1;
} }
else if (alg == 0x68) //Hytera BP
{
//just passing through to see if a key was loaded and the blocks decrypted
decrypted_pdu = hytera_bp_data_decrypt(state, empt, 1);
}
else if (alg == 0xD0) //Hytera Enhanced RC4
{
//just passing through to see if a key was loaded and the blocks decrypted
decrypted_pdu = hytera_ep_data_decrypt(opts, state, empt, 1);
}
else if (alg == 7 && state->vtx_key_loaded == 1) //Vertex Standard, either enc type
{
pack_bit_array_into_byte_array(state->static_ks_bits[slot], ob, 8);
end += (poc + 4); //also perform application over padding bytes and crc value
for (i = 0; i < end; i++)
state->dmr_pdu_sf[slot][i+start] ^= ob[i%8];
decrypted_pdu = 1;
//Recalcuate CRC32
uint8_t reordered_bits[1000];
memset (reordered_bits, 0, sizeof(reordered_bits));
//rearrage for ridiculously stupid CRC32 LSO/MSO ordering
for(i = 0, j = 0; i < ctr; i+=2, j+=16)
{
reordered_bits[j + 0] = (state->dmr_pdu_sf[slot][i+1] >> 7) & 0x01;
reordered_bits[j + 1] = (state->dmr_pdu_sf[slot][i+1] >> 6) & 0x01;
reordered_bits[j + 2] = (state->dmr_pdu_sf[slot][i+1] >> 5) & 0x01;
reordered_bits[j + 3] = (state->dmr_pdu_sf[slot][i+1] >> 4) & 0x01;
reordered_bits[j + 4] = (state->dmr_pdu_sf[slot][i+1] >> 3) & 0x01;
reordered_bits[j + 5] = (state->dmr_pdu_sf[slot][i+1] >> 2) & 0x01;
reordered_bits[j + 6] = (state->dmr_pdu_sf[slot][i+1] >> 1) & 0x01;
reordered_bits[j + 7] = (state->dmr_pdu_sf[slot][i+1] >> 0) & 0x01;
reordered_bits[j + 8] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
reordered_bits[j + 9] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
reordered_bits[j + 10] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01;
reordered_bits[j + 11] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01;
reordered_bits[j + 12] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01;
reordered_bits[j + 13] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01;
reordered_bits[j + 14] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01;
reordered_bits[j + 15] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
}
CRCComputed = (uint32_t)ComputeCrc32BitV(reordered_bits, (ctr * 8) - 32);
CRCExtracted = (uint32_t)convert_bits_into_output(reordered_bits+((ctr * 8) - 32), 32);
if (CRCComputed == CRCExtracted)
CRCCorrect = 1;
else CRCCorrect = 0;
//set back to original value
end -= (poc + 4);
//debug
// fprintf (stderr, " CTR: %d; CRC32 CMP: %08X EXT: %08X", ctr, CRCComputed, CRCExtracted);
}
else if (alg == 5 && akl == 1 && mi != 0) //AES-256 OFB else if (alg == 5 && akl == 1 && mi != 0) //AES-256 OFB
{ {
int nblocks = (state->data_byte_ctr[slot] / 16) + 1; int nblocks = (state->data_byte_ctr[slot] / 16) + 1;
@@ -1266,7 +1367,8 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
} }
else if (state->data_header_sap[slot] == 2 || state->data_header_sap[slot] == 3) //TCP and UDP Compression (may only be 3 UDP compression, unknown) else if (state->data_header_sap[slot] == 2 || state->data_header_sap[slot] == 3) //TCP and UDP Compression (may only be 3 UDP compression, unknown)
{ {
uint16_t len = ((blocks+1)*block_len)-4; //total number of bytes in PDU minus 4 CRC32 bytes uint8_t poc = state->data_block_poc[slot]; //need to test if others also need this
uint16_t len = ((blocks+1)*block_len)-4 - poc; //total number of bytes in PDU minus 4 CRC32 bytes and padding bytes
dmr_udp_comp_pdu (opts, state, len, state->dmr_pdu_sf[slot]); dmr_udp_comp_pdu (opts, state, len, state->dmr_pdu_sf[slot]);
} }
else if (state->data_header_sap[slot] == 1 && state->dmr_pdu_sf[slot][1] == 0x10) //MNIS Proprietary Header else if (state->data_header_sap[slot] == 1 && state->dmr_pdu_sf[slot][1] == 0x10) //MNIS Proprietary Header
@@ -1604,4 +1706,12 @@ void dmr_reset_blocks (dsd_opts * opts, dsd_state * state)
// sprintf (state->call_string[1], "%s", " "); //21 spaces // sprintf (state->call_string[1], "%s", " "); //21 spaces
sprintf (state->dmr_lrrp_gps[0], "%s", ""); sprintf (state->dmr_lrrp_gps[0], "%s", "");
sprintf (state->dmr_lrrp_gps[1], "%s", ""); sprintf (state->dmr_lrrp_gps[1], "%s", "");
//Hytera OTA Encryption
memset(state->hota_lc_ctx, 0, sizeof(state->hota_lc_ctx));
memset(state->hota_ctx_ok, 0, sizeof(state->hota_ctx_ok));
state->hota_sbrc_idx1[0] = -1;
state->hota_sbrc_idx2[0] = -1;
state->hota_sbrc_idx1[1] = -1;
state->hota_sbrc_idx2[1] = -1;
} }

View File

@@ -462,6 +462,12 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
if (internalslot == 0 && vc1 == 6) if (internalslot == 0 && vc1 == 6)
{ {
if (state->hota_enc_check && state->forced_alg_id > 1)
{
state->dmr_so = 0x40;
state->payload_algid = state->forced_alg_id;
state->payload_keyid = 0xFF;
}
//this needs to run prior to embedded link control //this needs to run prior to embedded link control
if (state->payload_algid == 0x02) if (state->payload_algid == 0x02)
hytera_enhanced_alg_refresh(state); hytera_enhanced_alg_refresh(state);
@@ -475,6 +481,12 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
if (internalslot == 1 && vc2 == 6) if (internalslot == 1 && vc2 == 6)
{ {
if (state->hota_enc_check && state->forced_alg_id > 1)
{
state->dmr_soR = 0x40;
state->payload_algidR = state->forced_alg_id;
state->payload_keyidR = 0xFF;
}
//this needs to run prior to embedded link control //this needs to run prior to embedded link control
if (state->payload_algidR == 0x02) if (state->payload_algidR == 0x02)
hytera_enhanced_alg_refresh(state); hytera_enhanced_alg_refresh(state);
@@ -509,6 +521,13 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
csi72_ambe2_codeword_keystream(state, ambe_fr3); csi72_ambe2_codeword_keystream(state, ambe_fr3);
} }
if (state->vtx256_ekey != 0)
{
vtx256_ambe2_interleave_inversion(ambe_fr);
vtx256_ambe2_interleave_inversion(ambe_fr2);
vtx256_ambe2_interleave_inversion(ambe_fr3);
}
#ifdef PRINT_AMBE72 #ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr); ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2); ambe2_codeword_print_i(opts, ambe_fr2);
@@ -968,6 +987,13 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
csi72_ambe2_codeword_keystream(state, ambe_fr3); csi72_ambe2_codeword_keystream(state, ambe_fr3);
} }
if (state->vtx256_ekey != 0)
{
vtx256_ambe2_interleave_inversion(ambe_fr);
vtx256_ambe2_interleave_inversion(ambe_fr2);
vtx256_ambe2_interleave_inversion(ambe_fr3);
}
#ifdef PRINT_AMBE72 #ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr); ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2); ambe2_codeword_print_i(opts, ambe_fr2);

View File

@@ -940,14 +940,21 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
//MassReg //MassReg
if (a_type == 4) if (a_type == 4)
{ {
uint8_t mreg_res = (uint8_t)ConvertBitIntoBytes(&bpbits1[0], 5);
uint8_t reg_window = (uint8_t)ConvertBitIntoBytes(&bpbits1[5], 4); //7.2.19.5.1 Table 7.77 convert raw to seconds uint8_t reg_window = (uint8_t)ConvertBitIntoBytes(&bpbits1[5], 4); //7.2.19.5.1 Table 7.77 convert raw to seconds
uint8_t aloha_mask = (uint8_t)ConvertBitIntoBytes(&bpbits1[9], 5); uint8_t aloha_mask = (uint8_t)ConvertBitIntoBytes(&bpbits1[9], 5);
//ADDRNull or MS Individual Address //ADDRNull or MS Individual Address
uint8_t reg_address = (uint8_t)ConvertBitIntoBytes(&bpbits2[16], 8); //see clause 7.2.41 uint32_t reg_address = bparms2; //see clause 7.2.41
fprintf (stderr, "\n"); //raw value only on the reg window, see aforementioned table fprintf (stderr, "\n"); //raw value only on the reg window, see aforementioned table
fprintf (stderr, " Reg Window: %X; Aloha Mask: %02X; Target: %d; ", reg_window, aloha_mask, reg_address); fprintf (stderr, " Reg Window: %X; Aloha Mask: %02X; Target: %d; ", reg_window, aloha_mask, reg_address);
dmr_gateway_identifier(0,reg_address);
if (mreg_res != 0)
fprintf (stderr, "Res: %02X", mreg_res);
if (reg_address != 0)
dmr_gateway_identifier(0, reg_address);
} }
//debug //debug

View File

@@ -6,7 +6,7 @@
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c * Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
* *
* LWVMOBILE * 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) //TODO: Test USBD LIP Decoder with Real World Samples (if/when available)
@@ -51,14 +51,15 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
//PDU Bytes and Bits //PDU Bytes and Bits
uint8_t DMR_PDU[25]; 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, 0, sizeof (DMR_PDU));
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits)); memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
uint8_t R[3]; uint8_t hota_enc = 0;
uint8_t BPTCReservedBits = 0; uint8_t is_ras = 0;
uint8_t is_ras = 0; uint8_t bptc_res_bits[3];
uint8_t crc_original_validity = 0; memset (bptc_res_bits, 0, sizeof(bptc_res_bits));
uint8_t crc_original_validity = 0;
uint32_t crcmask = 0; uint32_t crcmask = 0;
uint8_t crclen = 0; uint8_t crclen = 0;
@@ -76,6 +77,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
switch(databurst){ switch(databurst){
case 0x00: //PI case 0x00: //PI
hota_enc = 1;
is_bptc = 1; is_bptc = 1;
crclen = 16; crclen = 16;
crcmask = 0x6969; //insert pi and 69 jokes here crcmask = 0x6969; //insert pi and 69 jokes here
@@ -83,6 +85,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
sprintf(state->fsubtype, " PI "); sprintf(state->fsubtype, " PI ");
break; break;
case 0x01: //VLC case 0x01: //VLC
hota_enc = 1;
is_bptc = 1; is_bptc = 1;
is_lc = 1; is_lc = 1;
crclen = 24; crclen = 24;
@@ -91,6 +94,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
sprintf(state->fsubtype, " VLC "); sprintf(state->fsubtype, " VLC ");
break; break;
case 0x02: //TLC case 0x02: //TLC
hota_enc = 1;
is_bptc = 1; is_bptc = 1;
is_lc = 1; is_lc = 1;
crcmask = 0x999999; crcmask = 0x999999;
@@ -99,6 +103,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
sprintf(state->fsubtype, " TLC "); sprintf(state->fsubtype, " TLC ");
break; break;
case 0x03: //CSBK case 0x03: //CSBK
hota_enc = 1;
is_bptc = 1; is_bptc = 1;
crclen = 16; crclen = 16;
crcmask = 0xA5A5; crcmask = 0xA5A5;
@@ -106,6 +111,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
sprintf(state->fsubtype, " CSBK"); sprintf(state->fsubtype, " CSBK");
break; break;
case 0x04: //MBC Header case 0x04: //MBC Header
hota_enc = 1;
is_bptc = 1; is_bptc = 1;
crclen = 16; crclen = 16;
crcmask = 0xAAAA; crcmask = 0xAAAA;
@@ -119,6 +125,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
sprintf(state->fsubtype, " MBCC"); sprintf(state->fsubtype, " MBCC");
break; break;
case 0x06: //Data Header case 0x06: //Data Header
hota_enc = 1;
is_bptc = 1; is_bptc = 1;
crclen = 16; crclen = 16;
crcmask = 0xCCCC; crcmask = 0xCCCC;
@@ -176,6 +183,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
} }
break; break;
case 0x0B: //Unified Single Block Data USBD case 0x0B: //Unified Single Block Data USBD
hota_enc = 1;
is_bptc = 1; is_bptc = 1;
crclen = 16; crclen = 16;
crcmask = 0x3333; crcmask = 0x3333;
@@ -185,6 +193,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
//special types (not real data 'sync' bursts) //special types (not real data 'sync' bursts)
case 0xEB: //Embedded Signalling case 0xEB: //Embedded Signalling
hota_enc = 1;
crclen = 5; crclen = 5;
is_emb = 1; is_emb = 1;
pdu_len = 9; pdu_len = 9;
@@ -246,28 +255,41 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
BPTCDeInterleaveDMRData(info, BPTCDeInteleavedData); BPTCDeInterleaveDMRData(info, BPTCDeInteleavedData);
/* Extract the BPTC 196,96 DMR data */ /* Extract the BPTC 196,96 DMR data */
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, R); IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, bptc_res_bits, &is_ras);
/* 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);
#ifdef DEBUG_RAS
//debug print //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 */ //TODO: Checkdown no irrecoverable errors prior to OTA Decryption?
k = 0;
for(i = 0; i < 12; i++) //Hytera OTA Encryption
if (state->hota_key_loaded && hota_enc)
hytera_ota_decrypt_bptc_96(state, BPTCDmrDataBit, databurst);
//Hytera Data Block Level Decryption
if (databurst == 0x7) //Rate 1_2 Data Only
{ {
BPTCDmrDataByte[i] = 0; uint8_t bytes[12];
for(j = 0; j < 8; j++) memset(bytes, 0, sizeof(bytes));
{ pack_bit_array_into_byte_array(BPTCDmrDataBit, bytes, 12);
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] << 1; hytera_bp_data_decrypt(state, bytes, 12);
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] | (BPTCDmrDataBit[k] & 0x01); hytera_ep_data_decrypt(opts, state, bytes, 12);
k++; unpack_byte_array_into_bit_array(bytes, BPTCDmrDataBit, 12);
}
} }
/* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */ /* Convert the 96 bits BPTC data into 12 bytes */
pack_bit_array_into_byte_array(BPTCDmrDataBit, BPTCDmrDataByte, 12);
//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 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; CRCExtracted = 0;
for(i = 0; i < crclen; i++) for(i = 0; i < crclen; i++)
{ {
@@ -279,7 +301,24 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
CRCExtracted = CRCExtracted ^ crcmask; CRCExtracted = CRCExtracted ^ crcmask;
/* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */ /* 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);
//Hytera OTA Encryption LC Context Creation for VLC Header
if (state->hota_key_loaded && CRCCorrect && databurst == 0x01)
hytera_create_lc_ctx(state, BPTCDmrDataBit);
}
//set CRC to correct on unconfirmed 1/2 data blocks (for reporting due to no CRC available on these) //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) else if (state->data_conf_data[slot] == 0 && databurst == 0x7)
@@ -315,20 +354,23 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
//run CCITT on other data forms //run CCITT on other data forms
else 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; if (CRCComputed == CRCExtracted) CRCCorrect = 1;
else CRCCorrect = 0; 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 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 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; crc_original_validity = CRCCorrect;
CRCCorrect = 1; CRCCorrect = 1;
@@ -344,17 +386,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
} }
/* Convert corrected x bytes into x*8 bits */ /* Convert corrected x bytes into x*8 bits */
for(i = 0, j = 0; i < pdu_len; i++, j+=8) unpack_byte_array_into_bit_array(BPTCDmrDataByte, BPTCDmrDataBit, pdu_len);
{
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;
}
//convert to DMR_PDU and DMR_PDU_bits //convert to DMR_PDU and DMR_PDU_bits
for (i = 0; i < pdu_len; i++) for (i = 0; i < pdu_len; i++)
@@ -398,8 +430,42 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
} /* End for(j = 0; j < 8; j++) */ } /* End for(j = 0; j < 8; j++) */
} /* End for(i = 0; i < 16; i++) */ } /* End for(i = 0; i < 16; i++) */
//Hytera OTA Encryption
// if (state->hota_key_loaded && state->hota_ctx_ok[slot] == 0)
// hytera_ota_search_emb_lc(state, BptcDataMatrix); //only run as a debug option
if (state->hota_key_loaded && state->hota_ctx_ok[slot] == 1)
hytera_ota_decrypt_emb_lc(state, BptcDataMatrix);
/* Extract the 72 LC bit (+ 5 CRC bit) of the matrix */ /* Extract the 72 LC bit (+ 5 CRC bit) of the matrix */
IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit); if (state->hota_key_loaded && state->hota_ctx_ok[slot] == 0)
{
//just fill with all ones, would be garbage from FEC anyways
memset(LC_DataBit, 1, sizeof(LC_DataBit));
IrrecoverableErrors = 1;
//TODO: columns instead of rows to match application of keystream
#ifdef DEBUG_HOTA
fprintf(stderr, "\n BPTC 128: ");
for (int i = 0; i < 8; i++)
fprintf (stderr, "%04X", (uint16_t)convert_bits_into_output(BptcDataMatrix[i], 16));
fprintf (stderr, "\n");
#endif
}
else if (state->hota_enc_check == 1)
{
//just fill with all ones, would be garbage from FEC anyways
memset(LC_DataBit, 1, sizeof(LC_DataBit));
IrrecoverableErrors = 1;
//TODO: columns instead of rows to match application of keystream
#ifdef DEBUG_HOTA
fprintf(stderr, "\n BPTC 128: ");
for (int i = 0; i < 8; i++)
fprintf (stderr, "%04X", (uint16_t)convert_bits_into_output(BptcDataMatrix[i], 16));
fprintf (stderr, "\n");
#endif
}
else IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit);
/* Reconstitute the 5 bit CRC */ /* Reconstitute the 5 bit CRC */
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&LC_DataBit[72], 5); CRCExtracted = (uint32_t)ConvertBitIntoBytes(&LC_DataBit[72], 5);
@@ -407,6 +473,16 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
/* Compute the 5 bit CRC */ /* Compute the 5 bit CRC */
CRCComputed = ComputeCrc5Bit(LC_DataBit); 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; if(CRCExtracted == CRCComputed) CRCCorrect = 1;
else CRCCorrect = 0; else CRCCorrect = 0;
@@ -440,6 +516,10 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
memset (TrellisReturn, 0, sizeof(TrellisReturn)); memset (TrellisReturn, 0, sizeof(TrellisReturn));
IrrecoverableErrors = viterbi_r34(tdibits, TrellisReturn); IrrecoverableErrors = viterbi_r34(tdibits, TrellisReturn);
//Hytera Data Block Level Decryption
hytera_bp_data_decrypt(state, TrellisReturn, 18);
hytera_ep_data_decrypt(opts, state, TrellisReturn, 18);
//NOTE: IrrecoverableErrors in this context are a tally of errors from trellis //NOTE: IrrecoverableErrors in this context are a tally of errors from trellis
//they may have been successfully corrected, the CRC will reveal as much //they may have been successfully corrected, the CRC will reveal as much
@@ -518,6 +598,10 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
bit_ptr = 100; byte_ptr = 12-pdu_start; bit_ptr = 100; byte_ptr = 12-pdu_start;
pack_bit_array_into_byte_array (info+bit_ptr, DMR_PDU+byte_ptr, 12); pack_bit_array_into_byte_array (info+bit_ptr, DMR_PDU+byte_ptr, 12);
//Hytera Data Block Level Decryption
hytera_bp_data_decrypt(state, DMR_PDU, 24);
hytera_ep_data_decrypt(opts, state, DMR_PDU, 24);
//set CRC to correct on unconfirmed 1 rate data blocks (for reporting due to no CRC available on these) //set CRC to correct on unconfirmed 1 rate data blocks (for reporting due to no CRC available on these)
if (state->data_conf_data[slot] == 0) CRCCorrect = 1; if (state->data_conf_data[slot] == 0) CRCCorrect = 1;
@@ -593,19 +677,8 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
else fprintf (stderr, "Reserved %d ", usbd_st); else fprintf (stderr, "Reserved %d ", usbd_st);
} }
//set the original CRCCorrect back to its original value if the RAS flag was tripped //set the original CRCCorrect back to its original value if the RAS flag was tripped (if no key value present)
if (is_ras == 1) CRCCorrect = crc_original_validity; if (is_ras == 1 && state->ras_effective_key == 0) 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);
// }
if (IrrecoverableErrors != 0 && databurst != 0x08 && databurst != 0x09) //&& databurst != 0x05 if (IrrecoverableErrors != 0 && databurst != 0x08 && databurst != 0x09) //&& databurst != 0x05
{ {
@@ -614,16 +687,25 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
//print whether or not the 'RAS Field' bits are set to indicate RAS enabled (to be verified) //print whether or not the 'RAS Field' bit is set to indicate RAS
if (is_ras == 1) //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, "%s", KRED);
fprintf (stderr, " -RAS "); fprintf (stderr, " {RAS ");
//the value of this field seems to always, or usually, be 4, or just R[2] bit is set if (state->ras_effective_key != 0 && CRCComputed != CRCExtracted)
if (opts->payload == 1) fprintf (stderr, "%X ", BPTCReservedBits); 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); 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 && databurst != 0x09)
state->dmr_is_ras = is_ras;
if (IrrecoverableErrors == 0 && CRCCorrect == 0 && is_ras == 0 && databurst != 0x09 && databurst != 0x05) if (IrrecoverableErrors == 0 && CRCCorrect == 0 && is_ras == 0 && databurst != 0x09 && databurst != 0x05)
{ {
fprintf (stderr, "%s", KRED); fprintf (stderr, "%s", KRED);
@@ -639,9 +721,24 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
fprintf (stderr, " DMR PDU Payload "); fprintf (stderr, " DMR PDU Payload ");
for (i = 0; i < pdu_len; i++) for (i = 0; i < pdu_len; i++)
{ {
#ifdef DEBUG_RAS
fprintf (stderr, "%02X", DMR_PDU[i]);
#else
fprintf (stderr, "[%02X]", DMR_PDU[i]); 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)
{
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 //debug print
// if (dbsn) fprintf (stderr, " SN %X", dbsn); // if (dbsn) fprintf (stderr, " SN %X", dbsn);
// fprintf (stderr, " CRC - EXT %X CMP %X", CRCExtracted, CRCComputed); // 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_alias = 0;
int is_gps = 0; int is_gps = 0;
UNUSED(capsite); UNUSED(capsite);
int is_ras = 0;
//XPT 'Things' //XPT 'Things'
int is_xpt = 0; int is_xpt = 0;
@@ -75,10 +76,66 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
//Kirisun //Kirisun
if (*IrrecoverableErrors == 0 && CRCCorrect == 1 && fid == 0x0A) //&& (so & 0x40) == 0x40 if (*IrrecoverableErrors == 0 && CRCCorrect == 1 && fid == 0x0A) //&& (so & 0x40) == 0x40
{ {
//disable late entry for DMRA, Flag 3 if encrypted, 0 if not //disable late entry for DMRA, Flag 3 if encrypted, 0 if not
if (so & 0x40) if (so & 0x40)
opts->dmr_le = 3; opts->dmr_le = 3;
else opts->dmr_le = 0; else opts->dmr_le = 0;
}
//Hytera OTA Check Enabled, Set SRC from VLC (TLC and ELC differ)
//NOTE: VLC will always be same for each radio if its a group call
//while this is not the true src, it should be unique
//TGT will also be random between calls in each VLC,
//so assign static value
if (state->hota_enc_check)
{
uint32_t t_target = 8686868;
if (type == 1)
{
if (slot == 0)
{
state->lastsrc = source;
state->lasttg = t_target;
}
else
{
state->lastsrcR = source;
state->lasttgR = t_target;
}
fprintf (stderr, "\n%s", KYEL);
fprintf (stderr, " SLOT %d ", slot+1);
fprintf(stderr, "TGT=%u SRC=%u ", t_target, source);
fprintf (stderr, "Encrypted Pseudo Call Information");
fprintf (stderr, "%s", KNRM);
return;
}
else if (type == 2)
{
fprintf (stderr, "\n%s", KRED);
fprintf (stderr, " SLOT %d", slot+1);
fprintf (stderr, " TLC Encrypted;");
fprintf (stderr, "%s", KNRM);
return;
}
else if (type == 3)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, " SLOT %d", slot+1);
fprintf (stderr, " EMB LC Encrypted;");
fprintf (stderr, "%s", KNRM);
return;
}
}
//HOTA CTX was not recovered, so dump that and return;
else if (state->hota_key_loaded && state->hota_ctx_ok[slot] == 0 && type == 3)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, " SLOT %d", slot+1);
fprintf (stderr, " EMB LC Missing CTX; Encrypted;");
fprintf (stderr, "%s", KNRM);
return;
} }
//read ahead a little to get this for the xpt flag //read ahead a little to get this for the xpt flag
@@ -117,6 +174,17 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
if (slot == 0) state->dmr_flco = flco; if (slot == 0) state->dmr_flco = flco;
else state->dmr_flcoR = 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 //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 //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) ) if ( fid == 0x10 && (flco == 0x14 || flco == 0x15 || flco == 0x16 || flco == 0x17 || flco == 0x18) )
@@ -126,7 +194,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) //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; is_alias = 1;
dmr_talker_alias_lc_header(opts, state, slot, lc_bits); dmr_talker_alias_lc_header(opts, state, slot, lc_bits);
@@ -140,7 +208,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
} }
//Embedded GPS //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; is_gps = 1;
dmr_embedded_gps(opts, state, lc_bits); dmr_embedded_gps(opts, state, lc_bits);
@@ -202,8 +270,29 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
goto END_FLCO; goto END_FLCO;
} }
//Tait Talker Alias
if (fid == 0x58 && flco == 0x08 && type == 3)
{
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " SLOT %d", state->currentslot+1);
fprintf (stderr, " Tait Talker Alias: ");
char tta_str[8]; memset(tta_str, 0, sizeof(tta_str));
for (int i = 0; i < 7; i++)
tta_str[i] = (uint8_t)ConvertBitIntoBytes(&lc_bits[16+(i*8)], 8);
fprintf (stderr, "%s", tta_str);
uint32_t source = 0;
if (slot == 0)
source = state->lastsrc;
else source = state->lastsrcR;
if (state->event_history_s[slot].Event_History_Items[0].source_id == source)
sprintf (state->event_history_s[slot].Event_History_Items[0].alias, "%s; ", tta_str);
sprintf (state->generic_talker_alias[slot], "Talker Alias: %s; ", tta_str);
goto END_FLCO;
}
//Unknown Tait Things //Unknown Tait Things
if (fid == 0x58) else if (fid == 0x58)
{ {
//NOTE: fid 0x58 (tait) had a single flco 0x06 emb observed, but without the other blocks (4,5,7) for an alias //NOTE: fid 0x58 (tait) had a single flco 0x06 emb observed, but without the other blocks (4,5,7) for an alias
//will need to observe this one, or just remove it from the list, going to isolate tait lc for now //will need to observe this one, or just remove it from the list, going to isolate tait lc for now
@@ -453,6 +542,12 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
sprintf (state->generic_talker_alias[0], "%s", ""); sprintf (state->generic_talker_alias[0], "%s", "");
// sprintf (state->event_history_s[0].Event_History_Items[0].alias, "%s", "BUMBLEBEETUNA"); // sprintf (state->event_history_s[0].Event_History_Items[0].alias, "%s", "BUMBLEBEETUNA");
memset (state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0])); memset (state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
//Hytera OTA Encryption
memset(state->hota_lc_ctx[0], 0, sizeof(state->hota_lc_ctx[0]));
state->hota_ctx_ok[0] = 0;
state->hota_sbrc_idx1[0] = -1;
state->hota_sbrc_idx2[0] = -1;
} }
if (state->currentslot == 1) if (state->currentslot == 1)
{ {
@@ -473,6 +568,12 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
sprintf (state->generic_talker_alias[1], "%s", ""); sprintf (state->generic_talker_alias[1], "%s", "");
// sprintf (state->event_history_s[1].Event_History_Items[0].alias, "%s", "BUMBLEBEETUNA"); // sprintf (state->event_history_s[1].Event_History_Items[0].alias, "%s", "BUMBLEBEETUNA");
memset (state->dmr_pdu_sf[1], 0, sizeof(state->dmr_pdu_sf[1])); memset (state->dmr_pdu_sf[1], 0, sizeof(state->dmr_pdu_sf[1]));
//Hytera OTA Encryption
memset(state->hota_lc_ctx[1], 0, sizeof(state->hota_lc_ctx[1]));
state->hota_ctx_ok[1] = 0;
state->hota_sbrc_idx1[1] = -1;
state->hota_sbrc_idx2[1] = -1;
} }
} }
@@ -500,15 +601,15 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
if (opts->payload == 1 && is_xpt == 1 && flco == 0x3) fprintf(stderr, "HASH=%d ", tg_hash); if (opts->payload == 1 && is_xpt == 1 && flco == 0x3) fprintf(stderr, "HASH=%d ", tg_hash);
if (opts->payload == 1) fprintf(stderr, "FLCO=0x%02X FID=0x%02X SVC=0x%02X ", flco, fid, so); if (opts->payload == 1) fprintf(stderr, "FLCO=0x%02X FID=0x%02X SVC=0x%02X ", flco, fid, so);
//0x04 and 0x05 on a TLC seem to indicate a Cap + Private Call Terminator (perhaps one for each MS) //0x04 and 0x05 on a TLC seem to indicate a Cap + Private Call Terminator <--need to find this again and re-evaluate
//0x07 on a VLC seems to indicate a Cap+ Private Call Header //0x04 on a VLC seems to indicate a Cap+ Group Call Header <--single bit flip from normal group
//0x23 on the Embedded Voice Burst Sync seems to indicate a Cap+ or Cap+ TXI Private Call in progress //0x07 on a VLC seems to indicate a Cap+ Private Call Header <--single bit flip from normal private
//0x20 on the Embedded Voice Burst Sync seems to indicate a Moto (non-specific) Group Call in progress //0x20 on the Embedded Voice Burst Sync seems to indicate a Moto w/ FID 0x10 Group Call in progress (RAS)
//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 Moto w/ FID 0x10 Private Call in progress (RAS)
if (fid == 0x68) sprintf (state->call_string[slot], " Hytera "); if (fid == 0x68) sprintf (state->call_string[slot], " Hytera ");
else if (flco == 0x4 || flco == 0x5 || flco == 0x7 || flco == 0x23) //Cap+ Things else if (flco == 0x4 || flco == 0x5 || flco == 0x7) //Cap+ Things (double check 0x05)
{ {
// sprintf (state->call_string[slot], " Cap+"); // sprintf (state->call_string[slot], " Cap+");
sprintf (state->call_string[slot], "%s",""); sprintf (state->call_string[slot], "%s","");
@@ -528,13 +629,13 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
state->gi[slot] = 1; state->gi[slot] = 1;
} }
} }
else if (flco == 0x3) //UU_V_Ch_Usr else if (flco == 0x3 || flco == 0x23) //UU_V_Ch_Usr
{ {
sprintf (state->call_string[slot], " Private "); sprintf (state->call_string[slot], " Private ");
fprintf (stderr, "Private "); fprintf (stderr, "Private ");
state->gi[slot] = 1; state->gi[slot] = 1;
} }
else //Grp_V_Ch_Usr -- still valid on hytera VLC else //Grp_V_Ch_Usr
{ {
sprintf (state->call_string[slot], " Group "); sprintf (state->call_string[slot], " Group ");
fprintf (stderr, "Group "); fprintf (stderr, "Group ");
@@ -1191,7 +1292,19 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
} }
else if (slco == 0xF) else if (slco == 0x5)
{
uint32_t unk = (uint32_t)ConvertBitIntoBytes(&slco_bits[4], 24);
fprintf (stderr, " SLCO Caltta: %06X;", unk);
}
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); fprintf (stderr, " SLCO Capacity Plus Site: %d - Rest LSN: %d - RS: %02X", capsite, restchannel, cap_reserved);

View File

@@ -8,6 +8,9 @@
#include "dsd.h" #include "dsd.h"
static unsigned long long int next_iv_32[2] = {0,0};
static unsigned long long int next_iv_64[2] = {0,0};
//gather ambe_fr mi fragments for processing //gather ambe_fr mi fragments for processing
void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, uint8_t ambe_fr[4][24], uint8_t ambe_fr2[4][24], uint8_t ambe_fr3[4][24]) void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, uint8_t ambe_fr[4][24], uint8_t ambe_fr2[4][24], uint8_t ambe_fr3[4][24])
{ {
@@ -86,7 +89,96 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
if (g[0] && g[1] && g[2]) if (g[0] && g[1] && g[2])
{ {
if (slot == 0 && state->payload_algid != 0)
if (mi_crc_ok == 1 && state->hota_enc_check == 1)
{
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot %d DMRA EP Potential;", slot+1);
unsigned long long int temp_iv = mi_final;
//Reverse Potential IV for both 32-bit and 128-bit to show the IV
//that may have been carried by the PI header or last superframe
uint8_t iv_a[16]; memset(iv_a, 0, sizeof(iv_a));
uint8_t iv_d[16]; memset(iv_d, 0, sizeof(iv_d));
for (int i = 0; i < 4; i++)
{
iv_a[i] = (mi_final >> (24-(i*8))) & 0xFF;
iv_d[i] = (mi_final >> (24-(i*8))) & 0xFF;
}
fprintf (stderr, "\n ");
reverse_lfsr_32a_to_len(iv_a, 32);
fprintf (stderr, " ");
reverse_lfsr_32d_to_len(iv_d, 32);
fprintf (stderr, "\n");
if (next_iv_64[slot] != 0 && mi_final == next_iv_64[slot])
{
//probably safe to assume AES-256 if its DMRA specific
fprintf (stderr, " DMRA AES 128-bit IV detected;");
state->forced_alg_id = 0x25;
if (slot == 0 && state->payload_mi == 0)
{
fprintf (stderr, "\n");
state->payload_mi = mi_final;
state->dmr_so = 0x40;
LFSR128d(state);
}
else if (slot == 1 && state->payload_miR == 0)
{
fprintf (stderr, "\n");
state->payload_miR = mi_final;
state->dmr_soR = 0x40;
LFSR128d(state);
}
}
if (next_iv_32[slot] != 0 && mi_final == next_iv_32[slot])
{
//only DMRA RC4 uses this tap configuration
fprintf (stderr, " DMRA RC4 32-bit IV detected;");
state->forced_alg_id = 0x21;
if (slot == 0 && state->payload_mi == 0)
{
fprintf (stderr, "\n");
state->payload_mi = mi_final;
state->dmr_so = 0x40;
LFSR(state);
}
else if (slot == 1 && state->payload_miR == 0)
{
fprintf (stderr, "\n");
state->payload_miR = mi_final;
state->dmr_soR = 0x40;
LFSR(state);
}
}
//AES taps to next embedded value
for(int cnt = 0; cnt < 32; cnt++)
{
unsigned long long int bit = ( (temp_iv >> 31) ^ (temp_iv >> 21) ^ (temp_iv >> 1) ^ (temp_iv >> 0) ) & 0x1;
temp_iv = (temp_iv << 1) | bit;
}
next_iv_64[slot] = temp_iv & 0xFFFFFFFF;
//RC4 taps to next embedded value
temp_iv = mi_final;
for(int cnt = 0; cnt < 32; cnt++)
{
unsigned long long int bit = ( (temp_iv >> 31) ^ (temp_iv >> 3) ^ (temp_iv >> 1) ) & 0x1;
temp_iv = (temp_iv << 1) | bit;
}
next_iv_32[slot] = temp_iv & 0xFFFFFFFF;
fprintf (stderr, "\n");
fprintf (stderr, "%s", KNRM);
}
if (slot == 0 && state->payload_algid != 0 && state->payload_algid != 0x26)
{ {
if (state->payload_mi != mi_final) if (state->payload_mi != mi_final)
{ {
@@ -116,7 +208,7 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
} }
} }
if (slot == 1 && state->payload_algidR != 0) if (slot == 1 && state->payload_algidR != 0 && state->payload_algidR != 0x26)
{ {
if (state->payload_miR != mi_final) if (state->payload_miR != mi_final)
{ {
@@ -219,20 +311,37 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
fprintf (stderr, "%s\n", KNRM); fprintf (stderr, "%s\n", KNRM);
} }
if (state->payload_algid == 0x35 || state->payload_algid == 0x36 || state->payload_algid == 0x37) if ((state->payload_algid & 0xF0) == 0x30) //any kirisun 0x3X alg
{ {
uint8_t alg = state->payload_algid;
state->DMRvcL = 0; state->DMRvcL = 0;
state->payload_mi = kirisun_lfsr(state->payload_mi); state->payload_mi = kirisun_lfsr(state->payload_mi);
fprintf (stderr, "%s", KYEL); fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 1"); fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algid, state->payload_keyid); fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", alg, state->payload_keyid);
fprintf (stderr, " MI(32): %08llX;", state->payload_mi); fprintf (stderr, " MI(32): %08llX;", state->payload_mi);
fprintf (stderr, " Kirisun"); fprintf (stderr, " Kirisun ");
if (state->payload_algid == 0x36) if (alg == 0x31)
fprintf (stderr, " Advanced;"); fprintf (stderr, "Reinforced ");
else if (state->payload_algid == 0x37) else if (alg == 0x32)
fprintf (stderr, " Universal;"); fprintf (stderr, "Low ");
else fprintf (stderr, " Encryption;"); else if (alg == 0x33)
fprintf (stderr, "Middle ");
else if (alg == 0x34)
fprintf (stderr, "High ");
else if (alg == 0x35)
fprintf (stderr, "Enhanced ");
else if (alg == 0x36)
fprintf (stderr, "Advanced ");
else if (alg == 0x37)
fprintf (stderr, "Universal ");
else if (alg == 0x38)
fprintf (stderr, "Strengthened ");
else if (alg == 0x39)
fprintf (stderr, "Top ");
else if (alg == 0x3A)
fprintf (stderr, "Ultra ");
fprintf (stderr, "Encryption;");
fprintf (stderr, "%s\n", KNRM); fprintf (stderr, "%s\n", KNRM);
} }
@@ -266,20 +375,37 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
fprintf (stderr, "%s\n", KNRM); fprintf (stderr, "%s\n", KNRM);
} }
if (state->payload_algidR == 0x35 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37) if ((state->payload_algidR & 0xF0) == 0x30) //any 0x3X alg
{ {
uint8_t alg = state->payload_algidR;
state->DMRvcR = 0; state->DMRvcR = 0;
state->payload_miR = kirisun_lfsr(state->payload_miR); state->payload_miR = kirisun_lfsr(state->payload_miR);
fprintf (stderr, "%s", KYEL); fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 2"); fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algidR, state->payload_keyidR); fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", alg, state->payload_keyidR);
fprintf (stderr, " MI(32): %08llX;", state->payload_miR); fprintf (stderr, " MI(32): %08llX;", state->payload_miR);
fprintf (stderr, " Kirisun"); fprintf (stderr, " Kirisun ");
if (state->payload_algidR == 0x36) if (alg == 0x31)
fprintf (stderr, " Advanced;"); fprintf (stderr, "Reinforced ");
else if (state->payload_algidR == 0x37) else if (alg == 0x32)
fprintf (stderr, " Universal;"); fprintf (stderr, "Low ");
else fprintf (stderr, " Encryption;"); else if (alg == 0x33)
fprintf (stderr, "Middle ");
else if (alg == 0x34)
fprintf (stderr, "High ");
else if (alg == 0x35)
fprintf (stderr, "Enhanced ");
else if (alg == 0x36)
fprintf (stderr, "Advanced ");
else if (alg == 0x37)
fprintf (stderr, "Universal ");
else if (alg == 0x38)
fprintf (stderr, "Strengthened ");
else if (alg == 0x39)
fprintf (stderr, "Top ");
else if (alg == 0x3A)
fprintf (stderr, "Ultra ");
fprintf (stderr, "Encryption;");
fprintf (stderr, "%s\n", KNRM); fprintf (stderr, "%s\n", KNRM);
} }
@@ -331,6 +457,13 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
// 1 - The embedded signalling carries RC information associated to the other logical channel // 1 - The embedded signalling carries RC information associated to the other logical channel
for(i = 0; i < 32; i++) sbrc_interleaved[i] = state->dmr_embedded_signalling[slot][5][i + 8]; for(i = 0; i < 32; i++) sbrc_interleaved[i] = state->dmr_embedded_signalling[slot][5][i + 8];
//decrypt for Hytera OTA Encryption w/ loaded key
if (state->hota_key_loaded && state->hota_sbrc_idx1[state->currentslot] == -1)
hytera_ota_search_sb_rc(state, sbrc_interleaved, power);
if (state->hota_key_loaded)
hytera_ota_decrypt_sb_rc(state, sbrc_interleaved);
//power == 0 should be single burst //power == 0 should be single burst
if (power == 0) irr_err = BPTC_16x2_Extract_Data(sbrc_interleaved, sbrc_return, 0); if (power == 0) irr_err = BPTC_16x2_Extract_Data(sbrc_interleaved, sbrc_return, 0);
//power == 1 should be reverse channel -- still need to check the interleave inside of BPTC //power == 1 should be reverse channel -- still need to check the interleave inside of BPTC
@@ -338,6 +471,36 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
//bad emb burst, never set a valid power indicator value (probably 9) //bad emb burst, never set a valid power indicator value (probably 9)
if (power > 1) goto SBRC_END; if (power > 1) goto SBRC_END;
//Hytera OTA
if (irr_err && state->hota_key_loaded && state->hota_sbrc_idx2[state->currentslot] == -1)
{
state->hota_sbrc_idx1[state->currentslot] = -1;
state->hota_sbrc_idx2[state->currentslot] = -1;
fprintf (stderr, "%s \n", KRED);
fprintf (stderr, " SLOT %d ", slot+1);
fprintf (stderr, "SB_RC Missing IDX; Encrypted;");
fprintf (stderr, "%s\n", KNRM);
goto SBRC_END;
}
else if (irr_err && state->hota_key_loaded && state->hota_sbrc_idx2[state->currentslot] != -1)
{
state->hota_sbrc_idx1[state->currentslot] = -1;
state->hota_sbrc_idx2[state->currentslot] = -1;
fprintf (stderr, "%s \n", KRED);
fprintf (stderr, " SLOT %d ", slot+1);
fprintf (stderr, "SB_RC Incorrect IDX or FEC Errors; Encrypted;");
fprintf (stderr, "%s\n", KNRM);
goto SBRC_END;
}
else if (state->hota_enc_check)
{
fprintf (stderr, "%s \n", KRED);
fprintf (stderr, " SLOT %d ", slot+1);
fprintf (stderr, "SB_RC Encrypted;");
fprintf (stderr, "%s\n", KNRM);
goto SBRC_END;
}
//arrange the return for proper order for the crc3 check //arrange the return for proper order for the crc3 check
for (i = 0; i < 8; i++) sbrc_retcrc[i] = sbrc_return[i+3]; for (i = 0; i < 8; i++) sbrc_retcrc[i] = sbrc_return[i+3];
@@ -407,7 +570,7 @@ 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 //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?) //so we need to exclude op/alg 0 and 3 from the check (does algID 0x03/0x23 even exist?)
//Kirisun Placeholder //Kirisun
if (opts->dmr_le == 3) if (opts->dmr_le == 3)
{ {
if (irr_err != 0) if (irr_err != 0)
@@ -422,23 +585,38 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
fprintf (stderr, "%s SLOT %d SB/RC (FEC ERR) E:%d; I:%08X D:%03X; %s ", KRED, slot+1, irr_err, sbrcpl, sbrc_hex, KNRM); fprintf (stderr, "%s SLOT %d SB/RC (FEC ERR) E:%d; I:%08X D:%03X; %s ", KRED, slot+1, irr_err, sbrcpl, sbrc_hex, KNRM);
if (opts->payload == 1) fprintf (stderr, "\n"); if (opts->payload == 1) fprintf (stderr, "\n");
} }
else if (irr_err == 0 && sbrc_hex != 0) //first batch had sbrc_hex 0x5F1, second batch had 0x5E1 instead, what changed? else if (irr_err == 0 && sbrc_hex != 0) //older firmware had sbrc_hex 0x5F1, and 0x5E1 instead
{ {
fprintf (stderr, "\n"); fprintf (stderr, "\n");
fprintf (stderr, "%s", KCYN); fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot %d", state->currentslot+1); fprintf (stderr, " Slot %d", state->currentslot+1);
fprintf (stderr, " DMR LE SB Kirisun Encryption Identifier;"); fprintf (stderr, " DMR LE SB Kirisun");
//Newer Samples and/or updated firmware seem to do this with actual key values
uint8_t alg = sbrc_hex >> 7;
if (alg != 0xB)
{
fprintf (stderr, " ALG: %02X;", alg+0x30);
uint8_t kid = (sbrc_hex >> 3) & 0xF;
fprintf (stderr, " KID: %02X;", kid);
uint8_t unk = sbrc_hex & 0x7;
fprintf (stderr, " LOW3: %X;", unk);
if (state->currentslot == 0)
{
if (state->payload_algid == 0 && state->dmr_so & 0x40)
state->payload_algid = alg+0x30;
}
else
{
if (state->payload_algidR == 0 && state->dmr_soR & 0x40)
state->payload_algidR = alg+0x30;
}
}
fprintf (stderr, "%s ", KNRM); fprintf (stderr, "%s ", KNRM);
if (state->currentslot == 0)
{
if (state->payload_algid == 0 && state->dmr_so & 0x40)
state->payload_algid = 0x35; //placeholder value
}
else
{
if (state->payload_algidR == 0 && state->dmr_soR & 0x40)
state->payload_algidR = 0x35; //placeholder value
}
} }
} }
@@ -528,6 +706,7 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
if (alg == 2) fprintf (stderr, " DES;"); if (alg == 2) fprintf (stderr, " DES;");
if (alg == 4) fprintf (stderr, " AES128;"); if (alg == 4) fprintf (stderr, " AES128;");
if (alg == 5) fprintf (stderr, " AES256;"); if (alg == 5) fprintf (stderr, " AES256;");
if (alg == 6) fprintf (stderr, " Caltta BP");
fprintf (stderr, "%s ", KNRM); fprintf (stderr, "%s ", KNRM);
if (opts->payload == 1) fprintf (stderr, "\n"); if (opts->payload == 1) fprintf (stderr, "\n");
if (state->payload_keyid != key) if (state->payload_keyid != key)
@@ -550,6 +729,11 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
fprintf (stderr, "%s", KCYN); fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot 2"); fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR LE SB ALG ID: %02X; KEY ID: %02X;", alg + 0x20, key); fprintf (stderr, " DMR LE SB ALG ID: %02X; KEY ID: %02X;", alg + 0x20, key);
if (alg == 1) fprintf (stderr, " RC4;");
if (alg == 2) fprintf (stderr, " DES;");
if (alg == 4) fprintf (stderr, " AES128;");
if (alg == 5) fprintf (stderr, " AES256;");
if (alg == 6) fprintf (stderr, " Caltta BP");
fprintf (stderr, "%s ", KNRM); fprintf (stderr, "%s ", KNRM);
if (opts->payload == 1) fprintf (stderr, "\n"); if (opts->payload == 1) fprintf (stderr, "\n");
if (state->payload_keyidR != key) if (state->payload_keyidR != key)

View File

@@ -167,16 +167,6 @@ 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 + 0] = syncdata[i];
for(i = 0; i < 8; i++) emb_pdu[i + 8] = syncdata[i + 40]; for(i = 0; i < 8; i++) emb_pdu[i + 8] = syncdata[i + 40];
@@ -270,6 +260,13 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
csi72_ambe2_codeword_keystream(state, ambe_fr3); csi72_ambe2_codeword_keystream(state, ambe_fr3);
} }
if (state->vtx256_ekey != 0)
{
vtx256_ambe2_interleave_inversion(ambe_fr);
vtx256_ambe2_interleave_inversion(ambe_fr2);
vtx256_ambe2_interleave_inversion(ambe_fr3);
}
#ifdef PRINT_AMBE72 #ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr); ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2); ambe2_codeword_print_i(opts, ambe_fr2);
@@ -588,6 +585,13 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
csi72_ambe2_codeword_keystream(state, ambe_fr3); csi72_ambe2_codeword_keystream(state, ambe_fr3);
} }
if (state->vtx256_ekey != 0)
{
vtx256_ambe2_interleave_inversion(ambe_fr);
vtx256_ambe2_interleave_inversion(ambe_fr2);
vtx256_ambe2_interleave_inversion(ambe_fr3);
}
#ifdef PRINT_AMBE72 #ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr); ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2); ambe2_codeword_print_i(opts, ambe_fr2);

View File

@@ -267,8 +267,13 @@ void dmr_udp_comp_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t
slot = 1; slot = 1;
char comp_string[500]; memset (comp_string, 0, sizeof(comp_string)); char comp_string[500]; memset (comp_string, 0, sizeof(comp_string));
sprintf (comp_string, "IPC: %d; OP: %d; SRC: %d:%d (%s):(%s); DST: %d:%d (%s):(%s); ", ipid, opcode, said, spid, addrstring[0], portstring[0], daid, dpid, addrstring[1], portstring[1]);
watchdog_event_datacall (opts, state, said, daid, comp_string, slot); //use data header source and target instead of idx values
uint32_t source = state->dmr_lrrp_source[slot];
uint32_t target = state->dmr_lrrp_target[slot];
sprintf (comp_string, "IPC Call; SRC: %d; TGT: %d; %s", source, target, portstring);
// sprintf (comp_string, "IPC: %d; OP: %d; SRC: %d:%d (%s):(%s); DST: %d:%d (%s):(%s); ", ipid, opcode, said, spid, addrstring[0], portstring[0], daid, dpid, addrstring[1], portstring[1]);
watchdog_event_datacall (opts, state, source, target, comp_string, slot);
} }
@@ -486,29 +491,24 @@ void decode_ip_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t *
else if (port1 == 5007 && port2 == 5007) else if (port1 == 5007 && port2 == 5007)
{ {
//get pad amount to see where to stop parsing UTF16 text
int pad = state->data_block_poc[slot];
//sanity check
if (len > (50+pad))
len = len - pad - 50;
fprintf (stderr, "VTX STD TMS; "); fprintf (stderr, "VTX STD TMS; ");
//+28, get 9 bytes for unknown "header" information int tmslen = input[28];
//unknown elements 0E0002000000000000, could include a len value or type (utf-16, utf-8, is07?)
//the sample has a len of 8 utf-16 chars, only way to get that is on the 2 if not byte aligned (1000) //+29, get 8 bytes for unknown "header" information
//unknown elements 0002000000000000, could include type (utf-16, utf-8, is07?)
//the sample has a len of 14 utf-16 chars, corresponding to the found 0x0E value
if (opts->payload == 1) if (opts->payload == 1)
{ {
// fprintf (stderr, "Len: %03d; ", len); fprintf (stderr, "Len: %03d; ", tmslen);
fprintf (stderr, "??: "); fprintf (stderr, "??: ");
for (int i = 0; i < 9; i++) for (int i = 0; i < 8; i++)
fprintf (stderr, "%02X", input[i+28]); fprintf (stderr, "%02X", input[i+29]);
fprintf (stderr, "; Text: "); fprintf (stderr, "; Text: ");
} }
sprintf (state->dmr_lrrp_gps[slot], "VTX TMS SRC: %d; DST: %d; ", src24, dst24); sprintf (state->dmr_lrrp_gps[slot], "VTX TMS SRC: %d; DST: %d; ", src24, dst24);
utf16_to_text(state, 1, len, input+49); utf16_to_text(state, 1, tmslen*2, input+37);
} }
else if (port1 == 5017 && port2 == 5017) else if (port1 == 5017 && port2 == 5017)
{ {

View File

@@ -27,51 +27,68 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
if (MFID == 0x0A && CRCCorrect == 1) //Kirisun 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 //NOTE: Some Kirisun Radios seem to use byte 2 as the SVC_OPT, others seem to use it like a KID value
// 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 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 //NOTE: Original samples had this as 0x40 no matter which "key" was selected, new samples from different radio show a KID value
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 on older FW
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); 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 ALG and TGT value here to produce a key id via hashing it //TODO: Use ALG and TGT value here to produce a key id via hashing it
uint8_t hash = alg * target % 256; //more complex hash later (honestly, probably not) uint8_t hash = alg * target % 256; //more complex hash later (honestly, probably not)
//Only set real KID if the byte value isn't 0x40
if (kid == 0x40)
kid = hash;
//MI only appears to be 32-bit //MI only appears to be 32-bit
uint32_t mi = ((uint32_t)PI_BYTE[3] << 24) | ((uint32_t)PI_BYTE[4] << 16) | uint32_t mi = ((uint32_t)PI_BYTE[3] << 24) | ((uint32_t)PI_BYTE[4] << 16) |
((uint32_t)PI_BYTE[5] << 8) | ((uint32_t)PI_BYTE[6] << 0); ((uint32_t)PI_BYTE[5] << 8) | ((uint32_t)PI_BYTE[6] << 0);
if (state->currentslot == 0) if (state->currentslot == 0)
{ {
state->dmr_so = so; state->dmr_so = 0x40;
state->payload_algid = alg; state->payload_algid = alg;
state->payload_keyid = hash; state->payload_keyid = kid;
state->payload_mi = mi; state->payload_mi = mi;
} }
else else
{ {
state->dmr_soR = so; state->dmr_soR = 0x40;
state->payload_algidR = alg; state->payload_algidR = alg;
state->payload_keyidR = hash; state->payload_keyidR = kid;
state->payload_miR = mi; state->payload_miR = mi;
} }
fprintf (stderr, "%s ", KYEL); fprintf (stderr, "%s ", KYEL);
fprintf (stderr, "\n Slot %d", state->currentslot+1); fprintf (stderr, "\n Slot %d", state->currentslot+1);
fprintf (stderr, " DMR PI H- ALG ID: %02X; KEY ID: %02X; MI(32): %08X;", alg, hash, mi); fprintf (stderr, " DMR PI H- ALG ID: %02X; TGT: %d; KEY ID: %02X; MI(32): %08X;", alg, target, kid, mi);
fprintf (stderr, " Kirisun "); fprintf (stderr, " Kirisun ");
if (alg == 0x36) if (alg == 0x31)
fprintf (stderr, "Advanced;"); fprintf (stderr, "Reinforced ");
else if (alg == 0x32)
fprintf (stderr, "Low ");
else if (alg == 0x33)
fprintf (stderr, "Middle ");
else if (alg == 0x34)
fprintf (stderr, "High ");
else if (alg == 0x35)
fprintf (stderr, "Enhanced ");
else if (alg == 0x36)
fprintf (stderr, "Advanced ");
else if (alg == 0x37) else if (alg == 0x37)
fprintf (stderr, "Universal;"); fprintf (stderr, "Universal ");
else fprintf (stderr, "Encryption;"); else if (alg == 0x38)
fprintf (stderr, "Strengthened ");
else if (alg == 0x39)
fprintf (stderr, "Top ");
else if (alg == 0x3A)
fprintf (stderr, "Ultra ");
fprintf (stderr, "Encryption;");
fprintf (stderr, "%s", KNRM); 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 //flag late entry SB for Kirisun
//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,
//reverse engineered LFSR for Kirisun lines up with the late entry for 32-bit MI values.
opts->dmr_le = 3; opts->dmr_le = 3;
} }
@@ -146,7 +163,7 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
state->payload_algid = PI_BYTE[0]; state->payload_algid = PI_BYTE[0];
state->payload_keyid = PI_BYTE[2]; state->payload_keyid = PI_BYTE[2];
state->payload_mi = ((unsigned long long int)PI_BYTE[3] << 24ULL) | ((unsigned long long int)PI_BYTE[4] << 16ULL) | ((unsigned long long int)PI_BYTE[5] << 8ULL) | ((unsigned long long int)PI_BYTE[6] << 0ULL); state->payload_mi = ((unsigned long long int)PI_BYTE[3] << 24ULL) | ((unsigned long long int)PI_BYTE[4] << 16ULL) | ((unsigned long long int)PI_BYTE[5] << 8ULL) | ((unsigned long long int)PI_BYTE[6] << 0ULL);
if (state->payload_algid < 0x26) if (state->payload_algid < 0x28)
{ {
fprintf (stderr, "%s ", KYEL); fprintf (stderr, "%s ", KYEL);
fprintf (stderr, "\n Slot 1"); fprintf (stderr, "\n Slot 1");
@@ -182,6 +199,13 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
state->payload_algid = 0x25; state->payload_algid = 0x25;
} }
else if ((state->payload_algid & 0x07) == 0x06)
{
fprintf (stderr, " Caltta BP;");
state->payload_algid = 0x26;
state->payload_mi = 0;
}
fprintf (stderr, "%s ", KNRM); fprintf (stderr, "%s ", KNRM);
//expand the 32-bit MI to a 64-bit DES IV //expand the 32-bit MI to a 64-bit DES IV
@@ -199,7 +223,7 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
} }
} }
if (state->payload_algid >= 0x26) if (state->payload_algid >= 0x28)
{ {
state->payload_algid = 0; state->payload_algid = 0;
state->payload_keyid = 0; state->payload_keyid = 0;
@@ -249,6 +273,12 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
state->payload_algidR = 0x25; state->payload_algidR = 0x25;
} }
else if ((state->payload_algidR & 0x07) == 0x06)
{
fprintf (stderr, " Caltta BP;");
state->payload_algidR = 0x26;
state->payload_miR = 0;
}
fprintf (stderr, "%s ", KNRM); fprintf (stderr, "%s ", KNRM);
@@ -267,7 +297,7 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
} }
} }
if (state->payload_algidR >= 0x26) if (state->payload_algidR >= 0x28)
{ {
state->payload_algidR = 0; state->payload_algidR = 0;
state->payload_keyidR = 0; state->payload_keyidR = 0;
@@ -374,6 +404,33 @@ void LFSR64(dsd_state * state)
} }
} }
//if supplied IV is only 32-bit in value, then expand it to a 128-bit IV using these LFSR taps (packed array)
void lfsr_32_to_128(uint8_t * iv)
{
uint64_t lfsr = 0, bit = 0;
lfsr = ((uint64_t)iv[0] << 24ULL) + ((uint64_t)iv[1] << 16ULL) + ((uint64_t)iv[2] << 8ULL) + ((uint64_t)iv[3] << 0ULL);
uint8_t cnt = 0, x = 32;
for(cnt = 0;cnt < 96; cnt++)
{
//32,22,2,1 (per Xilinx XAPP 052) Table 3: Taps for Maximum-Length LFSR Counters
bit = ( (lfsr >> 31) ^ (lfsr >> 21) ^ (lfsr >> 1) ^ (lfsr >> 0) ) & 0x1;
lfsr = (lfsr << 1) | bit;
//continue packing iv
iv[x/8] = (iv[x/8] << 1) + bit;
x++;
}
fprintf (stderr, "\n IV(128): ");
for (x = 0; x < 16; x++)
fprintf (stderr, "%02X", iv[x]);
fprintf (stderr, "\n");
}
//Expand a 32-bit MI into a 128-bit IV for AES //Expand a 32-bit MI into a 128-bit IV for AES
void LFSR128d(dsd_state * state) void LFSR128d(dsd_state * state)

View File

@@ -438,3 +438,43 @@ uint32_t ComputeCrc32Bit(uint8_t * DMRData, uint32_t NbData)
/* Return the CRC */ /* Return the CRC */
return CRC; return CRC;
} /* End ComputeCrc32Bit() */ } /* End ComputeCrc32Bit() */
uint32_t ComputeCrc32BitV(uint8_t * DMRData, uint32_t NbData)
{
uint32_t i;
uint32_t CRC = 0x00000000; /* Initialization value = 0x00000000 */
/* Polynomial x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
* Normal = 0x04C11DB7
* Reversed = 0xEDB88320
* Reciprocal = 0xDB710641
* Reversed reciprocal = 0x82608EDB */
uint32_t Polynome = 0x04C11DB7;
for(i = 0; i < NbData; i++)
{
if(((CRC >> 31) & 1) ^ (DMRData[i] & 1))
{
CRC = (CRC << 1) ^ Polynome;
}
else
{
CRC <<= 1;
}
}
//Alternate Return Configuration for Vertex
//Encrypted PDUs that have already been reordered once before
//CRC32 CMP: 1622377A EXT: 377A1622
uint32_t a, b, c, d;
a = b = c = d = 0;
a = ((CRC >> 0) & 0xFF) << 16;
b = ((CRC >> 8) & 0xFF) << 24;
c = ((CRC >> 16) & 0xFF) << 0;
d = ((CRC >> 24) & 0xFF) << 8;
CRC = a + b + c + d;
/* Return the CRC */
return CRC;
} /* End ComputeCrc32BitV() */

View File

@@ -92,6 +92,10 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
{ {
encL = 0; encL = 0;
} }
else if (state->vtx_key_loaded == 1)
{
encL = 0;
}
} }
else if (state->payload_algid == 0x02 || state->payload_algid == 0x21 || state->payload_algid == 0x22) else if (state->payload_algid == 0x02 || state->payload_algid == 0x21 || state->payload_algid == 0x22)
{ {
@@ -100,7 +104,7 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25 || state->payload_algid == 0x36 || state->payload_algid == 0x37) else if (state->payload_algid == 0x24 || state->payload_algid == 0x25)
{ {
//going to need a better check for this later on, or seperated keys or something //going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[0] == 1) if (state->aes_key_loaded[0] == 1)
@@ -108,6 +112,21 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
else if (state->payload_algid == 0x26)
{
if (state->straight_ks == 0x26)
{
encL = 0;
}
}
} }
if (encR) if (encR)
@@ -118,6 +137,10 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
{ {
encR = 0; encR = 0;
} }
else if (state->vtx_key_loaded == 1)
{
encR = 0;
}
} }
else if (state->payload_algidR == 0x02 ||state->payload_algidR == 0x21 || state->payload_algidR == 0x22) else if (state->payload_algidR == 0x02 ||state->payload_algidR == 0x21 || state->payload_algidR == 0x22)
{ {
@@ -126,7 +149,7 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
encR = 0; encR = 0;
} }
} }
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37) else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25)
{ {
//going to need a better check for this later on, or seperated keys or something //going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[1] == 1) if (state->aes_key_loaded[1] == 1)
@@ -134,6 +157,21 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
encR = 0; encR = 0;
} }
} }
else if (state->payload_algidR >= 0x31 && state->payload_algidR <= 0x3A)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[1] == 1)
{
encR = 0;
}
}
else if (state->payload_algidR == 0x26)
{
if (state->straight_ks == 0x26)
{
encR = 0;
}
}
} }
//debug disable enc check for AES testing //debug disable enc check for AES testing
@@ -716,13 +754,28 @@ void playSynthesizedVoiceFM (dsd_opts * opts, dsd_state * state)
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid == 0x84 || state->payload_algid == 0x89 || state->nxdn_cipher_type == 0x3 || state->payload_algid == 0x36 || state->payload_algid == 0x37) else if (state->payload_algid == 0x84 || state->payload_algid == 0x89 || state->nxdn_cipher_type == 0x3)
{ {
if (state->aes_key_loaded[0] == 1) if (state->aes_key_loaded[0] == 1)
{ {
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
else if (state->payload_algidR == 0x26)
{
if (state->straight_ks == 0x26)
{
encL = 0;
}
}
} }
//WIP: Mute if on B list (or not W list) //WIP: Mute if on B list (or not W list)
@@ -1123,6 +1176,10 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
{ {
encL = 0; encL = 0;
} }
else if (state->vtx_key_loaded == 1)
{
encL = 0;
}
} }
else if (state->payload_algid == 0x02 || state->payload_algid == 0x21 || state->payload_algid == 0x22) else if (state->payload_algid == 0x02 || state->payload_algid == 0x21 || state->payload_algid == 0x22)
{ {
@@ -1131,7 +1188,7 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25 || state->payload_algid == 0x36 || state->payload_algid == 0x37) else if (state->payload_algid == 0x24 || state->payload_algid == 0x25)
{ {
//going to need a better check for this later on, or seperated keys or something //going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[0] == 1) if (state->aes_key_loaded[0] == 1)
@@ -1139,6 +1196,21 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
else if (state->payload_algid == 0x26)
{
if (state->straight_ks == 0x26)
{
encL = 0;
}
}
} }
if (encR) if (encR)
@@ -1149,6 +1221,10 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
{ {
encR = 0; encR = 0;
} }
else if (state->vtx_key_loaded == 1)
{
encR = 0;
}
} }
else if (state->payload_algidR == 0x02 ||state->payload_algidR == 0x21 || state->payload_algidR == 0x22) else if (state->payload_algidR == 0x02 ||state->payload_algidR == 0x21 || state->payload_algidR == 0x22)
{ {
@@ -1157,7 +1233,7 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
encR = 0; encR = 0;
} }
} }
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37) else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25)
{ {
//going to need a better check for this later on, or seperated keys or something //going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[1] == 1) if (state->aes_key_loaded[1] == 1)
@@ -1165,6 +1241,21 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
encR = 0; encR = 0;
} }
} }
else if (state->payload_algidR >= 0x31 && state->payload_algidR <= 0x3A)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[1] == 1)
{
encR = 0;
}
}
else if (state->payload_algidR == 0x26)
{
if (state->straight_ks == 0x26)
{
encR = 0;
}
}
} }
//debug for AES //debug for AES

View File

@@ -16,6 +16,7 @@
*/ */
#include <assert.h> #include <assert.h>
#include <limits.h>
#include "dsd.h" #include "dsd.h"
@@ -121,22 +122,46 @@ use_symbol (dsd_opts* opts, dsd_state* state, int symbol)
UNUSED(symbol); UNUSED(symbol);
int i; int i;
int sbuf2[128];
int lmin, lmax, lsum; int lmin, lmax, lsum;
int lmn[2] = {INT_MAX, INT_MAX};
int lmx[2] = {INT_MIN, INT_MIN};
// single-pass tracking of the 2 smallest and 2 largest values in the symbol window
for (i = 0; i < opts->ssize; i++) for (i = 0; i < opts->ssize; i++)
{ {
sbuf2[i] = state->sbuf[i]; int x = state->sbuf[i];
if (x < lmn[1])
{
if (x < lmn[0])
{
lmn[1] = lmn[0];
lmn[0] = x;
}
else
{
lmn[1] = x;
}
}
if (x > lmx[1])
{
if (x > lmx[0])
{
lmx[1] = lmx[0];
lmx[0] = x;
}
else
{
lmx[1] = x;
}
}
} }
qsort (sbuf2, opts->ssize, sizeof (int), comp);
// continuous update of min/max in rf_mod=1 (QPSK) mode // continuous update of min/max in rf_mod=1 (QPSK) mode
// in c4fm min/max must only be updated during sync // in c4fm min/max must only be updated during sync
if (state->rf_mod == 1) if (state->rf_mod == 1)
{ {
lmin = (sbuf2[0] + sbuf2[1]) / 2; lmin = (lmn[0] + lmn[1]) / 2;
lmax = (sbuf2[(opts->ssize - 1)] + sbuf2[(opts->ssize - 2)]) / 2; lmax = (lmx[1] + lmx[0]) / 2;
state->minbuf[state->midx] = lmin; state->minbuf[state->midx] = lmin;
state->maxbuf[state->midx] = lmax; state->maxbuf[state->midx] = lmax;
if (state->midx == (opts->msize - 1)) if (state->midx == (opts->msize - 1))
@@ -178,7 +203,7 @@ use_symbol (dsd_opts* opts, dsd_state* state, int symbol)
if (opts->datascope == 1) if (opts->datascope == 1)
{ {
print_datascope(opts, state, sbuf2); print_datascope(opts, state, state->sbuf);
} }
} }
else else

View File

@@ -877,6 +877,76 @@ void unpack_ambe (uint8_t * input, char * ambe)
ambe[48] = input[6] >> 7; ambe[48] = input[6] >> 7;
} }
uint64_t reverse_lfsr_32a_to_len(uint8_t * iv, int16_t len)
{
uint64_t lfsr = 0, bit1 = 0, bit2 = 0;
lfsr = ((uint64_t)iv[0] << 24ULL) + ((uint64_t)iv[1] << 16ULL) + ((uint64_t)iv[2] << 8ULL) + ((uint64_t)iv[3] << 0ULL);
memset (iv, 0, 8*sizeof(uint8_t));
for(int16_t cnt = 0; cnt < len; cnt++)
{
//32,22,2,1 (per Xilinx XAPP 052) Table 3: Taps for Maximum-Length LFSR Counters
//taps at the +1 position on all but MSB, then check the LSB and configure bit as required
bit1 = ( (lfsr >> 22) ^ (lfsr >> 2) ^ (lfsr >> 1) ) & 0x1;
bit2 = lfsr & 1;
if (bit1 == bit2)
bit2 = 0;
else bit2 = 1;
//just run this in reverse of normal LFSR
lfsr = (lfsr >> 1) | (bit2 << 31);
}
for (int16_t i = 0; i < 4; i++)
iv[i] = (lfsr >> (24-(i*8))) & 0xFF;
fprintf (stderr, "REV LFSR AES(%02d): ", len);
for (int16_t i = 0; i < 4; i++)
fprintf (stderr, "%02X", iv[i]);
return bit2;
}
uint64_t reverse_lfsr_32d_to_len(uint8_t * iv, int16_t len)
{
uint64_t lfsr = 0, bit1 = 0, bit2 = 0;
lfsr = ((uint64_t)iv[0] << 24ULL) + ((uint64_t)iv[1] << 16ULL) + ((uint64_t)iv[2] << 8ULL) + ((uint64_t)iv[3] << 0ULL);
memset (iv, 0, 8*sizeof(uint8_t));
for(int16_t cnt = 0; cnt < len; cnt++)
{
//magical taps that somebody must have dreamt up
//taps at the +1 position on all but MSB, then check the LSB and configure bit as required
bit1 = ((lfsr >> 4) ^ (lfsr >> 2)) & 0x1;
bit2 = lfsr & 1;
if (bit1 == bit2)
bit2 = 0;
else bit2 = 1;
//just run this in reverse of normal LFSR
lfsr = (lfsr >> 1) | (bit2 << 31);
}
for (int16_t i = 0; i < 4; i++)
iv[i] = (lfsr >> (24-(i*8))) & 0xFF;
fprintf (stderr, "REV LFSR RC4(%02d): ", len);
for (int16_t i = 0; i < 4; i++)
fprintf (stderr, "%02X", iv[i]);
return bit2;
}
//recover previous IV for SDRTrunk .mbe files when P25p1 //recover previous IV for SDRTrunk .mbe files when P25p1
uint64_t reverse_lfsr_64_to_len(dsd_opts * opts, uint8_t * iv, int16_t len) uint64_t reverse_lfsr_64_to_len(dsd_opts * opts, uint8_t * iv, int16_t len)
{ {
@@ -1027,7 +1097,7 @@ uint16_t ambe2_str_to_decode(dsd_opts * opts, dsd_state * state, char * ambe_str
//force slot to 0 //force slot to 0
state->currentslot = 0; state->currentslot = 0;
uint8_t c3_hex = (uint64_t)ConvertBitIntoBytes(c3, 4); uint8_t c3_hex = (uint8_t)ConvertBitIntoBytes(c3, 4);
//debug //debug
// fprintf (stderr, "\n AMBE#: %02d / %d / %d; F: %X;", ambe2_counter, ambe2_counter/3, ambe2_counter%3, c3_hex); // fprintf (stderr, "\n AMBE#: %02d / %d / %d; F: %X;", ambe2_counter, ambe2_counter/3, ambe2_counter%3, c3_hex);
@@ -1035,17 +1105,6 @@ uint16_t ambe2_str_to_decode(dsd_opts * opts, dsd_state * state, char * ambe_str
//use div and mod 3 to set current storage fragment //use div and mod 3 to set current storage fragment
state->late_entry_mi_fragment[0][(ambe2_counter/3)+1][ambe2_counter%3] = c3_hex; state->late_entry_mi_fragment[0][(ambe2_counter/3)+1][ambe2_counter%3] = c3_hex;
//run LFSR evaluate stored codewords for matching IV or replace if not match or not set
if (ambe2_counter == 17)
{
if (state->payload_mi != 0)
{
fprintf (stderr, "\n");
LFSR(state);
}
fprintf (stderr, "\n");
dmr_late_entry_mi (opts, state);
}
} }
char ambe_d[49]; memset(ambe_d, 0, sizeof(ambe_d)); char ambe_d[49]; memset(ambe_d, 0, sizeof(ambe_d));
@@ -1561,6 +1620,60 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
} }
else if ((alg_id == 0x24 || alg_id == 0x25) && state->payload_mi != 0)
{
uint8_t ks_bytes[375];
memset(ks_bytes, 0, sizeof(ks_bytes));
uint8_t aes_key[32];
memset(aes_key, 0, sizeof(aes_key));
uint8_t empty[32];
memset(empty, 0, sizeof(empty));
//Load key from A1 - A4
for (int i = 0; i < 8; i++)
{
aes_key[i+0] = (state->A1[0] >> (56-(i*8))) & 0xFF;
aes_key[i+8] = (state->A2[0] >> (56-(i*8))) & 0xFF;
aes_key[i+16] = (state->A3[0] >> (56-(i*8))) & 0xFF;
aes_key[i+24] = (state->A4[0] >> (56-(i*8))) & 0xFF;
}
//check to see if a key is actually loaded
if (memcmp (aes_key, empty, sizeof(aes_key)) != 0)
{
if (alg_id == 0x24) //128, or 256
aes_ofb_keystream_output(state->aes_iv, aes_key, ks_bytes, 0, 16); //16*16=256
else aes_ofb_keystream_output(state->aes_iv, aes_key, ks_bytes, 2, 16); //16*16=256
unpack_byte_array_into_bit_array(ks_bytes+16, ks, 256-16); //unpack starting after discards
ks_available = 1;
}
}
//Forced Application of DMRA DES Mode (untested on .mbe real samples)
if (alg_id == 0x22 && state->R != 0 && state->payload_mi != 0)
{
//expand IV from 32 to 64 (late entry iv should fix this)
// if (alg_id == 0x22)
// LFSR64(state);
uint8_t ks_bytes[375];
memset(ks_bytes, 0, sizeof(ks_bytes));
des_multi_keystream_output(iv_hex, state->R, ks_bytes, 1, 32); //32*8=256
unpack_byte_array_into_bit_array(ks_bytes+8, ks, 256-8);
ks_available = 1;
}
} }
//TODO: This //TODO: This
else if (state->forced_alg_id == 1) //Basic Privacy else if (state->forced_alg_id == 1) //Basic Privacy
@@ -1700,7 +1813,11 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
//debug set value //debug set value
if (opts->payload == 1) if (opts->payload == 1)
fprintf (stderr, "\n IV: %016llX;", iv_hex); //not really needed if loaded into array {
if (iv_hex > 0xFFFFFFFF)
fprintf (stderr, "\n IV: %016llX;", iv_hex);
else fprintf (stderr, "\n IV: %08llX;", iv_hex);
}
//debug print current str_buffer //debug print current str_buffer
// fprintf (stderr, "\n Encryption MI/IV: %s", str_buffer); // fprintf (stderr, "\n Encryption MI/IV: %s", str_buffer);
@@ -1752,10 +1869,14 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
} }
//P25 DES (v1 tested and working on P1, v2 should be okay now) //P25 and DMR DES (v1 tested and working on P1, v2 should be okay now), DMR Untested but should be okay
else if (alg_id == 0x81 && state->R != 0) else if ((alg_id == 0x22 || alg_id == 0x81) && state->R != 0)
{ {
//expand IV from 32 to 64
if (alg_id == 0x22)
LFSR64(state);
des_multi_keystream_output(iv_hex, state->R, ks_bytes, 1, 32); //32*8=256 des_multi_keystream_output(iv_hex, state->R, ks_bytes, 1, 32); //32*8=256
if (protocol == 1) //Phase 1 IMBE start on 19 for DES-OFB (8 discard + 9 LCW + 2 reserved) if (protocol == 1) //Phase 1 IMBE start on 19 for DES-OFB (8 discard + 9 LCW + 2 reserved)
@@ -1792,8 +1913,8 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
} }
//P25 AES (v1 and v2 tested working on P1) //P25 and DMR AES (v1 and v2 tested working on P1), working on DMR
else if ( (alg_id == 0x84 || alg_id == 0x89) && state->aes_key_loaded[0] == 1 ) else if ( (alg_id == 0x24 || alg_id == 0x25 || alg_id == 0x84 || alg_id == 0x89) && state->aes_key_loaded[0] == 1 )
{ {
uint8_t aes_key[32]; uint8_t aes_key[32];
@@ -1821,9 +1942,11 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
aes_last_iv[i] = aes_iv[i]; aes_last_iv[i] = aes_iv[i];
//Resolve the longer IV from the shorter one //Resolve the longer IV from the shorter one
lfsr_64_to_128(aes_iv); if (alg_id == 0x24 || alg_id == 0x25)
lfsr_32_to_128(aes_iv);
else lfsr_64_to_128(aes_iv);
if (alg_id == 0x89) //128, or 256 if (alg_id == 0x89 || alg_id == 0x24) //128, or 256
aes_ofb_keystream_output(aes_iv, aes_key, ks_bytes, 0, 16); //16*16=256 aes_ofb_keystream_output(aes_iv, aes_key, ks_bytes, 0, 16); //16*16=256
else aes_ofb_keystream_output(aes_iv, aes_key, ks_bytes, 2, 16); //16*16=256 else aes_ofb_keystream_output(aes_iv, aes_key, ks_bytes, 2, 16); //16*16=256
@@ -1987,6 +2110,15 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
//reset if over for DMR 18 AMBE+2 frames //reset if over for DMR 18 AMBE+2 frames
if (dmra_le && ambe2_counter == 18) if (dmra_le && ambe2_counter == 18)
{ {
//run LFSR evaluate stored codewords for matching IV or replace if not match or not set
if (state->forced_alg_id == 0x21 && state->payload_mi != 0)
{
fprintf (stderr, "\n");
LFSR(state);
}
fprintf (stderr, "\n");
dmr_late_entry_mi (opts, state);
ambe2_counter = 0; ambe2_counter = 0;
ks_idx = 0; ks_idx = 0;
} }

View File

@@ -16,6 +16,7 @@
*/ */
#include "dsd.h" #include "dsd.h"
#include <limits.h>
#include <locale.h> #include <locale.h>
void void
@@ -164,7 +165,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
char synctest8[9]; //M17 char synctest8[9]; //M17
char modulation[8]; char modulation[8];
char *synctest_p; char *synctest_p;
char synctest_buf[10240]; //what actually is assigned to this, can't find its use anywhere? char synctest_buf[10240 + 48]; //history for sync matching; look-back window up to 48 bytes
int lmin, lmax, lidx; int lmin, lmax, lidx;
//assign t_max value based on decoding type expected (all non-auto decodes first) //assign t_max value based on decoding type expected (all non-auto decodes first)
@@ -197,11 +198,10 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
} }
else t_max = 24; //24 for everything else else t_max = 24; //24 for everything else
int lbuf[48], lbuf2[48]; //if we use t_max in these arrays, and t >= t_max in condition below, then it can overflow those checks in there if t exceeds t_max int lbuf[48]; //if we use t_max in these arrays, and t >= t_max in condition below, then it can overflow those checks in there if t exceeds t_max
int lsum; int lsum;
//init the lbuf //init the lbuf
memset (lbuf, 0, sizeof(lbuf)); memset (lbuf, 0, sizeof(lbuf));
memset (lbuf2, 0, sizeof(lbuf2));
// detect frame sync // detect frame sync
t = 0; t = 0;
@@ -214,7 +214,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
synctest20[20] = 0; synctest20[20] = 0;
modulation[7] = 0; //not initialized or terminated (unsure if this would be an issue or not) modulation[7] = 0; //not initialized or terminated (unsure if this would be an issue or not)
synctest_pos = 0; synctest_pos = 0;
synctest_p = synctest_buf + 10; synctest_p = synctest_buf + 48;
sync = 0; sync = 0;
lmin = 0; lmin = 0;
lmax = 0; lmax = 0;
@@ -374,23 +374,37 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
*synctest_p = dibit; *synctest_p = dibit;
if (t >= t_max) //works excelent now with short sync patterns, and no issues with large ones! if (t >= t_max) //works excelent now with short sync patterns, and no issues with large ones!
{ {
int lmn[5] = {INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX};
int lmx[5] = {INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN};
int j, x;
for (i = 0; i < t_max; i++) //24 for (i = 0; i < t_max; i++) //24
{ {
lbuf2[i] = lbuf[i]; x = lbuf[i];
if (x < lmn[4])
{
for (j = 4; j > 0 && x < lmn[j - 1]; j--)
lmn[j] = lmn[j - 1];
lmn[j] = x;
}
if (x > lmx[4])
{
for (j = 4; j > 0 && x > lmx[j - 1]; j--)
lmx[j] = lmx[j - 1];
lmx[j] = x;
}
} }
qsort (lbuf2, t_max, sizeof (int), comp);
//on shorter sync patterns, we need to look at the edges since we don't have many to pull from //on shorter sync patterns, we need to look at the edges since we don't have many to pull from
if (t_max < 13) if (t_max < 13)
{ {
lmin = (lbuf2[0] + lbuf2[1] + lbuf2[2]) / 3; lmin = (lmn[0] + lmn[1] + lmn[2]) / 3;
lmax = (lbuf2[t_max - 3] + lbuf2[t_max - 2] + lbuf2[t_max - 1]) / 3; lmax = (lmx[2] + lmx[1] + lmx[0]) / 3;
} }
//on longer sync patterns, we can get more towards the middle and eliminate outlier values //on longer sync patterns, we can get more towards the middle and eliminate outlier values
else else
{ {
lmin = (lbuf2[2] + lbuf2[3] + lbuf2[4]) / 3; lmin = (lmn[2] + lmn[3] + lmn[4]) / 3;
lmax = (lbuf2[t_max - 5] + lbuf2[t_max - 4] + lbuf2[t_max - 3]) / 3; lmax = (lmx[4] + lmx[3] + lmx[2]) / 3;
} }
if (state->rf_mod == 1) if (state->rf_mod == 1)
@@ -1374,7 +1388,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
{ {
// buffer reset // buffer reset
synctest_pos = 0; synctest_pos = 0;
synctest_p = synctest_buf; synctest_p = synctest_buf + 48;
noCarrier (opts, state); noCarrier (opts, state);
} }

View File

@@ -528,7 +528,7 @@ void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
if (slot == 1) src = state->lasttgR; if (slot == 1) src = state->lasttgR;
//save to event history string //save to event history string
if (state->event_history_s[slot].Event_History_Items[0].source_id == src) // if (state->event_history_s[slot].Event_History_Items[0].source_id == src)
sprintf (state->event_history_s[slot].Event_History_Items[0].gps_s, "%s", state->dmr_embedded_gps[slot]); sprintf (state->event_history_s[slot].Event_History_Items[0].gps_s, "%s", state->dmr_embedded_gps[slot]);
//save to LRRP report for mapping/logging //save to LRRP report for mapping/logging

View File

@@ -83,15 +83,6 @@
}; };
#endif #endif
int comp (const void *a, const void *b)
{
if (*((const int *) a) == *((const int *) b))
return 0;
else if (*((const int *) a) < *((const int *) b))
return -1;
else
return 1;
}
//struct for checking existence of directory to write to //struct for checking existence of directory to write to
struct stat st = {0}; struct stat st = {0};
@@ -437,6 +428,11 @@
state->K2 = 0; state->K2 = 0;
state->K3 = 0; state->K3 = 0;
state->K4 = 0; state->K4 = 0;
//vertex standard keys
// memset(state->vtx256_key, 0, sizeof(state->vtx256_key));
state->vtx40_key = 0;
state->vtx256_ekey = 0;
state->vtx_key_loaded = 0;
memset (state->A1, 0, sizeof(state->A1)); memset (state->A1, 0, sizeof(state->A1));
memset (state->A2, 0, sizeof(state->A2)); memset (state->A2, 0, sizeof(state->A2));
memset (state->A3, 0, sizeof(state->A3)); memset (state->A3, 0, sizeof(state->A3));
@@ -590,6 +586,14 @@
//we do reset the counter, but not the static_ks_bits //we do reset the counter, but not the static_ks_bits
memset (state->static_ks_counter, 0, sizeof(state->static_ks_counter)); memset (state->static_ks_counter, 0, sizeof(state->static_ks_counter));
//Hytera OTA Encryption
memset(state->hota_lc_ctx, 0, sizeof(state->hota_lc_ctx));
memset(state->hota_ctx_ok, 0, sizeof(state->hota_ctx_ok));
state->hota_sbrc_idx1[0] = -1;
state->hota_sbrc_idx2[0] = -1;
state->hota_sbrc_idx1[1] = -1;
state->hota_sbrc_idx2[1] = -1;
} //nocarrier } //nocarrier
void void
@@ -699,6 +703,9 @@
opts->rtlsdr_center_freq = 850000000; //set to an initial value (if user is using a channel map, then they won't need to specify anything other than -i rtl if desired) opts->rtlsdr_center_freq = 850000000; //set to an initial value (if user is using a channel map, then they won't need to specify anything other than -i rtl if desired)
opts->rtl_started = 0; opts->rtl_started = 0;
opts->rtl_rms = 0; //root means square power level on rtl input signal opts->rtl_rms = 0; //root means square power level on rtl input signal
opts->rtltcp_enabled = 0; //1 when the rtl input is an rtl_tcp server instead of a local dongle
sprintf (opts->rtltcp_hostname, "%s", "localhost");
opts->rtltcp_portno = 1234; //rtl_tcp default port
//end RTL user options //end RTL user options
opts->pulse_raw_rate_in = 48000; opts->pulse_raw_rate_in = 48000;
opts->pulse_raw_rate_out = 48000;// opts->pulse_raw_rate_out = 48000;//
@@ -1068,6 +1075,7 @@
state->any_bp = 0; state->any_bp = 0;
state->straight_ks = 0; state->straight_ks = 0;
state->straight_mod = 0; state->straight_mod = 0;
state->straight_discard = 0;
//ks array storage and counters //ks array storage and counters
memset (state->ks_octetL, 0, sizeof(state->ks_octetL)); memset (state->ks_octetL, 0, sizeof(state->ks_octetL));
@@ -1094,6 +1102,12 @@
//xl specific, we need to know if the ESS is from HDU, or from LDU2 //xl specific, we need to know if the ESS is from HDU, or from LDU2
state->xl_is_hdu = 0; state->xl_is_hdu = 0;
//vertex standard keys
memset(state->vtx256_key, 0, sizeof(state->vtx256_key));
state->vtx40_key = 0;
state->vtx256_ekey = 0;
state->vtx_key_loaded = 0;
//NXDN, when a new IV has arrived //NXDN, when a new IV has arrived
state->nxdn_new_iv = 0; state->nxdn_new_iv = 0;
@@ -1235,6 +1249,26 @@
state->dmr_end_alert[0] = 0; state->dmr_end_alert[0] = 0;
state->dmr_end_alert[1] = 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;
//Hytera OTA Encryption
state->hota_enc_check = 0;
state->hota_key_loaded = 0;
state->dmr_is_hota = 0;
state->hota_bit_shift = 0;
memset(state->hota_byte_shifts, 0, sizeof(state->hota_byte_shifts));
memset(state->hota_rc4_key, 0, sizeof(state->hota_rc4_key));
memset(state->hota_rc4_ks, 0, sizeof(state->hota_rc4_ks));
memset(state->hota_lc_ctx, 0, sizeof(state->hota_lc_ctx));
memset(state->hota_ctx_ok, 0, sizeof(state->hota_ctx_ok));
state->hota_sbrc_idx1[0] = -1;
state->hota_sbrc_idx2[0] = -1;
state->hota_sbrc_idx1[1] = -1;
state->hota_sbrc_idx2[1] = -1;
sprintf (state->dmr_branding, "%s", ""); sprintf (state->dmr_branding, "%s", "");
sprintf (state->dmr_branding_sub, "%s", ""); sprintf (state->dmr_branding_sub, "%s", "");
sprintf (state->dmr_site_parms, "%s", ""); sprintf (state->dmr_site_parms, "%s", "");
@@ -1398,6 +1432,7 @@
printf (" pulse:6 or pulse:virtual_sink2.monitor for pulse audio signal input on virtual_sink2 (see -O) \n"); printf (" pulse:6 or pulse:virtual_sink2.monitor for pulse audio signal input on virtual_sink2 (see -O) \n");
printf (" rtl for rtl dongle (Default Values -- see below)\n"); printf (" rtl for rtl dongle (Default Values -- see below)\n");
printf (" rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below)\n"); printf (" rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below)\n");
printf (" rtltcp:host:port:freq:gain:ppm:bw:sq:vol for rtl dongle on an rtl_tcp server (see below)\n");
printf (" tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355)\n"); printf (" tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355)\n");
printf (" tcp:192.168.7.5:7355 for custom address and port \n"); printf (" tcp:192.168.7.5:7355 for custom address and port \n");
printf (" filename.bin for OP25/FME capture bin files\n"); printf (" filename.bin for OP25/FME capture bin files\n");
@@ -1452,7 +1487,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"); 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 #endif
printf (" -z Set TDMA Voice Slot Preference when using /dev/dsp audio output (prevent lag and stuttering)\n"); 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 (" -v <hex> Set Filtering Bitmap Options (Advanced Option)\n");
printf (" 1 1 1 1 (0xF): PBF/LPF/HPF/HPFD on\n"); printf (" 1 1 1 1 (0xF): PBF/LPF/HPF/HPFD on\n");
printf ("\n"); printf ("\n");
@@ -1469,6 +1504,11 @@
printf (" vol <num> RTL-SDR Sample 'Volume' Multiplier (default = 2)(1,2,3)\n"); printf (" vol <num> RTL-SDR Sample 'Volume' Multiplier (default = 2)(1,2,3)\n");
printf (" Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T\n"); printf (" Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T\n");
printf (" Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2\n"); printf (" Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2\n");
printf (" Usage: rtltcp:host:port:freq:gain:ppm:bw:sq:vol\n");
printf (" host <name> rtl_tcp Server Hostname or IP Address (default = localhost)\n");
printf (" port <num> rtl_tcp Server Port (default = 1234)\n");
printf (" freq, gain, ppm, bw, sq, vol as above\n");
printf (" Example: dsd-fme -fs -i rtltcp:192.168.1.10:1234:450M:26:-2:8 -C connect_plus_chan.csv -T\n");
printf ("\n"); printf ("\n");
printf ("Decoder options:\n"); printf ("Decoder options:\n");
printf (" -fa Auto Detection\n"); printf (" -fa Auto Detection\n");
@@ -1492,7 +1532,7 @@
printf (" -fE Decode only EDACS EA/ProVoice with ESK 0xA0*\n"); printf (" -fE Decode only EDACS EA/ProVoice with ESK 0xA0*\n");
printf (" -fm Decode only dPMR*\n"); printf (" -fm Decode only dPMR*\n");
printf (" -l Disable DMR, dPMR, NXDN, M17 input filtering\n"); printf (" -l Disable DMR, dPMR, NXDN, M17 input filtering\n");
printf (" -u <num> Unvoiced speech quality (default=3)\n"); // printf (" -u <num> Unvoiced speech quality (default=3)\n");
printf (" -xx Expect non-inverted X2-TDMA signal\n"); printf (" -xx Expect non-inverted X2-TDMA signal\n");
printf (" -xr Expect inverted DMR signal\n"); printf (" -xr Expect inverted DMR signal\n");
printf (" -xd Expect inverted ICOM dPMR signal\n"); printf (" -xd Expect inverted ICOM dPMR signal\n");
@@ -1534,11 +1574,20 @@
printf (" -H '736B9A9C5645288B 243AD5CB8701EF8A' \n"); printf (" -H '736B9A9C5645288B 243AD5CB8701EF8A' \n");
printf (" -H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8' \n"); printf (" -H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8' \n");
printf ("\n"); printf ("\n");
printf (" -H <hex> Manually Enter AES-128, AES-256, or Kirisun-256 Hex Key (see example below)\n"); printf (" -H <hex> Manually Enter AES-128, AES-256, or Kirisun 128 or 256 Hex Key (see example below)\n");
printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n"); printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n");
printf (" -H '736B9A9C5645288B 243AD5CB8701EF8A' \n"); printf (" -H '736B9A9C5645288B 243AD5CB8701EF8A' \n");
printf (" -H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8' \n"); printf (" -H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8' \n");
printf ("\n"); printf ("\n");
printf (" -H <hex> Manually Enter Caltta 10/32/64 Char Basic Privacy Hex Key (see example below)\n");
printf (" -H C:40:1234567890 \n");
printf (" -H C:128:736B9A9C5645288B243AD5CB8701EF8A' \n");
printf (" -H C:256:20029736A5D91042C923EB0697484433005EFC58A1905195E28E9C7836AA2DB8' \n");
printf ("\n");
printf (" -H <hex> Manually Enter Hytera OTA Encryption 32 Char Hex Key (see example below)\n");
printf (" -H O:0:0 no keystream application, but check for Hytera OTA Encryption and DMRA EP Late Entry IV.\n");
printf (" -H O:128:17bfe59b29a06136fb7d3811fd1a4904 for 128-bit RC4 OTA Key Setup\n");
printf ("\n");
printf (" -R <dec> Manually Enter dPMR or NXDN EHR Scrambler Key Value (Decimal Value)\n"); printf (" -R <dec> Manually Enter dPMR or NXDN EHR Scrambler Key Value (Decimal Value)\n");
printf (" \n"); printf (" \n");
printf (" -1 <hex> Manually Enter RC4 or DES Key Value (DMR, P25, NXDN) (Hex Value) \n"); printf (" -1 <hex> Manually Enter RC4 or DES Key Value (DMR, P25, NXDN) (Hex Value) \n");
@@ -1571,6 +1620,23 @@
printf (" \n"); printf (" \n");
printf (" -A <hex> Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value) \n"); printf (" -A <hex> Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value) \n");
printf (" \n"); printf (" \n");
printf (" -$ <string> Enter RAS Password 6-24 Char Key Value (DMR) (a-z A-Z, 0-9, $ # _ -) \n");
printf (" -$ Password123\n");
printf (" \n");
printf (" Special Use Cases involving # and $, use parenthesis and put \\ in front of all $ signs\n");
printf (" -$ \"######\"\n");
printf (" -$ \"\\$\\$\\$\\$\\$\\$\"\n");
printf (" \n");
printf (" -y <hex> Enter RAS 56-bit Effective Key Value (DMR) (14 Char 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");
printf (" -u <string> Manually Enter Vertex Standard 10 or 64 Char Hex Key \n");
printf (" -u 10:1234567891 or 64:AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDD\n");
printf (" -u 0:0 no KS application, but apply the inversion to the interleave (clear errors on 256)\n");
printf (" \n");
printf (" -S <str> Manually Enter and Enforce Generic Static Keystream -> Length and BYTE PACKED / ALIGNED String for AMBE (up to 882 bits)\n"); printf (" -S <str> Manually Enter and Enforce Generic Static Keystream -> Length and BYTE PACKED / ALIGNED String for AMBE (up to 882 bits)\n");
printf (" For Example, enter 16-bit Keystream 0909 as:\n"); printf (" For Example, enter 16-bit Keystream 0909 as:\n");
printf (" -S 16:0909\n"); printf (" -S 16:0909\n");
@@ -1580,6 +1646,8 @@
printf (" -S 49:ED0AED4AED4AED4A\n"); printf (" -S 49:ED0AED4AED4AED4A\n");
printf (" For Example, Baofeng 49-bit Keystream for 56-bit Custom Encryption as:\n"); printf (" For Example, Baofeng 49-bit Keystream for 56-bit Custom Encryption as:\n");
printf (" -S 49:11AA22BB33CC44\n"); printf (" -S 49:11AA22BB33CC44\n");
printf (" For Example, Caltta 40-bit Keystream w/ optional 7-bit discard as:\n");
printf (" -S 40:1234567890:7\n");
printf (" \n"); printf (" \n");
printf (" -k <file> Import Key List from csv file (Decimal Format) -- Lower Case 'k'.\n"); printf (" -k <file> Import Key List from csv file (Decimal Format) -- Lower Case 'k'.\n");
printf (" Only supports NXDN, DMR Basic Privacy (decimal value). \n"); printf (" Only supports NXDN, DMR Basic Privacy (decimal value). \n");
@@ -1610,7 +1678,7 @@
printf (" -p Disable Tune to Private Calls (DMR TIII, P25, NXDN Type-C and Type-D)\n"); printf (" -p Disable Tune to Private Calls (DMR TIII, P25, NXDN Type-C and Type-D)\n");
printf (" -E Disable Tune to Group Calls (DMR TIII, Con+, Cap+, P25, NXDN Type-C, and Type-D)\n"); printf (" -E Disable Tune to Group Calls (DMR TIII, Con+, Cap+, P25, NXDN Type-C, and Type-D)\n");
printf (" -e Enable Tune to Data Calls (DMR TIII, Cap+, NXDN Type-C)\n"); printf (" -e Enable Tune to Data Calls (DMR TIII, Cap+, NXDN Type-C)\n");
printf (" (NOTE: No Clear Distinction between Cap+ Private Voice Calls and Data Calls -- Both enabled with Data Calls \n"); printf (" (NOTE: No Clear Distinction between Cap+ Private Voice Calls and Data Calls -- Both enabled with Data Calls) \n");
printf (" -I <dec> Specify TG to Hold During Trunking (DMR, P25, NXDN Type-C Trunking)\n"); printf (" -I <dec> Specify TG to Hold During Trunking (DMR, P25, NXDN Type-C Trunking)\n");
printf (" -U <port> Enable RIGCTL/TCP; Set TCP Port for RIGCTL. (4532 on SDR++)\n"); printf (" -U <port> Enable RIGCTL/TCP; Set TCP Port for RIGCTL. (4532 on SDR++)\n");
printf (" -B <Hertz> Set RIGCTL Setmod Bandwidth in Hertz (0 - default - OFF)\n"); printf (" -B <Hertz> Set RIGCTL Setmod Bandwidth in Hertz (0 - default - OFF)\n");
@@ -1898,7 +1966,7 @@
exitflag = 0; 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) switch (c)
@@ -2017,6 +2085,30 @@
baofeng_ap_pc5_keystream_creation(&state, optarg); baofeng_ap_pc5_keystream_creation(&state, optarg);
break; break;
//Auctus (GoComm) Keystream Creation
case ',':
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;
//Vertex Standard KS Generation
case 'u':
vertex_keystream_setup(&state, optarg);
state.keyloader = 0;
break;
//Straight KS Generation //Straight KS Generation
case 'S': case 'S':
straight_mod_xor_keystream_creation(&state, optarg); straight_mod_xor_keystream_creation(&state, optarg);
@@ -2027,10 +2119,11 @@
fprintf (stderr,"DMRA Late Entry Encryption Identifiers Disabled\n"); fprintf (stderr,"DMRA Late Entry Encryption Identifiers Disabled\n");
break; break;
case 'y': //use experimental 'float' audio output //float audio experiment not going to be used going forward, very minimal benefit
opts.floating_point = 1; //enable floating point audio output // case 'y': //use experimental 'float' audio output
fprintf (stderr,"Enabling Experimental Floating Point Audio Output\n"); // opts.floating_point = 1; //enable floating point audio output
break; // fprintf (stderr,"Enabling Experimental Floating Point Audio Output\n");
// break;
case 'Y': //conventional scanner mode case 'Y': //conventional scanner mode
opts.scanner_mode = 1; //enable scanner opts.scanner_mode = 1; //enable scanner
@@ -2230,32 +2323,48 @@
break; break;
case 'H': case 'H':
//new handling for 10/32/64 Char Key //check for caltta bp key load
if (optarg[0] == 'C' && optarg[1] == ':')
strncpy(opts.szNumbers, optarg, 1023);
opts.szNumbers[1023] = '\0';
state.K1 = strtoull (opts.szNumbers, &pEnd, 16);
state.K2 = strtoull (pEnd, &pEnd, 16);
state.K3 = strtoull (pEnd, &pEnd, 16);
state.K4 = strtoull (pEnd, &pEnd, 16);
fprintf (stderr, "Hytera40/128/256 BP or AES128/256 Key = %016llX %016llX %016llX %016llX\n", state.K1, state.K2, state.K3, state.K4);
opts.dmr_mute_encL = 0;
opts.dmr_mute_encR = 0;
if (state.K1 == 0 && state.K2 == 0 && state.K3 == 0 && state.K4 == 0)
{ {
opts.dmr_mute_encL = 1; caltta_bp_keystream_creation(&state, optarg+2);
opts.dmr_mute_encR = 1; }
//check for Hytera OTA key load
else if (optarg[0] == 'O' && optarg[1] == ':')
{
hytera_ota_keystream_setup(&state, optarg+2);
}
else
{
strncpy(opts.szNumbers, optarg, 1023);
opts.szNumbers[1023] = '\0';
state.K1 = strtoull (opts.szNumbers, &pEnd, 16);
state.K2 = strtoull (pEnd, &pEnd, 16);
state.K3 = strtoull (pEnd, &pEnd, 16);
state.K4 = strtoull (pEnd, &pEnd, 16);
if (state.K2 == 0 && state.K3 == 0 && state.K4 == 0)
fprintf (stderr, "Hytera BP 40-bit Key: %010llX\n", state.K1);
else if (state.K2 != 0)
fprintf (stderr, "Multi 128-bit Key: %016llX%016llX\n", state.K1, state.K2);
else
fprintf (stderr, "Multi 256-bit Key: %016llX%016llX%016llX%016llX\n", state.K1, state.K2, state.K3, state.K4);
opts.dmr_mute_encL = 0;
opts.dmr_mute_encR = 0;
if (state.K1 == 0 && state.K2 == 0 && state.K3 == 0 && state.K4 == 0)
{
opts.dmr_mute_encL = 1;
opts.dmr_mute_encR = 1;
}
state.H = state.K1; //shim still required?
//load the AES keys into a seperate handler
state.A1[0] = state.A1[1] = state.K1;
state.A2[0] = state.A2[1] = state.K2;
state.A3[0] = state.A3[1] = state.K3;
state.A4[0] = state.A4[1] = state.K4;
//disable keyloader function
state.keyloader = 0;
//signal an aes key is loaded into each slot -- if no value presented, then this won't matter anyways
state.aes_key_loaded[0] = state.aes_key_loaded[1] = 1;
} }
state.H = state.K1; //shim still required?
//load the AES keys into a seperate handler
state.A1[0] = state.A1[1] = state.K1;
state.A2[0] = state.A2[1] = state.K2;
state.A3[0] = state.A3[1] = state.K3;
state.A4[0] = state.A4[1] = state.K4;
//disable keyloader function
state.keyloader = 0;
//signal an aes key is loaded into each slot -- if no value presented, then this won't matter anyways
state.aes_key_loaded[0] = state.aes_key_loaded[1] = 1;
break; break;
case '4': case '4':
@@ -3073,18 +3182,18 @@
fprintf (stderr,"Enabling 6000 sps P25p2 all optimizations.\n"); fprintf (stderr,"Enabling 6000 sps P25p2 all optimizations.\n");
} }
break; break;
case 'u': // case 'u':
sscanf (optarg, "%i", &opts.uvquality); // sscanf (optarg, "%i", &opts.uvquality);
if (opts.uvquality < 1) // if (opts.uvquality < 1)
{ // {
opts.uvquality = 1; // opts.uvquality = 1;
} // }
else if (opts.uvquality > 64) // else if (opts.uvquality > 64)
{ // {
opts.uvquality = 64; // opts.uvquality = 64;
} // }
fprintf (stderr,"Setting unvoice speech quality to %i waves per band.\n", opts.uvquality); // fprintf (stderr,"Setting unvoice speech quality to %i waves per band.\n", opts.uvquality);
break; // break;
case 'x': case 'x':
if (optarg[0] == 'x') if (optarg[0] == 'x')
{ {
@@ -3218,9 +3327,24 @@
if (curr != NULL) ; //continue if (curr != NULL) ; //continue
else goto RTLEND; //end early with preset values else goto RTLEND; //end early with preset values
curr = strtok(NULL, ":"); //rtl device number "-D" if (strcmp (curr, "rtltcp") == 0) //rtl_tcp server instead of a local dongle
if (curr != NULL) opts.rtl_dev_index = atoi (curr); {
else goto RTLEND; opts.rtltcp_enabled = 1;
curr = strtok(NULL, ":"); //rtl_tcp hostname
if (curr != NULL) snprintf (opts.rtltcp_hostname, sizeof(opts.rtltcp_hostname), "%s", curr);
else goto RTLEND;
curr = strtok(NULL, ":"); //rtl_tcp port
if (curr != NULL) opts.rtltcp_portno = atoi (curr);
else goto RTLEND;
}
else
{
curr = strtok(NULL, ":"); //rtl device number "-D"
if (curr != NULL) opts.rtl_dev_index = atoi (curr);
else goto RTLEND;
}
curr = strtok(NULL, ":"); //rtl freq "-c" curr = strtok(NULL, ":"); //rtl freq "-c"
if (curr != NULL) opts.rtlsdr_center_freq = (uint32_t)atofs(curr); if (curr != NULL) opts.rtlsdr_center_freq = (uint32_t)atofs(curr);
@@ -3263,6 +3387,8 @@
RTLEND: RTLEND:
if (opts.rtltcp_enabled == 1) goto RTLTCPEND; //no local dongle to look for
device_count = rtlsdr_get_device_count(); device_count = rtlsdr_get_device_count();
if (!device_count) if (!device_count)
{ {
@@ -3290,10 +3416,15 @@
} }
RTLTCPEND:
if (opts.rtl_volume_multiplier > 3 || opts.rtl_volume_multiplier < 0) if (opts.rtl_volume_multiplier > 3 || opts.rtl_volume_multiplier < 0)
opts.rtl_volume_multiplier = 1; //I wonder if you could flip polarity by using -1 opts.rtl_volume_multiplier = 1; //I wonder if you could flip polarity by using -1
fprintf (stderr, "Dev %d ", opts.rtl_dev_index); if (opts.rtltcp_enabled == 1)
fprintf (stderr, "rtl_tcp %s:%d ", opts.rtltcp_hostname, opts.rtltcp_portno);
else
fprintf (stderr, "Dev %d ", opts.rtl_dev_index);
fprintf (stderr, "Freq %d ", opts.rtlsdr_center_freq); fprintf (stderr, "Freq %d ", opts.rtlsdr_center_freq);
fprintf (stderr, "Gain %d ", opts.rtl_gain_value); fprintf (stderr, "Gain %d ", opts.rtl_gain_value);
fprintf (stderr, "PPM %d ", opts.rtlsdr_ppm_error); fprintf (stderr, "PPM %d ", opts.rtlsdr_ppm_error);

View File

@@ -304,9 +304,9 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if (state->payload_algid == 0x9F) //DES-XL if (state->payload_algid == 0x9F) //DES-XL
des_multi_keystream_output (state->payload_miP, state->R, state->ks_octetL, 2, state->xl_is_hdu); //hard coded bit count value, xl_is_hdu determines lfsr run values des_multi_keystream_output (state->payload_miP, state->R, state->ks_octetL, 2, state->xl_is_hdu); //hard coded bit count value, xl_is_hdu determines lfsr run values
if (state->payload_algid == 0x84) //AES256 if (state->payload_algid == 0x84) //AES256
aes_ofb_keystream_output (state->aes_iv, aes_key, state->ks_octetL, 2, 14); //14 + 1 discard round aes_ofb_keystream_output (state->aes_iv, aes_key, state->ks_octetL, 2, 16); //15 + 1 discard round
if (state->payload_algid == 0x89) //AES128 if (state->payload_algid == 0x89) //AES128
aes_ofb_keystream_output (state->aes_iv, aes_key, state->ks_octetL, 0, 14); //14 + 1 discard round aes_ofb_keystream_output (state->aes_iv, aes_key, state->ks_octetL, 0, 16); //15 + 1 discard round
} }
@@ -599,7 +599,14 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
} }
if (state->rkey_array[hash] != 0) if (state->rkey_array[hash] != 0)
{ {
#ifdef VERTEX_KEY40_BY_TG
state->vtx40_key = state->rkey_array[hash] & 0xFFFFFFFFFF;
vertex_40_keystream_creation(state, state->vtx40_key);
#elif VERTEX_KEY256_BY_TG
//TODO: This
#else
state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255 state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255
#endif
state->K1 = state->H = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit state->K1 = state->H = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit
opts->dmr_mute_encL = 0; opts->dmr_mute_encL = 0;
// fprintf (stderr, "Key: %X ", state->rkey_array[hash]); // fprintf (stderr, "Key: %X ", state->rkey_array[hash]);
@@ -619,72 +626,101 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
} }
} }
if ( (state->vtx_key_loaded != 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
(state->vtx_key_loaded != 0 && state->forced_alg_id == 1) )
{
if (memcmp(ambe_d, ambe_silence, 49) != 0)
{
for (int i = 0; i < 49; i++)
{
if (i < 8 || i > 11)
ambe_d[i] ^= (char)state->static_ks_bits[0][i];
}
}
opts->dmr_mute_encL = 0; //shim to unmute
}
//Hytera BP //TODO: Clean this mess up
if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) || if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
(state->K1 > 0 && state->forced_alg_id == 1) ) (state->K1 > 0 && state->forced_alg_id == 1) )
{ {
int pos = 0; int pos = 0;
unsigned long long int k1 = state->K1; unsigned long long int k1 = state->K1;
unsigned long long int k2 = state->K2; unsigned long long int k2 = state->K2;
unsigned long long int k3 = state->K3; unsigned long long int k3 = state->K3;
unsigned long long int k4 = state->K4; unsigned long long int k4 = state->K4;
int T_Key[256] = {0}; int T_Key[256] = {0};
int pN[882] = {0}; int pN[882] = {0};
int len = 0; int len = 0;
if (k2 == 0) if (k2 == 0)
{
len = 39;
k1 = k1 << 24;
}
if (k2 != 0)
{
len = 127;
}
if (k4 != 0)
{
len = 255;
}
for (i = 0; i < 64; i++)
{
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
}
for (i = 0; i < 882; i++)
{
pN[i] = T_Key[pos];
pos++;
if (pos > len)
{ {
pos = 0; len = 39;
k1 = k1 << 24;
} }
} if (k2 != 0)
//sanity check
if (state->DMRvcL > 17) //18
{
state->DMRvcL = 17; //18
}
pos = state->DMRvcL * 49;
if (memcmp(ambe_d, ambe_silence, 49) == 0)
pos += 49;
else
{
for(i = 0; i < 49; i++)
{ {
ambe_d[i] ^= pN[pos]; len = 127;
}
if (k4 != 0)
{
len = 255;
}
for (i = 0; i < 64; i++)
{
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
}
for (i = 0; i < 882; i++)
{
pN[i] = T_Key[pos];
pos++; pos++;
if (pos > len)
{
pos = 0;
}
} }
//sanity check
if (state->DMRvcL > 17) //18
{
state->DMRvcL = 17; //18
}
pos = state->DMRvcL * 49;
if (memcmp(ambe_d, ambe_silence, 49) == 0)
pos += 49;
else
{
for(i = 0; i < 49; i++)
{
ambe_d[i] ^= pN[pos];
pos++;
}
}
state->DMRvcL++;
} }
state->DMRvcL++;
//Caltta BP
if ( (state->straight_ks == 0x26 && state->dmr_so & 0x40 && state->payload_algid == 0x26) || //&& state->payload_keyid != 0
(state->straight_ks == 0x26 && state->forced_alg_id == 0x26) )
{
if (memcmp(ambe_d, ambe_silence, 49) == 0)
state->static_ks_counter[state->currentslot] += 56;
else
{
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%state->straight_mod] & 1); //Yikes!
state->static_ks_counter[state->currentslot] += 7;
}
} }
//DMR and P25p2 DES-OFB 56 Handling, Slot 1, VCH 0 -- consider moving into the AES handler //DMR and P25p2 DES-OFB 56 Handling, Slot 1, VCH 0 -- consider moving into the AES handler
@@ -743,8 +779,8 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
(state->currentslot == 0 && state->payload_algid == 0x25 && state->aes_key_loaded[0] == 1 ) || //DMR AES256 (state->currentslot == 0 && state->payload_algid == 0x25 && state->aes_key_loaded[0] == 1 ) || //DMR AES256
(state->currentslot == 0 && state->payload_algid == 0x89 && state->aes_key_loaded[0] == 1 ) || //P25 AES128 (state->currentslot == 0 && state->payload_algid == 0x89 && state->aes_key_loaded[0] == 1 ) || //P25 AES128
(state->currentslot == 0 && state->payload_algid == 0x84 && state->aes_key_loaded[0] == 1 ) || //P25 AES256 (state->currentslot == 0 && state->payload_algid == 0x84 && state->aes_key_loaded[0] == 1 ) || //P25 AES256
(state->currentslot == 0 && state->payload_algid == 0x36 && state->aes_key_loaded[0] == 1 ) || //KIRI ADV (state->currentslot == 0 && state->payload_algid >= 0x31 && \
(state->currentslot == 0 && state->payload_algid == 0x37 && state->aes_key_loaded[0] == 1 ) || //KIRI UNI state->payload_algid <= 0x3A && state->aes_key_loaded[0] == 1 ) || //KIRI Any
(state->currentslot == 0 && state->payload_algid == 0x02 && state->R != 0 ) ) //HYT ENHANCED (state->currentslot == 0 && state->payload_algid == 0x02 && state->R != 0 ) ) //HYT ENHANCED
{ {
@@ -777,19 +813,13 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if(state->payload_algid == 0x02) if(state->payload_algid == 0x02)
{ {
n = 0; n = 0;
hytera_enhanced_rc4_setup(opts, state, state->R, state->payload_mi); hytera_enhanced_rc4_setup(opts, state, state->R, state->payload_mi, 135);
} }
if (state->payload_algid == 0x36) if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
{ {
n = 0; n = 0;
kirisun_adv_keystream_creation(state); kirisun_keystream_creation(state);
} }
if (state->payload_algid == 0x37)
{
n = 0;
kirisun_uni_keystream_creation(state);
}
//Load Keystream Octet Bytes directly into keystream array //TODO: Convert to unpack function //Load Keystream Octet Bytes directly into keystream array //TODO: Convert to unpack function
for (i = 0; i < 9 * 16; i++) //9 rounds at 16 octets for (i = 0; i < 9 * 16; i++) //9 rounds at 16 octets
{ {
@@ -1016,6 +1046,9 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
state->payload_algid = 0; state->payload_algid = 0;
for (int i = 0; i < 49; i++) for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%state->straight_mod] & 1); //Yikes! ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%state->straight_mod] & 1); //Yikes!
//apply optional discard (i.e., caltta bp)
if (state->straight_discard != 0)
state->static_ks_counter[state->currentslot] += state->straight_discard;
} }
} }
@@ -1066,7 +1099,14 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
} }
if (state->rkey_array[hash] != 0) if (state->rkey_array[hash] != 0)
{ {
#ifdef VERTEX_KEY40_BY_TG
state->vtx40_key = state->rkey_array[hash] & 0xFFFFFFFFFF;
vertex_40_keystream_creation(state, state->vtx40_key);
#elif VERTEX_KEY256_BY_TG
//TODO: This
#else
state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255 state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255
#endif
state->K1 = state->H = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit state->K1 = state->H = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit
opts->dmr_mute_encR = 0; opts->dmr_mute_encR = 0;
// fprintf (stderr, "Key: %X ", state->rkey_array[hash]); // fprintf (stderr, "Key: %X ", state->rkey_array[hash]);
@@ -1086,6 +1126,21 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
} }
} }
if ( (state->vtx_key_loaded != 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
(state->vtx_key_loaded != 0 && state->forced_alg_id == 1) )
{
if (memcmp(ambe_d, ambe_silence, 49) != 0)
{
for (int i = 0; i < 49; i++)
{
if (i < 8 || i > 11)
ambe_d[i] ^= (char)state->static_ks_bits[1][i];
}
}
opts->dmr_mute_encR = 0; //shim to unmute
}
//Hytera BP //TODO: Clean this mess up
if ( (state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) || if ( (state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) ||
(state->K1 > 0 && state->forced_alg_id == 1)) (state->K1 > 0 && state->forced_alg_id == 1))
{ {
@@ -1154,6 +1209,20 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
state->DMRvcR++; state->DMRvcR++;
} }
//Caltta BP
if ( (state->straight_ks == 0x26 && state->dmr_soR & 0x40 && state->payload_algidR == 0x26) || //&& state->payload_keyidR != 0
(state->straight_ks == 0x26 && state->forced_alg_id == 0x26) )
{
if (memcmp(ambe_d, ambe_silence, 49) == 0)
state->static_ks_counter[state->currentslot] += 56;
else
{
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%state->straight_mod] & 1); //Yikes!
state->static_ks_counter[state->currentslot] += 7;
}
}
//DMR and P25p2 DES-OFB 56 Handling, Slot 2, VCH 1 -- Consider moving into AES handler //DMR and P25p2 DES-OFB 56 Handling, Slot 2, VCH 1 -- Consider moving into AES handler
if ( (state->currentslot == 1 && state->payload_algidR == 0x22 && state->RR != 0) || if ( (state->currentslot == 1 && state->payload_algidR == 0x22 && state->RR != 0) ||
(state->currentslot == 1 && state->payload_algidR == 0x81 && state->RR != 0) ) (state->currentslot == 1 && state->payload_algidR == 0x81 && state->RR != 0) )
@@ -1210,8 +1279,8 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
(state->currentslot == 1 && state->payload_algidR == 0x25 && state->aes_key_loaded[1] == 1 ) || //DMR AES256 (state->currentslot == 1 && state->payload_algidR == 0x25 && state->aes_key_loaded[1] == 1 ) || //DMR AES256
(state->currentslot == 1 && state->payload_algidR == 0x89 && state->aes_key_loaded[1] == 1 ) || //P25 AES128 (state->currentslot == 1 && state->payload_algidR == 0x89 && state->aes_key_loaded[1] == 1 ) || //P25 AES128
(state->currentslot == 1 && state->payload_algidR == 0x84 && state->aes_key_loaded[1] == 1 ) || //P25 AES256 (state->currentslot == 1 && state->payload_algidR == 0x84 && state->aes_key_loaded[1] == 1 ) || //P25 AES256
(state->currentslot == 1 && state->payload_algidR == 0x36 && state->aes_key_loaded[1] == 1 ) || //KIRI ADV (state->currentslot == 1 && state->payload_algidR >= 0x31 && \
(state->currentslot == 1 && state->payload_algidR == 0x37 && state->aes_key_loaded[1] == 1 ) || //KIRI UNI state->payload_algidR <= 0x3A && state->aes_key_loaded[1] == 1 ) || //KIRI Any
(state->currentslot == 1 && state->payload_algidR == 0x02 && state->RR != 0 ) ) //HYT ENHANCED (state->currentslot == 1 && state->payload_algidR == 0x02 && state->RR != 0 ) ) //HYT ENHANCED
{ {
@@ -1244,19 +1313,13 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if(state->payload_algidR == 0x02) if(state->payload_algidR == 0x02)
{ {
n = 0; n = 0;
hytera_enhanced_rc4_setup(opts, state, state->RR, state->payload_miR); hytera_enhanced_rc4_setup(opts, state, state->RR, state->payload_miR, 135);
} }
if (state->payload_algidR == 0x36) if (state->payload_algidR >= 0x31 && state->payload_algidR <= 0x3A)
{ {
n = 0; n = 0;
kirisun_adv_keystream_creation(state); kirisun_keystream_creation(state);
} }
if (state->payload_algidR == 0x37)
{
n = 0;
kirisun_uni_keystream_creation(state);
}
//Load Keystream Octet Bytes directly into keystream array //Load Keystream Octet Bytes directly into keystream array
for (i = 0; i < 9 * 16; i++) //9 rounds at 16 octets //TODO: Convert to unpack function for (i = 0; i < 9 * 16; i++) //9 rounds at 16 octets //TODO: Convert to unpack function
{ {
@@ -1482,6 +1545,9 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
state->payload_algidR = 0; state->payload_algidR = 0;
for (int i = 0; i < 49; i++) for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%state->straight_mod] & 1); //Yikes! ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%state->straight_mod] & 1); //Yikes!
//apply optional discard (i.e., caltta bp)
if (state->straight_discard != 0)
state->static_ks_counter[state->currentslot] += state->straight_discard;
} }
} }
@@ -1538,6 +1604,9 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if (state->ken_sc == 1) if (state->ken_sc == 1)
state->dmr_encL = 0; state->dmr_encL = 0;
if (state->payload_algid == 0x26 && state->straight_ks == 0x26)
state->dmr_encL = 0;
//reverse mute testing, only mute unencrypted traffic (slave piggyback dsd+ method) //reverse mute testing, only mute unencrypted traffic (slave piggyback dsd+ method)
if (opts->reverse_mute == 1) if (opts->reverse_mute == 1)
{ {
@@ -1618,6 +1687,9 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if (state->ken_sc == 1) if (state->ken_sc == 1)
state->dmr_encR = 0; state->dmr_encR = 0;
if (state->payload_algidR == 0x26 && state->straight_ks == 0x26)
state->dmr_encR = 0;
//reverse mute testing, only mute unencrypted traffic (slave piggyback dsd+ method) //reverse mute testing, only mute unencrypted traffic (slave piggyback dsd+ method)
if (opts->reverse_mute == 1) if (opts->reverse_mute == 1)
{ {

View File

@@ -244,7 +244,9 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (opts->audio_in_type == 3) if (opts->audio_in_type == 3)
{ {
printw ("| RTL: %d;", opts->rtl_dev_index); if (opts->rtltcp_enabled == 1)
printw ("| RTL_TCP: %s:%d;", opts->rtltcp_hostname, opts->rtltcp_portno);
else printw ("| RTL: %d;", opts->rtl_dev_index);
if (opts->rtl_gain_actual == -100) if (opts->rtl_gain_actual == -100)
printw (" G: AGC;"); printw (" G: AGC;");
else else
@@ -921,10 +923,16 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw ("Freq: %.06lf MHz", (double)state->p25_cc_freq/1000000); 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) else if (lls == 32 || lls == 33 || lls == 34)
{ {
printw ("DMR MS - DCC: %02i; ", state->dmr_color_code); printw ("DMR MS - DCC: %02i; ", state->dmr_color_code);
if (state->dmr_is_ras == 1)
printw ("{RAS}");
} }
else if (lls == 0 || lls == 1) //P1 else if (lls == 0 || lls == 1) //P1
{ {
@@ -1104,6 +1112,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); } if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); }
attron(COLOR_PAIR(3)); attron(COLOR_PAIR(3));
} }
if (state->payload_algid == 0x26)
{
attron(COLOR_PAIR(1));
printw("Caltta BP ");
// if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); } //TODO: This
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 0x02) if (state->payload_algid == 0x02)
{ {
attron(COLOR_PAIR(1)); attron(COLOR_PAIR(1));
@@ -1112,26 +1127,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw(" Key: %010llX", state->R); printw(" Key: %010llX", state->R);
attron(COLOR_PAIR(3)); attron(COLOR_PAIR(3));
} }
if (state->payload_algid == 0x36) if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
{ {
attron(COLOR_PAIR(1)); attron(COLOR_PAIR(1));
printw("Kirisun Advanced "); printw("Kirisun Encryption ");
if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); } if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); }
attron(COLOR_PAIR(3)); 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"); printw ("\n");
@@ -1299,6 +1301,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); } if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); }
attron(COLOR_PAIR(3)); attron(COLOR_PAIR(3));
} }
if (state->payload_algidR == 0x26)
{
attron(COLOR_PAIR(1));
printw("Caltta BP ");
// if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); } //TODO: This
attron(COLOR_PAIR(3));
}
if (state->payload_algidR == 0x02) if (state->payload_algidR == 0x02)
{ {
attron(COLOR_PAIR(1)); attron(COLOR_PAIR(1));
@@ -1307,26 +1316,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw(" Key: %010llX", state->RR); printw(" Key: %010llX", state->RR);
attron(COLOR_PAIR(3)); attron(COLOR_PAIR(3));
} }
if (state->payload_algidR == 0x36) if (state->payload_algidR >= 0x31 && state->payload_algidR <= 0x3A)
{ {
attron(COLOR_PAIR(1)); attron(COLOR_PAIR(1));
printw("Kirisun Advanced "); printw("Kirisun Advanced ");
if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); } if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); }
attron(COLOR_PAIR(3)); 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"); printw ("\n");

View File

@@ -314,3 +314,25 @@ rs_12_9_checksum_t *rs_12_9_calc_checksum(rs_12_9_codeword_t *codeword) {
} }
return &rs_12_9_checksum; 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));
//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];
}

View File

@@ -48,6 +48,7 @@ static int atan_lut_size = 131072; /* 512 KB */
static int atan_lut_coef = 8; static int atan_lut_coef = 8;
//UDP -- keep for compatibility reasons //UDP -- keep for compatibility reasons
#include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
static pthread_t socket_freq; static pthread_t socket_freq;
@@ -453,6 +454,9 @@ void dc_block_filter(struct demod_state *fm)
{ {
int i, avg; int i, avg;
int64_t sum = 0; int64_t sum = 0;
if (fm->result_len == 0) {
return;
}
for (i=0; i < fm->result_len; i++) { for (i=0; i < fm->result_len; i++) {
sum += fm->result[i]; sum += fm->result[i];
} }
@@ -567,7 +571,7 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx)
static void *dongle_thread_fn(void *arg) static void *dongle_thread_fn(void *arg)
{ {
struct dongle_state *s = static_cast<dongle_state*>(arg); struct dongle_state *s = static_cast<dongle_state*>(arg);
rtlsdr_read_async(s->dev, rtlsdr_callback, s, 0, s->buf_len); rtlsdr_or_tcp_read_async(s->dev, rtlsdr_callback, s, 0, s->buf_len);
return 0; return 0;
} }
@@ -604,17 +608,17 @@ int nearest_gain(rtlsdr_dev_t *dev, int target_gain)
{ {
int i, r, err1, err2, count, nearest; int i, r, err1, err2, count, nearest;
int* gains; int* gains;
r = rtlsdr_set_tuner_gain_mode(dev, 1); r = rtlsdr_or_tcp_set_tuner_gain_mode(dev, 1);
if (r < 0) { if (r < 0) {
fprintf (stderr, "WARNING: Failed to enable manual gain.\n"); fprintf (stderr, "WARNING: Failed to enable manual gain.\n");
return r; return r;
} }
count = rtlsdr_get_tuner_gains(dev, NULL); count = rtlsdr_or_tcp_get_tuner_gains(dev, NULL);
if (count <= 0) { if (count <= 0) {
return 0; return 0;
} }
gains = static_cast<int*>(malloc(sizeof(int) * count)); gains = static_cast<int*>(malloc(sizeof(int) * count));
count = rtlsdr_get_tuner_gains(dev, gains); count = rtlsdr_or_tcp_get_tuner_gains(dev, gains);
nearest = gains[0]; nearest = gains[0];
for (i=0; i<count; i++) { for (i=0; i<count; i++) {
err1 = abs(target_gain - nearest); err1 = abs(target_gain - nearest);
@@ -629,20 +633,28 @@ int nearest_gain(rtlsdr_dev_t *dev, int target_gain)
int verbose_set_frequency(rtlsdr_dev_t *dev, uint32_t frequency) int verbose_set_frequency(rtlsdr_dev_t *dev, uint32_t frequency)
{ {
int r; int r = -1;
r = rtlsdr_set_center_freq(dev, frequency);
if (r < 0) { //spam tuning
fprintf (stderr, " (WARNING: Failed to set Center Frequency). \n"); for (int i = 0; i < 20; i++)
} else { {
fprintf (stderr, " (Center Frequency: %u Hz.) \n", frequency); r = rtlsdr_or_tcp_set_center_freq(dev, frequency);
if (r < 0)
fprintf (stderr, " (RETRY: Failed to set Center Frequency %u). \n", dongle.freq);
else
{
fprintf (stderr, " (Center Frequency: %u Hz.) \n", frequency);
break;
}
} }
return r; return r;
} }
int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate)
{ {
int r; int r;
r = rtlsdr_set_sample_rate(dev, samp_rate); r = rtlsdr_or_tcp_set_sample_rate(dev, samp_rate);
if (r < 0) { if (r < 0) {
fprintf (stderr, "WARNING: Failed to set sample rate.\n"); fprintf (stderr, "WARNING: Failed to set sample rate.\n");
} else { } else {
@@ -654,7 +666,7 @@ int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate)
int verbose_direct_sampling(rtlsdr_dev_t *dev, int on) int verbose_direct_sampling(rtlsdr_dev_t *dev, int on)
{ {
int r; int r;
r = rtlsdr_set_direct_sampling(dev, on); r = rtlsdr_or_tcp_set_direct_sampling(dev, on);
if (r != 0) { if (r != 0) {
fprintf (stderr, "WARNING: Failed to set direct sampling mode.\n"); fprintf (stderr, "WARNING: Failed to set direct sampling mode.\n");
return r; return r;
@@ -671,7 +683,7 @@ int verbose_direct_sampling(rtlsdr_dev_t *dev, int on)
int verbose_offset_tuning(rtlsdr_dev_t *dev) int verbose_offset_tuning(rtlsdr_dev_t *dev)
{ {
int r; int r;
r = rtlsdr_set_offset_tuning(dev, 1); r = rtlsdr_or_tcp_set_offset_tuning(dev, 1);
if (r != 0) { if (r != 0) {
fprintf (stderr, "WARNING: Failed to set offset tuning.\n"); fprintf (stderr, "WARNING: Failed to set offset tuning.\n");
} else { } else {
@@ -683,7 +695,7 @@ int verbose_offset_tuning(rtlsdr_dev_t *dev)
int verbose_auto_gain(rtlsdr_dev_t *dev) int verbose_auto_gain(rtlsdr_dev_t *dev)
{ {
int r; int r;
r = rtlsdr_set_tuner_gain_mode(dev, 0); r = rtlsdr_or_tcp_set_tuner_gain_mode(dev, 0);
if (r != 0) { if (r != 0) {
fprintf (stderr, "WARNING: Failed to set tuner gain.\n"); fprintf (stderr, "WARNING: Failed to set tuner gain.\n");
} else { } else {
@@ -695,12 +707,12 @@ int verbose_auto_gain(rtlsdr_dev_t *dev)
int verbose_gain_set(rtlsdr_dev_t *dev, int gain) int verbose_gain_set(rtlsdr_dev_t *dev, int gain)
{ {
int r; int r;
r = rtlsdr_set_tuner_gain_mode(dev, 1); r = rtlsdr_or_tcp_set_tuner_gain_mode(dev, 1);
if (r < 0) { if (r < 0) {
fprintf (stderr, "WARNING: Failed to enable manual gain.\n"); fprintf (stderr, "WARNING: Failed to enable manual gain.\n");
return r; return r;
} }
r = rtlsdr_set_tuner_gain(dev, gain); r = rtlsdr_or_tcp_set_tuner_gain(dev, gain);
if (r != 0) { if (r != 0) {
fprintf (stderr, "WARNING: Failed to set tuner gain.\n"); fprintf (stderr, "WARNING: Failed to set tuner gain.\n");
} else { } else {
@@ -714,7 +726,7 @@ int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error)
int r; int r;
// if (ppm_error == 0) { // if (ppm_error == 0) {
// return 0;} // return 0;}
r = rtlsdr_set_freq_correction(dev, ppm_error); r = rtlsdr_or_tcp_set_freq_correction(dev, ppm_error);
if (r < 0) { if (r < 0) {
fprintf (stderr, "WARNING: Failed to set ppm error.\n"); fprintf (stderr, "WARNING: Failed to set ppm error.\n");
} else { } else {
@@ -726,7 +738,7 @@ int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error)
int verbose_reset_buffer(rtlsdr_dev_t *dev) int verbose_reset_buffer(rtlsdr_dev_t *dev)
{ {
int r; int r;
r = rtlsdr_reset_buffer(dev); r = rtlsdr_or_tcp_reset_buffer(dev);
if (r < 0) { if (r < 0) {
fprintf (stderr, "WARNING: Failed to reset buffers.\n");} fprintf (stderr, "WARNING: Failed to reset buffers.\n");}
return r; return r;
@@ -799,7 +811,7 @@ static void *controller_thread_fn(void *arg)
/* hacky hopping */ /* hacky hopping */
s->freq_now = (s->freq_now + 1) % s->freq_len; s->freq_now = (s->freq_now + 1) % s->freq_len;
optimal_settings(s->freqs[s->freq_now], demod.rate_in); optimal_settings(s->freqs[s->freq_now], demod.rate_in);
rtlsdr_set_center_freq(dongle.dev, dongle.freq); rtlsdr_or_tcp_set_center_freq(dongle.dev, dongle.freq);
dongle.mute = BUFFER_DUMP; dongle.mute = BUFFER_DUMP;
} }
return 0; return 0;
@@ -1004,7 +1016,7 @@ static void *socket_thread_fn(void *arg) {
new_freq = chars_to_int(buffer); new_freq = chars_to_int(buffer);
dongle.freq = new_freq; dongle.freq = new_freq;
optimal_settings(new_freq, demod.rate_in); optimal_settings(new_freq, demod.rate_in);
rtlsdr_set_center_freq(dongle.dev, dongle.freq); rtlsdr_or_tcp_set_center_freq(dongle.dev, dongle.freq);
fprintf (stderr, "\nTuning to: %d [Hz] \n", new_freq); fprintf (stderr, "\nTuning to: %d [Hz] \n", new_freq);
} }
@@ -1019,7 +1031,7 @@ static void *socket_thread_fn(void *arg) {
void rtlsdr_sighandler() void rtlsdr_sighandler()
{ {
fprintf (stderr, "Signal caught, exiting!\n"); fprintf (stderr, "Signal caught, exiting!\n");
rtlsdr_cancel_async(dongle.dev); rtlsdr_or_tcp_cancel_async(dongle.dev);
} }
void open_rtlsdr_stream(dsd_opts *opts) void open_rtlsdr_stream(dsd_opts *opts)
@@ -1072,14 +1084,23 @@ void open_rtlsdr_stream(dsd_opts *opts)
ACTUAL_BUF_LENGTH = lcm_post[demod.post_downsample] * DEFAULT_BUF_LENGTH; ACTUAL_BUF_LENGTH = lcm_post[demod.post_downsample] * DEFAULT_BUF_LENGTH;
r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index); if (opts->rtltcp_enabled == 1)
if (r < 0)
{ {
fprintf (stderr, "Failed to open rtlsdr device %d.\n", dongle.dev_index); if (rtltcp_open(opts->rtltcp_hostname, opts->rtltcp_portno) < 0)
exit(1); //rtltcp_open prints its own error messages
} else { exit(1);
fprintf (stderr, "Using RTLSDR Device Index: %d. \n", dongle.dev_index); }
} else
{
r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index);
if (r < 0)
{
fprintf (stderr, "Failed to open rtlsdr device %d.\n", dongle.dev_index);
exit(1);
} else {
fprintf (stderr, "Using RTLSDR Device Index: %d. \n", dongle.dev_index);
}
}
if (demod.deemph) { if (demod.deemph) {
demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * 75e-6))))); demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * 75e-6)))));
@@ -1111,7 +1132,7 @@ void open_rtlsdr_stream(dsd_opts *opts)
void cleanup_rtlsdr_stream() void cleanup_rtlsdr_stream()
{ {
fprintf (stderr, "cleaning up...\n"); fprintf (stderr, "cleaning up...\n");
rtlsdr_cancel_async(dongle.dev); rtlsdr_or_tcp_cancel_async(dongle.dev);
pthread_join(dongle.thread, NULL); pthread_join(dongle.thread, NULL);
safe_cond_signal(&demod.ready, &demod.ready_m); safe_cond_signal(&demod.ready, &demod.ready_m);
pthread_join(demod.thread, NULL); pthread_join(demod.thread, NULL);
@@ -1124,7 +1145,7 @@ void cleanup_rtlsdr_stream()
output_cleanup(&output); output_cleanup(&output);
controller_cleanup(&controller); controller_cleanup(&controller);
rtlsdr_close(dongle.dev); rtlsdr_or_tcp_close(dongle.dev);
} }
//original for safe keeping //original for safe keeping
@@ -1149,7 +1170,15 @@ int get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state)
if (opts->rtlsdr_ppm_error != dongle.ppm_error) if (opts->rtlsdr_ppm_error != dongle.ppm_error)
{ {
dongle.ppm_error = opts->rtlsdr_ppm_error; dongle.ppm_error = opts->rtlsdr_ppm_error;
verbose_ppm_set(dongle.dev, dongle.ppm_error);
for (int i = 0; i < 20; i++)
{
int r = -1;
r = verbose_ppm_set(dongle.dev, dongle.ppm_error);
if (r < 0)
fprintf (stderr, " Retry Gain.");
else break;
}
} }
//if Gain or AGC Value is Manually Changed, Change it here now //if Gain or AGC Value is Manually Changed, Change it here now
@@ -1157,7 +1186,15 @@ int get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state)
{ {
dongle.gain = (opts->rtl_gain_value * 10); dongle.gain = (opts->rtl_gain_value * 10);
opts->rtl_gain_actual = dongle.gain = nearest_gain(dongle.dev, dongle.gain); opts->rtl_gain_actual = dongle.gain = nearest_gain(dongle.dev, dongle.gain);
verbose_gain_set(dongle.dev, dongle.gain);
for (int i = 0; i < 20; i++)
{
int r = -1;
r = verbose_gain_set(dongle.dev, dongle.gain);
if (r < 0)
fprintf (stderr, " Retry Gain.");
else break;
}
//debug //debug
// fprintf (stderr, "\n Dongle Gain Set: %d; \n", dongle.gain); // fprintf (stderr, "\n Dongle Gain Set: %d; \n", dongle.gain);
@@ -1166,7 +1203,15 @@ int get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state)
else if (opts->rtl_gain_value == 0 && dongle.gain != AUTO_GAIN) else if (opts->rtl_gain_value == 0 && dongle.gain != AUTO_GAIN)
{ {
opts->rtl_gain_actual = dongle.gain = AUTO_GAIN; opts->rtl_gain_actual = dongle.gain = AUTO_GAIN;
verbose_auto_gain(dongle.dev);
for (int i = 0; i < 20; i++)
{
int r = 1;
r = verbose_auto_gain(dongle.dev);
if (r < 0)
fprintf (stderr, " Retry Gain.");
else break;
}
//debug //debug
// fprintf (stderr, "\n AGC Set; \n"); // fprintf (stderr, "\n AGC Set; \n");
@@ -1197,16 +1242,23 @@ int get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state)
//function may lag since it isn't running as its own thread //function may lag since it isn't running as its own thread
void rtl_dev_tune(dsd_opts * opts, long int frequency) void rtl_dev_tune(dsd_opts * opts, long int frequency)
{ {
int r;
if (opts->payload == 1) if (opts->payload == 1)
fprintf (stderr, "\nTuning to %lu Hz.", frequency); fprintf (stderr, "\nTuning to %lu Hz.", frequency);
dongle.freq = opts->rtlsdr_center_freq = frequency; dongle.freq = opts->rtlsdr_center_freq = frequency;
optimal_settings(dongle.freq, demod.rate_in); optimal_settings(dongle.freq, demod.rate_in); //this too?
if (opts->payload == 1) if (opts->payload == 1)
fprintf (stderr, " (Center Frequency: %u Hz.) \n", dongle.freq); fprintf (stderr, " (Center Frequency: %u Hz.) \n", dongle.freq);
r = rtlsdr_set_center_freq(dongle.dev, dongle.freq);
if (r < 0) //spam tuning
fprintf (stderr, " (WARNING: Failed to set Center Frequency %u). \n", dongle.freq); for (int i = 0; i < 20; i++)
{
int r = -1;
r = rtlsdr_or_tcp_set_center_freq(dongle.dev, dongle.freq);
if (r < 0)
fprintf (stderr, " (RETRY: Failed to set Center Frequency %u). \n", dongle.freq);
else break;
}
pthread_rwlock_wrlock(&output.rw); //prevent possible segfault when cleaning the queue pthread_rwlock_wrlock(&output.rw); //prevent possible segfault when cleaning the queue
rtl_clean_queue(); rtl_clean_queue();

331
src/rtl_sdr_tcp.c Normal file
View File

@@ -0,0 +1,331 @@
/*-------------------------------------------------------------------------------
* rtl_sdr_tcp.c
* rtl_tcp client for the RTL-SDR input: samples arrive on a TCP socket, and tuner commands are sent back on it
*-----------------------------------------------------------------------------*/
#include "dsd.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <time.h>
//MSG_NOSIGNAL is missing before macOS 11; SO_NOSIGPIPE in rtltcp_open keeps send from raising SIGPIPE there
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
//rtlsdr_read_async's default block size, in bytes
#define RTLTCP_BUF_LENGTH (16 * 32 * 512)
//rtl_tcp state: socket, tuner type, and a mutex that keeps commands from different threads from interleaving
//NOTE: Keeping these as static global is not great. They really should be in some kind of opaque handle
// like rtl_tcp_dev_t or something. For applications that only use one device at a time, this is fine.
// But if we ever move to supporting multiple simultaneous RTL tuners, this will need to change.
static int rtltcp_sock = -1;
static int rtltcp_tuner_type = RTLSDR_TUNER_UNKNOWN; //from the server's greeting
static pthread_mutex_t rtltcp_m = PTHREAD_MUTEX_INITIALIZER;
//limit for connecting to each address, reading the greeting, and sending a command, so a stalled server can't hang dsd-fme
static const int RTLTCP_TIMEOUT_MS = 5000;
static int64_t rtltcp_now_ms(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
//Poll in short intervals so shutdown works even when the server stops responding.
//A negative deadline permits an idle sample stream; setup and commands have fixed deadlines.
static int rtltcp_wait(int sock, short events, int64_t deadline)
{
struct pollfd pfd = {sock, events, 0};
while (!exitflag) {
int timeout = 100;
if (deadline >= 0) {
//check if we have exceeded our wait time
int64_t remaining = deadline - rtltcp_now_ms();
if (remaining <= 0) {
errno = ETIMEDOUT;
return -1;
}
//set the next poll timeout
if (remaining < timeout) timeout = (int)remaining;
}
//poll and handle the result
int r = poll(&pfd, 1, timeout);
if (r > 0) return 0; //recv/send/getsockopt will report EOF or socket errors
if (r < 0 && errno != EINTR) return -1;
}
errno = EINTR;
return -1;
}
//rtl_tcp command bytes, each named after the librtlsdr function the server calls with the parameter
enum
{
RTLTCP_SET_CENTER_FREQ = 0x01,
RTLTCP_SET_SAMPLE_RATE = 0x02,
RTLTCP_SET_TUNER_GAIN_MODE = 0x03,
RTLTCP_SET_TUNER_GAIN = 0x04,
RTLTCP_SET_FREQ_CORRECTION = 0x05,
RTLTCP_SET_DIRECT_SAMPLING = 0x09,
RTLTCP_SET_OFFSET_TUNING = 0x0a
};
//send one rtl_tcp command: a command byte followed by a 32-bit big-endian parameter.
//A partly sent command would misalign every later one, so a failed send ends the connection.
static int rtltcp_command(uint8_t cmd, uint32_t param)
{
//encode command and parameter to send to rtl_tcp
unsigned char buf[5] = {cmd, (unsigned char)(param >> 24), (unsigned char)(param >> 16), (unsigned char)(param >> 8), (unsigned char)param};
size_t sent = 0;
pthread_mutex_lock(&rtltcp_m);
//non-blocking send with a timeout
int64_t deadline = rtltcp_now_ms() + RTLTCP_TIMEOUT_MS;
while (sent < 5) {
if (rtltcp_wait(rtltcp_sock, POLLOUT, deadline) < 0) break;
ssize_t n = send(rtltcp_sock, buf + sent, 5 - sent, MSG_NOSIGNAL);
if (n < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) {
continue;
}
if (n <= 0) {
break;
}
sent += n;
}
//a failed or partial send ends the connection
if (sent < 5) {
shutdown(rtltcp_sock, SHUT_RDWR);
}
pthread_mutex_unlock(&rtltcp_m);
return sent < 5 ? -1 : 0;
}
//connect to an rtl_tcp server over IPv4 or IPv6, read its greeting, and store the socket in rtltcp_sock; prints the outcome and returns 0, or -1 on failure
int rtltcp_open(const char *hostname, int portno)
{
struct addrinfo hints, *res, *ai;
char portstr[16];
unsigned char header[12];
int sock = -1;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
snprintf(portstr, sizeof(portstr), "%d", portno);
if (getaddrinfo(hostname, portstr, &hints, &res) != 0) {
fprintf (stderr, "Failed to resolve rtl_tcp server %s.\n", hostname);
return -1;
}
//try each resolved address in turn until one connects
for (ai = res; ai != NULL && !exitflag; ai = ai->ai_next) {
//socket creation
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0) {
continue;
}
//set nonblocking
int flags = fcntl(sock, F_GETFL, 0);
if (flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
close(sock);
sock = -1;
continue;
}
//connect
int r = connect(sock, ai->ai_addr, ai->ai_addrlen);
//connect returns immediately; wait for the attempt to finish, then SO_ERROR says whether it succeeded
if (r < 0 && errno == EINPROGRESS && rtltcp_wait(sock, POLLOUT, rtltcp_now_ms() + RTLTCP_TIMEOUT_MS) == 0) {
int error = 0;
socklen_t len = sizeof(error);
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) == 0 && error == 0) r = 0;
}
//successful connection
if (r == 0 && !exitflag) {
break;
}
//close socket and retry with different address
close(sock);
sock = -1;
}
freeaddrinfo(res);
if (sock < 0) {
fprintf (stderr, "Failed to connect to rtl_tcp server %s:%d.\n", hostname, portno);
return -1;
}
#ifdef SO_NOSIGPIPE
//macOS and the BSDs: a send to a closed connection returns an error instead of raising SIGPIPE
int one = 1;
setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
#endif
size_t received = 0;
//non-blocking receive with a timeout
int64_t deadline = rtltcp_now_ms() + RTLTCP_TIMEOUT_MS;
while (received < sizeof(header)) {
if (rtltcp_wait(sock, POLLIN, deadline) < 0) break;
ssize_t n = recv(sock, header + received, sizeof(header) - received, 0);
if (n < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) continue;
if (n <= 0) break;
received += n;
}
//the greeting is "RTL0", the tuner type, and the number of tuner gain steps
if (received != sizeof(header) || memcmp(header, "RTL0", 4) != 0) {
fprintf (stderr, "%s:%d did not send an rtl_tcp greeting.\n", hostname, portno);
close(sock);
return -1;
}
//socket is valid and ready to use
rtltcp_sock = sock;
//store tuner type for later use in rtltcp_get_tuner_gains
rtltcp_tuner_type = ((uint32_t)header[4] << 24) | ((uint32_t)header[5] << 16) | ((uint32_t)header[6] << 8) | header[7];
fprintf (stderr, "Using rtl_tcp server %s:%d. \n", hostname, portno);
return 0;
}
//rtlsdr_get_tuner_gains for the server's tuner: copies its gain steps, in tenths of a dB, into gains unless gains is NULL, and returns the number of steps
static int rtltcp_get_tuner_gains(int *gains)
{
//rtl_tcp advertises only the gain count, not the values; librtlsdr's gain query requires a local device.
//Unfortunately this means we have to copy librtlsdr's tables here to select the nearest valid gain given the remote device.
//It would be better if rtl_tcp told us the actual list of supported gains, but it doesn't.
static const int e4k[] = { -10, 15, 40, 65, 90, 115, 140, 165, 190, 215, 240, 290, 340, 420 };
static const int fc0012[] = { -99, -40, 71, 179, 192 };
static const int fc0013[] = { -99, -73, -65, -63, -60, -58, -54, 58, 61, 63, 65, 67, 68, 70, 71, 179, 181, 182, 184, 186, 188, 191, 197 };
static const int r82xx[] = { 0, 9, 14, 27, 37, 77, 87, 125, 144, 157, 166, 197, 207, 229, 254, 280, 297, 328, 338, 364, 372, 386, 402, 421, 434, 439, 445, 480, 496 };
static const int none[] = { 0 };
const int *table = none;
int count = 1;
//different tuners have different possible gains
switch (rtltcp_tuner_type) {
case RTLSDR_TUNER_E4000: table = e4k; count = sizeof(e4k) / sizeof(int); break;
case RTLSDR_TUNER_FC0012: table = fc0012; count = sizeof(fc0012) / sizeof(int); break;
case RTLSDR_TUNER_FC0013: table = fc0013; count = sizeof(fc0013) / sizeof(int); break;
case RTLSDR_TUNER_R820T:
case RTLSDR_TUNER_R828D: table = r82xx; count = sizeof(r82xx) / sizeof(int); break;
}
if (gains != NULL) {
memcpy(gains, table, count * sizeof(int));
}
return count;
}
//close the rtl_tcp connection; returns close's result
static int rtltcp_close(void)
{
int r = close(rtltcp_sock);
rtltcp_sock = -1;
return r;
}
//pass rtl_tcp samples to cb until the connection closes or exitflag is set, in the same block size that rtlsdr_read_async delivers by default
static int rtltcp_read_async(rtlsdr_read_async_cb_t cb, void *ctx)
{
static unsigned char buf[RTLTCP_BUF_LENGTH];
size_t received = 0;
//receive until exit is requested or the connection ends; no deadline, so an idle stream can wait indefinitely
while (rtltcp_wait(rtltcp_sock, POLLIN, -1) == 0) {
ssize_t n = recv(rtltcp_sock, buf + received, sizeof(buf) - received, 0);
if (n < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) continue;
if (n <= 0) break;
received += n;
if (received == sizeof(buf)) {
cb(buf, sizeof(buf), ctx);
received = 0;
}
}
//we are finished (for a variety of reasons). ensure exitflag is set
if (!exitflag) {
fprintf (stderr, "rtl_tcp server closed the connection.\n");
exitflag = 1;
}
return 0;
}
//Each rtlsdr_or_tcp_* function takes the arguments of the librtlsdr function it is named after.
//While an rtl_tcp server is connected, the call goes to the server; otherwise it goes to librtlsdr.
int rtlsdr_or_tcp_close(rtlsdr_dev_t *dev)
{
return rtltcp_sock >= 0 ? rtltcp_close() : rtlsdr_close(dev);
}
int rtlsdr_or_tcp_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len)
{
return rtltcp_sock >= 0 ? rtltcp_read_async(cb, ctx) : rtlsdr_read_async(dev, cb, ctx, buf_num, buf_len);
}
int rtlsdr_or_tcp_cancel_async(rtlsdr_dev_t *dev)
{
return rtltcp_sock >= 0 ? shutdown(rtltcp_sock, SHUT_RDWR) : rtlsdr_cancel_async(dev);
}
int rtlsdr_or_tcp_reset_buffer(rtlsdr_dev_t *dev)
{
return rtltcp_sock >= 0 ? 0 : rtlsdr_reset_buffer(dev);
}
int rtlsdr_or_tcp_get_tuner_gains(rtlsdr_dev_t *dev, int *gains)
{
return rtltcp_sock >= 0 ? rtltcp_get_tuner_gains(gains) : rtlsdr_get_tuner_gains(dev, gains);
}
int rtlsdr_or_tcp_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_CENTER_FREQ, freq) : rtlsdr_set_center_freq(dev, freq);
}
int rtlsdr_or_tcp_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_SAMPLE_RATE, rate) : rtlsdr_set_sample_rate(dev, rate);
}
int rtlsdr_or_tcp_set_direct_sampling(rtlsdr_dev_t *dev, int on)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_DIRECT_SAMPLING, on) : rtlsdr_set_direct_sampling(dev, on);
}
int rtlsdr_or_tcp_set_offset_tuning(rtlsdr_dev_t *dev, int on)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_OFFSET_TUNING, on) : rtlsdr_set_offset_tuning(dev, on);
}
int rtlsdr_or_tcp_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_TUNER_GAIN_MODE, manual) : rtlsdr_set_tuner_gain_mode(dev, manual);
}
int rtlsdr_or_tcp_set_tuner_gain(rtlsdr_dev_t *dev, int gain)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_TUNER_GAIN, gain) : rtlsdr_set_tuner_gain(dev, gain);
}
int rtlsdr_or_tcp_set_freq_correction(rtlsdr_dev_t *dev, int ppm)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_FREQ_CORRECTION, ppm) : rtlsdr_set_freq_correction(dev, ppm);
}

View File

@@ -595,7 +595,7 @@ void pn95_lfsr() //test to see if this generates the correct bits now
} }
void ysf_ehr (dsd_opts * opts, dsd_state * state, uint8_t dbuf[180], int start, int stop ) void ysf_ehr (dsd_opts * opts, dsd_state * state, uint8_t * dbuf, int start, int stop )
{ {
int i; int i;
char ambe_fr[4][24]; char ambe_fr[4][24];
@@ -850,7 +850,7 @@ void processYSF(dsd_opts * opts, dsd_state * state)
} }
// send VCH to ehr voice handler // send VCH to ehr voice handler
ysf_ehr (opts, state, vbuf, 0, 4); ysf_ehr (opts, state, vbuf, 0, 5);
//send DCH to decoder //send DCH to decoder
ysf_conv_dch (opts, state, bn, bt, fn, ft, cm, dbuf); ysf_conv_dch (opts, state, bn, bt, fn, ft, cm, dbuf);