Skip to content

Commit 8fb56a5

Browse files
authored
Merge pull request #1640 from DSheirer/1638-hytera-encryption-parameters
#1638 DMR Decoder - Encryption Parameters & Hytera RRS
2 parents 85b2a38 + 5d7b73f commit 8fb56a5

27 files changed

+662
-291
lines changed

src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.announcement.VoteNowAdvice;
5858
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.grant.ChannelGrant;
5959
import io.github.dsheirer.module.decode.dmr.message.data.header.HeaderMessage;
60+
import io.github.dsheirer.module.decode.dmr.message.data.header.hytera.HyteraDataEncryptionHeader;
6061
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCMessage;
6162
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.GPSInformation;
6263
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.GroupVoiceChannelUser;
@@ -74,13 +75,17 @@
7475
import io.github.dsheirer.module.decode.dmr.message.type.ServiceOptions;
7576
import io.github.dsheirer.module.decode.dmr.message.voice.VoiceEMBMessage;
7677
import io.github.dsheirer.module.decode.dmr.message.voice.VoiceMessage;
77-
import io.github.dsheirer.module.decode.dmr.message.voice.embedded.Arc4EncryptionParameters;
7878
import io.github.dsheirer.module.decode.dmr.message.voice.embedded.EmbeddedParameters;
79+
import io.github.dsheirer.module.decode.dmr.message.voice.embedded.EncryptionParameters;
7980
import io.github.dsheirer.module.decode.event.DecodeEvent;
8081
import io.github.dsheirer.module.decode.event.DecodeEventType;
8182
import io.github.dsheirer.module.decode.event.PlottableDecodeEvent;
8283
import io.github.dsheirer.module.decode.ip.hytera.sds.HyteraUnknownPacket;
84+
import io.github.dsheirer.module.decode.ip.hytera.shortdata.HyteraShortDataPacket;
8385
import io.github.dsheirer.module.decode.ip.hytera.sms.HyteraSmsPacket;
86+
import io.github.dsheirer.module.decode.ip.mototrbo.ars.ARSPacket;
87+
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.LRRPPacket;
88+
import io.github.dsheirer.module.decode.ip.mototrbo.xcmp.XCMPPacket;
8489
import io.github.dsheirer.protocol.Protocol;
8590
import io.github.dsheirer.source.tuner.channel.rotation.AddChannelRotationActiveStateRequest;
8691
import io.github.dsheirer.util.PacketUtil;
@@ -320,6 +325,32 @@ private void processPacket(DMRPacketMessage packet)
320325
.build();
321326
broadcast(smsEvent);
322327
}
328+
//Hytera Short Data - Radio Registration Service (RRS)
329+
else if(packet.getPacket() instanceof HyteraShortDataPacket hsdp)
330+
{
331+
MutableIdentifierCollection mic = new MutableIdentifierCollection(packet.getIdentifiers());
332+
333+
StringBuilder sb = new StringBuilder();
334+
sb.append("HYTERA");
335+
336+
if(hsdp.getPacketSequence().isEncrypted())
337+
{
338+
HyteraDataEncryptionHeader hdeh = (HyteraDataEncryptionHeader)hsdp.getPacketSequence().getProprietaryDataHeader();
339+
sb.append(" ENCRYPTED ALGORITHM:").append(hdeh.getAlgorithm());
340+
sb.append(" KEY:").append(hdeh.getKeyId());
341+
sb.append(" IV:").append(hdeh.getIV());
342+
}
343+
344+
sb.append(" SHORT DATA:").append(hsdp.getMessage().toHexString());
345+
346+
347+
DecodeEvent shortDataEvent = DMRDecodeEvent.builder(DecodeEventType.RADIO_REGISTRATION_SERVICE, packet.getTimestamp())
348+
.identifiers(mic)
349+
.timeslot(getTimeslot())
350+
.details(sb.toString())
351+
.build();
352+
broadcast(shortDataEvent);
353+
}
323354
//Unknown Hytera Long Data Service Token Message
324355
else if(packet.getPacket() instanceof HyteraUnknownPacket hyteraUnknownPacket)
325356
{
@@ -328,10 +359,46 @@ else if(packet.getPacket() instanceof HyteraUnknownPacket hyteraUnknownPacket)
328359
DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(DecodeEventType.UNKNOWN_PACKET, packet.getTimestamp())
329360
.identifiers(mic)
330361
.timeslot(getTimeslot())
331-
.details("HYTERA UNK TOKEN MSG:" + hyteraUnknownPacket.getHeader().toString())
362+
.details("HYTERA LONG DATA UNK TOKEN MSG:" + hyteraUnknownPacket.getHeader().toString())
332363
.build();
333364
broadcast(unknownTokenEvent);
334365
}
366+
//Motorola ARS
367+
else if(packet.getPacket() instanceof ARSPacket ars)
368+
{
369+
MutableIdentifierCollection mic = new MutableIdentifierCollection(packet.getIdentifiers());
370+
371+
DecodeEvent shortDataEvent = DMRDecodeEvent.builder(DecodeEventType.RADIO_REGISTRATION_SERVICE, packet.getTimestamp())
372+
.identifiers(mic)
373+
.timeslot(getTimeslot())
374+
.details(ars.toString())
375+
.build();
376+
broadcast(shortDataEvent);
377+
}
378+
//Motorola LRRP
379+
else if(packet.getPacket() instanceof LRRPPacket lrrp)
380+
{
381+
MutableIdentifierCollection mic = new MutableIdentifierCollection(packet.getIdentifiers());
382+
383+
DecodeEvent shortDataEvent = DMRDecodeEvent.builder(DecodeEventType.LRRP, packet.getTimestamp())
384+
.identifiers(mic)
385+
.timeslot(getTimeslot())
386+
.details(lrrp.toString())
387+
.build();
388+
broadcast(shortDataEvent);
389+
}
390+
//Motorola XCMP
391+
else if(packet.getPacket() instanceof XCMPPacket xcmp)
392+
{
393+
MutableIdentifierCollection mic = new MutableIdentifierCollection(packet.getIdentifiers());
394+
395+
DecodeEvent shortDataEvent = DMRDecodeEvent.builder(DecodeEventType.XCMP, packet.getTimestamp())
396+
.identifiers(mic)
397+
.timeslot(getTimeslot())
398+
.details(xcmp.toString())
399+
.build();
400+
broadcast(shortDataEvent);
401+
}
335402
else
336403
{
337404
DecodeEvent packetEvent = DMRDecodeEvent.builder(DecodeEventType.DATA_PACKET, packet.getTimestamp())
@@ -380,7 +447,7 @@ private void processVoice(VoiceMessage message)
380447
{
381448
EmbeddedParameters embedded = voiceEmb.getEmbeddedParameters();
382449

383-
if(embedded.getShortBurst() instanceof Arc4EncryptionParameters arc4)
450+
if(embedded.getShortBurst() instanceof EncryptionParameters arc4)
384451
{
385452
updateEncryptedCall(arc4, true, voiceEmb.getTimestamp());
386453
}
@@ -972,6 +1039,15 @@ private void processLinkControl(LCMessage message, boolean isTerminator)
9721039
{
9731040
switch(message.getOpcode())
9741041
{
1042+
case FULL_ENCRYPTION_PARAMETERS:
1043+
if(message instanceof io.github.dsheirer.module.decode.dmr.message.data.lc.full.EncryptionParameters ep)
1044+
{
1045+
if(mCurrentCallEvent != null)
1046+
{
1047+
mCurrentCallEvent.setDetails(ep.getDetails());
1048+
}
1049+
}
1050+
break;
9751051
case SHORT_CAPACITY_PLUS_REST_CHANNEL_NOTIFICATION:
9761052
if(message instanceof CapacityPlusRestChannel)
9771053
{
@@ -1141,7 +1217,7 @@ private void processLinkControl(LCMessage message, boolean isTerminator)
11411217
* @param encryptionParameters decoded from the Voice Frame F
11421218
* @param isGroup true for group or false for individual call.
11431219
*/
1144-
private void updateEncryptedCall(Arc4EncryptionParameters encryptionParameters, boolean isGroup, long timestamp)
1220+
private void updateEncryptedCall(EncryptionParameters encryptionParameters, boolean isGroup, long timestamp)
11451221
{
11461222
if(mCurrentCallEvent != null)
11471223
{
@@ -1151,7 +1227,7 @@ private void updateEncryptedCall(Arc4EncryptionParameters encryptionParameters,
11511227
{
11521228
details = encryptionParameters.toString();
11531229
}
1154-
else if(!details.contains(encryptionParameters.toString()))
1230+
else if(!details.contains(encryptionParameters.toString()) && !details.contains("ENCRYPTION"))
11551231
{
11561232
details += " " + encryptionParameters;
11571233
}

src/main/java/io/github/dsheirer/module/decode/dmr/message/data/DMRDataMessageFactory.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
import io.github.dsheirer.module.decode.dmr.message.data.header.ConfirmedDataHeader;
3434
import io.github.dsheirer.module.decode.dmr.message.data.header.DataHeader;
3535
import io.github.dsheirer.module.decode.dmr.message.data.header.DefinedShortDataHeader;
36-
import io.github.dsheirer.module.decode.dmr.message.data.header.HeaderMessage;
3736
import io.github.dsheirer.module.decode.dmr.message.data.header.MBCHeader;
37+
import io.github.dsheirer.module.decode.dmr.message.data.header.PiHeader;
3838
import io.github.dsheirer.module.decode.dmr.message.data.header.ProprietaryDataHeader;
3939
import io.github.dsheirer.module.decode.dmr.message.data.header.RawShortDataHeader;
4040
import io.github.dsheirer.module.decode.dmr.message.data.header.ResponseDataHeader;
@@ -43,7 +43,7 @@
4343
import io.github.dsheirer.module.decode.dmr.message.data.header.UDTHeader;
4444
import io.github.dsheirer.module.decode.dmr.message.data.header.UnconfirmedDataHeader;
4545
import io.github.dsheirer.module.decode.dmr.message.data.header.VoiceHeader;
46-
import io.github.dsheirer.module.decode.dmr.message.data.header.hytera.HyteraProprietaryDataHeader;
46+
import io.github.dsheirer.module.decode.dmr.message.data.header.hytera.HyteraDataEncryptionHeader;
4747
import io.github.dsheirer.module.decode.dmr.message.data.header.motorola.MNISProprietaryDataHeader;
4848
import io.github.dsheirer.module.decode.dmr.message.data.header.motorola.MotorolaDataEncryptionHeader;
4949
import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBlock;
@@ -98,7 +98,7 @@ public static DataMessage create(DMRSyncPattern pattern, CorrectedBinaryMessage
9898
return new MBCHeader(pattern, getPayload(message), cach, slotType, timestamp, timeslot);
9999
case CHANNEL_CONTROL_ENC_HEADER:
100100
case PI_HEADER:
101-
return new HeaderMessage(pattern, getPayload(message), cach, slotType, timestamp, timeslot);
101+
return new PiHeader(pattern, getPayload(message), cach, slotType, timestamp, timeslot);
102102
case VOICE_HEADER:
103103
return new VoiceHeader(pattern, getPayload(message), cach, slotType, timestamp, timeslot);
104104
case DATA_ENC_HEADER:
@@ -138,10 +138,10 @@ public static DataMessage create(DMRSyncPattern pattern, CorrectedBinaryMessage
138138
return mprdh;
139139
}
140140
case HYTERA_68:
141-
HyteraProprietaryDataHeader hpdh = new HyteraProprietaryDataHeader(pattern, payload,
142-
cach, slotType, timestamp, timeslot);
143-
hpdh.setValid(valid);
144-
return hpdh;
141+
HyteraDataEncryptionHeader hsdh = new HyteraDataEncryptionHeader(pattern, payload,
142+
cach, slotType, timestamp, timeslot);
143+
hsdh.setValid(valid);
144+
return hsdh;
145145
default:
146146
ProprietaryDataHeader pdh = new ProprietaryDataHeader(pattern, payload, cach,
147147
slotType, timestamp, timeslot);

src/main/java/io/github/dsheirer/module/decode/dmr/message/data/DataMessageWithLinkControl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
*/
3535
public abstract class DataMessageWithLinkControl extends DataMessage
3636
{
37-
private LCMessage mLCMessage;
37+
protected LCMessage mLCMessage;
3838

3939
/**
4040
* Constructs an instance.

src/main/java/io/github/dsheirer/module/decode/dmr/message/data/block/DataBlock1Rate.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ public String toString()
5656
{
5757
StringBuilder sb = new StringBuilder();
5858
sb.append("CC:").append(getSlotType().getColorCode());
59+
if(hasRAS())
60+
{
61+
sb.append(" RAS:").append(getBPTCReservedBits());
62+
}
5963
if(!isValid())
6064
{
6165
sb.append(" [CRC ERROR]");

src/main/java/io/github/dsheirer/module/decode/dmr/message/data/block/DataBlock1_2Rate.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ public String toString()
5656
{
5757
StringBuilder sb = new StringBuilder();
5858
sb.append("CC:").append(getSlotType().getColorCode());
59+
if(hasRAS())
60+
{
61+
sb.append(" RAS:").append(getBPTCReservedBits());
62+
}
5963
if(!isValid())
6064
{
6165
sb.append(" [CRC ERROR]");

src/main/java/io/github/dsheirer/module/decode/dmr/message/data/block/DataBlock3_4Rate.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ public String toString()
5757
{
5858
StringBuilder sb = new StringBuilder();
5959
sb.append("CC:").append(getSlotType().getColorCode());
60+
if(hasRAS())
61+
{
62+
sb.append(" RAS:").append(getBPTCReservedBits());
63+
}
6064
if(!isValid())
6165
{
6266
sb.append(" [CRC ERROR]");

src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/Opcode.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727

2828
/**
2929
* Control Signalling Block (CSBK) Opcode enumeration
30-
*
3130
* ETSI TS 102 361-2 DMR Voice & Generic Services, Annex B
3231
* ETSI TS 102 361-4 DMR Trunking Protocol, Annex B
3332
*/
@@ -91,6 +90,8 @@ public enum Opcode
9190

9291
HYTERA_08_ACKNOWLEDGE(Vendor.HYTERA_8, 32, "HYTERA 08 ACKNOWLEDGE"),
9392
HYTERA_08_ANNOUNCEMENT(Vendor.HYTERA_8, 40, "HYTERA 08 ANNOUNCEMENT"),
93+
//CSBKO 44 & 47 observed on Tier3 interleaved in voice group call terminator sequence. 44 was continuously transmitted
94+
//and 47 was only transmitted 3x times in succession in the middle of the terminators and CSBKO 44 messages.
9495
HYTERA_08_CSBKO_44(Vendor.HYTERA_8, 44, "HYTERA 08 CSBKO 44"),
9596
HYTERA_08_CSBKO_47(Vendor.HYTERA_8, 47, "HYTERA 08 CSBKO 47"),
9697

@@ -104,9 +105,9 @@ public enum Opcode
104105

105106
UNKNOWN(Vendor.UNKNOWN, -1, "UNKNOWN");
106107

107-
private Vendor mVendor;
108-
private int mValue;
109-
private String mLabel;
108+
private final Vendor mVendor;
109+
private final int mValue;
110+
private final String mLabel;
110111

111112
/**
112113
* Constructor
Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,19 @@
1717
* ****************************************************************************
1818
*/
1919

20-
package io.github.dsheirer.module.decode.dmr.message.data.header.hytera;
20+
package io.github.dsheirer.module.decode.dmr.message.data.header;
2121

2222
import io.github.dsheirer.bits.CorrectedBinaryMessage;
2323
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
2424
import io.github.dsheirer.module.decode.dmr.message.CACH;
2525
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
26-
import io.github.dsheirer.module.decode.dmr.message.data.header.ProprietaryDataHeader;
26+
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCMessage;
27+
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCMessageFactory;
2728

2829
/**
29-
* Hytera Proprietary Data Header
30+
* PI/Encryption Header with Link Control
3031
*/
31-
public class HyteraProprietaryDataHeader extends ProprietaryDataHeader
32+
public class PiHeader extends HeaderMessage
3233
{
3334
/**
3435
* Constructs an instance.
@@ -40,23 +41,42 @@ public class HyteraProprietaryDataHeader extends ProprietaryDataHeader
4041
* @param timestamp message was received
4142
* @param timeslot for the DMR burst
4243
*/
43-
public HyteraProprietaryDataHeader(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach, SlotType slotType, long timestamp, int timeslot)
44+
public PiHeader(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach, SlotType slotType, long timestamp, int timeslot)
4445
{
4546
super(syncPattern, message, cach, slotType, timestamp, timeslot);
4647
}
4748

49+
/**
50+
* Access the embedded link control message
51+
*/
52+
public LCMessage getLCMessage()
53+
{
54+
if(mLCMessage == null)
55+
{
56+
//Overrides default to ignore opcode and construct an encryption parameters message.
57+
mLCMessage = LCMessageFactory.createFullEncryption(getMessage(), getTimestamp(), getTimeslot());
58+
}
59+
60+
return mLCMessage;
61+
}
62+
4863
@Override
4964
public String toString()
5065
{
5166
StringBuilder sb = new StringBuilder();
52-
sb.append("CC:").append(getSlotType().getColorCode());
67+
5368
if(!isValid())
5469
{
5570
sb.append(" [CRC ERROR]");
5671
}
57-
sb.append(" HYTERA PROPRIETARY DATA HEADER");
58-
sb.append(" SAP:").append(getServiceAccessPoint());
59-
sb.append(" MSG:").append(getMessage().toHexString());
72+
73+
sb.append(getSlotType());
74+
75+
if(hasRAS())
76+
{
77+
sb.append(" RAS:").append(getBPTCReservedBits()).append(" ");
78+
}
79+
sb.append(" ").append(getLCMessage());
6080
return sb.toString();
6181
}
6282
}

0 commit comments

Comments
 (0)