#2013 Enhance SUID extension message handling and correct encryption sync when the algorithm is detected as ACCORDION and the key ID and MI are both zero, indicating a bad algorithm ID value. (#2014)

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
Denny Sheirer
2024-10-14 13:32:40 -04:00
committed by GitHub
parent 67f91b80f2
commit 97afdfa8a9
12 changed files with 414 additions and 186 deletions

View File

@@ -31,8 +31,10 @@ import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.MutableIdentifierCollection;
import io.github.dsheirer.identifier.alias.TalkerAliasManager;
import io.github.dsheirer.identifier.encryption.EncryptionKeyIdentifier;
import io.github.dsheirer.identifier.patch.PatchGroupIdentifier;
import io.github.dsheirer.identifier.patch.PatchGroupPreLoadDataContent;
import io.github.dsheirer.identifier.scramble.ScrambleParameterIdentifier;
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
import io.github.dsheirer.log.LoggingSuppressor;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.IMessageListener;
@@ -709,6 +711,78 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
}
}
/**
* Process a Phase 1 HDU indicating a call start.
*
* @param frequency for the call event
* @param talkgroup to update within the event.
* @param eki for encryption settings.
* @param timestamp for the update
*/
public void processP1TrafficCallStart(long frequency, Identifier<?> talkgroup, Identifier<?> radio,
EncryptionKeyIdentifier eki, ServiceOptions serviceOptions,
IChannelDescriptor channelDescriptor, long timestamp)
{
mLock.lock();
try
{
P25TrafficChannelEventTracker tracker = getTracker(frequency, P25P1Message.TIMESLOT_1);
if(tracker != null)
{
removeTracker(frequency, P25P1Message.TIMESLOT_1);
}
DecodeEventType decodeEventType = getDecodeEventType(talkgroup, eki);
MutableIdentifierCollection mic = new MutableIdentifierCollection();
mic.update(talkgroup);
mic.update(radio);
mic.update(eki);
//Create a new event for the current call.
P25ChannelGrantEvent callEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
.channelDescriptor(channelDescriptor)
.details("PHASE 1 CALL " + (serviceOptions != null ? serviceOptions : ""))
.identifiers(mic)
.build();
tracker = new P25TrafficChannelEventTracker(callEvent);
addTracker(tracker, frequency, P25P1Message.TIMESLOT_1);
broadcast(tracker);
}
finally
{
mLock.unlock();
}
}
/**
* Determines the HDU message call decode event type from the talkgroup identifier and encryption key
* @param talkgroup to inspect
* @param eki to inspect
* @return decode event type.
*/
private static DecodeEventType getDecodeEventType(Identifier talkgroup, EncryptionKeyIdentifier eki)
{
DecodeEventType decodeEventType = null;
if(talkgroup instanceof PatchGroupIdentifier)
{
decodeEventType = eki.isEncrypted() ? DecodeEventType.CALL_PATCH_GROUP_ENCRYPTED : DecodeEventType.CALL_PATCH_GROUP;
}
else if(talkgroup instanceof TalkgroupIdentifier ti && ti.getValue() == 0) //Unit-to-Unit private call
{
decodeEventType = eki.isEncrypted() ? DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED : DecodeEventType.CALL_UNIT_TO_UNIT;
}
else
{
decodeEventType = eki.isEncrypted() ? DecodeEventType.CALL_GROUP_ENCRYPTED : DecodeEventType.CALL_GROUP;
}
return decodeEventType;
}
/**
* Updates an identifier for an ongoing call event on the frequency and updates the event duration timestamp.
*
@@ -755,6 +829,55 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
}
}
/**
* Updates all current identifiers for an ongoing call event on the frequency and updates the event duration timestamp.
*
* Note: if this manager does not have an existing call event for the frequency, the update is ignored
* because we don't have enough detail to create a call event.
*
* This is used primarily to add encryption, GPS, talker alias, etc. but can be used for any identifier update.
*
* @param frequency for the call event
* @param identifier to update within the event.
* @param timestamp for the update
*/
public void processP1TrafficCurrentUserIdentifiers(long frequency, List<Identifier> identifiers, long timestamp, String context)
{
mLock.lock();
try
{
P25TrafficChannelEventTracker tracker = getTracker(frequency, P25P1Message.TIMESLOT_1);
if(tracker != null && tracker.isComplete())
{
removeTracker(frequency, P25P1Message.TIMESLOT_1);
tracker = null;
}
if(tracker != null)
{
for(Identifier identifier : identifiers)
{
tracker.addIdentifierIfMissing(identifier);
//Add the encryption key to the call event details.
if(identifier instanceof EncryptionKeyIdentifier eki && eki.isEncrypted())
{
tracker.addDetailsIfMissing(eki.toString());
}
}
tracker.updateDurationTraffic(timestamp);
broadcast(tracker);
}
}
finally
{
mLock.unlock();
}
}
/**
* Processes traffic channel announced current user information.
* @param frequency of the traffic channel

View File

@@ -39,7 +39,7 @@ import io.github.dsheirer.module.decode.p25.phase1.message.ldu.EncryptionSyncPar
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU1Message;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU2Message;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDUMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULCMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
import io.github.dsheirer.preference.UserPreferences;
import java.util.List;
@@ -119,9 +119,9 @@ public class P25P1CallSequenceRecorder extends MBECallSequenceRecorder
{
process((LDUMessage)message);
}
else if(message instanceof TDULinkControlMessage)
else if(message instanceof TDULCMessage)
{
process((TDULinkControlMessage)message);
process((TDULCMessage)message);
}
else if(message instanceof TDUMessage)
{
@@ -133,7 +133,7 @@ public class P25P1CallSequenceRecorder extends MBECallSequenceRecorder
}
}
private void process(TDULinkControlMessage tdulc)
private void process(TDULCMessage tdulc)
{
process(tdulc.getLinkControlWord());
}

View File

@@ -61,7 +61,6 @@ import io.github.dsheirer.module.decode.p25.phase1.message.IFrequencyBand;
import io.github.dsheirer.module.decode.p25.phase1.message.P25P1Message;
import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HDUMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HeaderData;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.IExtendedSourceMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisReturnToControlChannel;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerAliasComplete;
@@ -113,7 +112,7 @@ import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.PacketMess
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPPacketMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.response.ResponseMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.umbtc.isp.UMBTCTelephoneInterconnectRequestExplicitDialing;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULCMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.harris.osp.L3HarrisGroupRegroupExplicitEncryptionCommand;
@@ -190,6 +189,7 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
private final P25P1NetworkConfigurationMonitor mNetworkConfigurationMonitor;
private final Listener<ChannelEvent> mChannelEventListener;
private P25TrafficChannelManager mTrafficChannelManager;
private ServiceOptions mCurrentServiceOptions;
/**
* Constructs an APCO-25 decoder state with an optional traffic channel manager.
@@ -316,10 +316,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
break;
}
}
else if(iMessage instanceof IExtendedSourceMessage esm && iMessage instanceof LinkControlWord lcw)
{
processLC(lcw, esm.getTimestamp(), esm.isTerminator());
}
else if(iMessage instanceof MotorolaTalkerAliasComplete tac)
{
mTrafficChannelManager.getTalkerAliasManager().update(tac.getRadio(), tac.getAlias());
@@ -413,7 +409,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
getIdentifierCollection().update(updated);
DecodeEventType decodeEventType = getLCDecodeEventType(lcw);
ServiceOptions serviceOptions = null;
if(lcw instanceof IServiceOptionsProvider sop)
@@ -831,17 +826,31 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
if(message.isValid() && message instanceof HDUMessage hdu)
{
HeaderData headerData = hdu.getHeaderData();
//Run the talkgroup through the patch group manager so we don't get a plain talkgroup in addition to the patch
Identifier talkgroup = mPatchGroupManager.update(headerData.getTalkgroup(), message.getTimestamp());
mTrafficChannelManager.processP1TrafficCurrentUser(getCurrentFrequency(), talkgroup, hdu.getTimestamp(), message.toString());
if(headerData.isEncryptedAudio())
if(headerData.isValid())
{
broadcast(new DecoderStateEvent(this, Event.START, State.ENCRYPTED));
}
else
{
broadcast(new DecoderStateEvent(this, Event.START, State.CALL));
Identifier talkgroup = headerData.getTalkgroup();
//Run the talkgroup through the patch group manager so we don't get a plain talkgroup in addition to the patch
//Talkgroup value of zero indicates a unit-to-unit call, so don't attempt to update it as a patch group
if(headerData.getTalkgroup().getValue() > 0)
{
talkgroup = mPatchGroupManager.update(talkgroup, message.getTimestamp());
}
Identifier<?> radio = getIdentifierCollection().getFromIdentifier();
mTrafficChannelManager.processP1TrafficCallStart(getCurrentFrequency(), talkgroup, radio,
headerData.getEncryptionKey(), mCurrentServiceOptions, getCurrentChannel(), message.getTimestamp());
if(headerData.isEncryptedAudio())
{
broadcast(new DecoderStateEvent(this, Event.START, State.ENCRYPTED));
}
else
{
broadcast(new DecoderStateEvent(this, Event.START, State.CALL));
}
}
}
}
@@ -862,6 +871,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
if(lcw != null && lcw.isValid())
{
processLC(lcw, message.getTimestamp(), false);
mTrafficChannelManager.processP1TrafficCurrentUserIdentifiers(getCurrentFrequency(),
getIdentifierCollection().getIdentifiers(), message.getTimestamp(), ldu1.toString());
}
}
else if(message instanceof LDU2Message ldu2)
@@ -917,7 +928,7 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
getIdentifierCollection().remove(IdentifierClass.USER, Role.FROM);
}
if(message instanceof TDULinkControlMessage tdulc)
if(message instanceof TDULCMessage tdulc)
{
LinkControlWord lcw = tdulc.getLinkControlWord();
@@ -1831,7 +1842,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
{
switch(lcw.getOpcode())
{
//Calls in-progress on this channel
case SOURCE_ID_EXTENSION:
//Ignore - handled elsewhere
break;
//Calls getting ready to start or in-progress on this channel
case GROUP_VOICE_CHANNEL_USER:
case MOTOROLA_GROUP_REGROUP_VOICE_CHANNEL_USER:
case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER:
@@ -1839,7 +1853,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
case UNIT_TO_UNIT_VOICE_CHANNEL_USER_EXTENDED:
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
mTrafficChannelManager.processP1TrafficCallEnd(getCurrentFrequency(), timestamp, lcw.toString());
List<Identifier> updated = mPatchGroupManager.update(lcw.getIdentifiers(), timestamp);
getIdentifierCollection().update(updated);
}
else
{
@@ -1941,11 +1957,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
getIdentifierCollection().update(frequencyID);
}
if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}
mNetworkConfigurationMonitor.process(lcw);
break;
case NETWORK_STATUS_BROADCAST_EXPLICIT:
@@ -2162,9 +2173,6 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
closeCurrentCallEvent(timestamp, lcw.toString());
}
break;
case SOURCE_ID_EXTENSION:
//Ignore - handled elsewhere
break;
default:
if(isTerminator)
{

View File

@@ -19,10 +19,12 @@
package io.github.dsheirer.module.decode.p25.phase1;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.message.AbstractMessage;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.P25FrequencyBandPreloadDataContent;
import io.github.dsheirer.module.decode.p25.phase1.message.IFrequencyBand;
import io.github.dsheirer.module.decode.p25.phase1.message.IFrequencyBandReceiver;
import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HDUMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.IExtendedSourceMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.HarrisTalkerAliasAssembler;
@@ -30,7 +32,8 @@ import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisT
import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaTalkerAliasAssembler;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.standard.LCSourceIDExtension;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU1Message;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU2Message;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULCMessage;
import io.github.dsheirer.sample.Listener;
import java.util.List;
import java.util.Map;
@@ -59,9 +62,12 @@ public class P25P1MessageProcessor implements Listener<IMessage>
private Map<Integer,IFrequencyBand> mFrequencyBandMap = new TreeMap<Integer,IFrequencyBand>();
/**
* Temporary holding of an extended source link control message while it awaits the extension message to arrive.
* Temporary holding for an extended source link control message while it awaits the extension message to arrive.
*/
private IExtendedSourceMessage mExtendedSourceMessage;
private TDULCMessage mHeldTDULCMessage;
private HDUMessage mHeldHDUMessage;
private LDU1Message mHeldLDU1Message;
private LDU2Message mHeldLDU2Message;
/**
* Motorola talker alias assembler for link control header and data blocks.
@@ -103,120 +109,200 @@ public class P25P1MessageProcessor implements Listener<IMessage>
@Override
public void receive(IMessage message)
{
//Optional message created during processing that should be sent after the current argument is sent.
IMessage additionalMessageToSend = null;
if(message.isValid())
{
//Reassemble extended source link control messages.
if(message instanceof LDU1Message ldu)
if(message instanceof LDU1Message ldu1)
{
if(ldu.getLinkControlWord() instanceof IExtendedSourceMessage extendedSourceMessage)
LinkControlWord lcw = ldu1.getLinkControlWord();;
if(lcw instanceof IExtendedSourceMessage esm)
{
additionalMessageToSend = reassembleLC(extendedSourceMessage);
if(lcw instanceof LCSourceIDExtension extension)
{
processSourceIDExtension(extension);
}
else if(esm.isExtensionRequired())
{
processSourceIDExtension(null);
mHeldLDU1Message = ldu1;
return;
}
}
else if(ldu.getLinkControlWord() instanceof LCHarrisTalkerAliasBase harrisTalkerAlias)
else if(lcw instanceof LCHarrisTalkerAliasBase harrisTalkerAlias)
{
//Send the LCW to the harris talker alias assembler
additionalMessageToSend = mHarrisTalkerAliasAssembler.process(harrisTalkerAlias, ldu.getTimestamp());
dispatch(mHarrisTalkerAliasAssembler.process(harrisTalkerAlias, ldu1.getTimestamp()));
}
//Flush any held messages
processSourceIDExtension(null);
dispatch(ldu1);
}
else if(message instanceof LDU2Message ldu2)
{
//If we held onto an LDU1 awaiting a source ID extension, then also hold onto this LDU2 and flush them in sequence.
if(mHeldLDU1Message != null)
{
mHeldLDU2Message = ldu2;
}
else
{
dispatch(ldu2);
}
}
else if(message instanceof TDULinkControlMessage tdu)
else if(message instanceof TDULCMessage tdulc)
{
LinkControlWord lcw = tdu.getLinkControlWord();
LinkControlWord lcw = tdulc.getLinkControlWord();
if(lcw instanceof IExtendedSourceMessage extendedSourceMessage)
if(lcw instanceof IExtendedSourceMessage esm)
{
additionalMessageToSend = reassembleLC(extendedSourceMessage);
if(lcw instanceof LCSourceIDExtension sourceIDExtension)
{
processSourceIDExtension(sourceIDExtension);
}
else if(esm.isExtensionRequired())
{
processSourceIDExtension(null);
mHeldTDULCMessage = tdulc;
return;
}
}
//Motorola carries the talker alias in the TDULC
else if(mMotorolaTalkerAliasAssembler.add(lcw, message.getTimestamp()))
{
additionalMessageToSend = mMotorolaTalkerAliasAssembler.assemble();
dispatch(mMotorolaTalkerAliasAssembler.assemble());
}
//Harris carries the talker alias in the LDU voice messages, so reset the assembler on TDULC.
//Harris carries the talker alias in the LDU1 link control messages, so reset the assembler on TDULC.
mHarrisTalkerAliasAssembler.reset();
dispatch(tdulc);
}
//Insert frequency band identifier update messages into channel-type messages */
if(message instanceof IFrequencyBandReceiver)
else if(message instanceof HDUMessage hdu && mHeldTDULCMessage != null)
{
IFrequencyBandReceiver receiver = (IFrequencyBandReceiver)message;
List<IChannelDescriptor> channels = receiver.getChannels();
for(IChannelDescriptor channel : channels)
{
int[] frequencyBandIdentifiers = channel.getFrequencyBandIdentifiers();
for(int id : frequencyBandIdentifiers)
{
if(mFrequencyBandMap.containsKey(id))
{
channel.setFrequencyBand(mFrequencyBandMap.get(id));
}
}
}
//If the last TDULC message was held because it needs an extension that can arrive in the first LDU1,
//hold onto the intermediate HDU message and send everything (TDU, HDU, LDU) in correct sequence.
mHeldHDUMessage = hdu;
}
//Store band identifiers so that they can be injected into channel type messages
if(message instanceof IFrequencyBand)
else
{
IFrequencyBand bandIdentifier = (IFrequencyBand)message;
//Only store the frequency band if it's new so we don't hold on to more than one instance of the
//frequency band message. Otherwise, we'll hold on to several instances of each message as they get
//injected into other messages with channel information.
if(!mFrequencyBandMap.containsKey(bandIdentifier.getIdentifier()))
{
mFrequencyBandMap.put(bandIdentifier.getIdentifier(), bandIdentifier);
}
}
}
if(mMessageListener != null)
{
if(message != null)
{
mMessageListener.receive(message);
}
if(additionalMessageToSend != null)
{
mMessageListener.receive(additionalMessageToSend);
//flush any held messages
processSourceIDExtension(null);
dispatch(message);
}
}
}
/**
* Processes link control words to reassemble source ID extension messages.
* @param lcw to process
* @return reassembled link control or null if the original lcw is a receiver (which we hold onto and send later)
* Processes the source extension message and attaches it to any held TDULC or LDU1 with LC messages and flushes any
* held messages.
* @param extension to attach to held message(s) or null extension to flush all held messages.
*/
private IMessage reassembleLC(IExtendedSourceMessage lcw)
private void processSourceIDExtension(LCSourceIDExtension extension)
{
IMessage toReturn = null;
if(lcw instanceof IExtendedSourceMessage extendedSourceMessage && extendedSourceMessage.isExtensionRequired())
if(mHeldTDULCMessage != null && mHeldTDULCMessage.getLinkControlWord() instanceof IExtendedSourceMessage esm)
{
mExtendedSourceMessage = extendedSourceMessage;
}
else if(lcw instanceof LCSourceIDExtension sie && mExtendedSourceMessage != null)
{
mExtendedSourceMessage.setSourceIDExtension(sie);
toReturn = mExtendedSourceMessage;
mExtendedSourceMessage = null;
}
else
{
//The source extension message should always immediately follow the message that is being extended, so if
//we get a message that is not an extended message or the extension, then we've missed the extension and we
//should nullify any extended message that's waiting for the extension.
mExtendedSourceMessage = null;
esm.setSourceIDExtension(extension);
dispatch(mHeldTDULCMessage);
mHeldTDULCMessage = null;
dispatch(mHeldHDUMessage);
mHeldHDUMessage = null;
}
return toReturn;
if(mHeldLDU1Message != null && mHeldLDU1Message.getLinkControlWord() instanceof IExtendedSourceMessage esm)
{
esm.setSourceIDExtension(extension);
dispatch(mHeldLDU1Message);
mHeldLDU1Message = null;
dispatch(mHeldLDU2Message);
mHeldLDU2Message = null;
}
}
/**
* Post-process the message for frequency band details.
* @param message to post process and dispatch
*/
private void dispatch(IMessage message)
{
if(message == null)
{
return;
}
processForFrequencyBands(message);
//Also process the link control messages for frequency bands.
if(message instanceof LDU1Message ldu1)
{
processForFrequencyBands(ldu1.getLinkControlWord());
}
else if(message instanceof TDULCMessage tdulc)
{
processForFrequencyBands(tdulc.getLinkControlWord());
}
if(mMessageListener != null)
{
mMessageListener.receive(message);
}
}
/**
* Captures IDEN_UPDATE messages and attaches them to messages with channel descriptors.
* @param message to process for frequency band assembly.
*/
private void processForFrequencyBands(IMessage message)
{
if(message instanceof AbstractMessage am)
{
processForFrequencyBands(am);
}
}
/**
* Captures IDEN_UPDATE messages and attaches them to messages with channel descriptors.
* @param message to process for frequency band assembly.
*/
private void processForFrequencyBands(AbstractMessage message)
{
//Insert frequency band identifier update messages into channel-type messages */
if(message instanceof IFrequencyBandReceiver)
{
IFrequencyBandReceiver receiver = (IFrequencyBandReceiver)message;
List<IChannelDescriptor> channels = receiver.getChannels();
for(IChannelDescriptor channel : channels)
{
int[] frequencyBandIdentifiers = channel.getFrequencyBandIdentifiers();
for(int id : frequencyBandIdentifiers)
{
if(mFrequencyBandMap.containsKey(id))
{
channel.setFrequencyBand(mFrequencyBandMap.get(id));
}
}
}
}
//Store band identifiers so that they can be injected into channel type messages
if(message instanceof IFrequencyBand)
{
IFrequencyBand bandIdentifier = (IFrequencyBand)message;
//Only store the frequency band if it's new so we don't hold on to more than one instance of the
//frequency band message. Otherwise, we'll hold on to several instances of each message as they get
//injected into other messages with channel information.
if(!mFrequencyBandMap.containsKey(bandIdentifier.getIdentifier()))
{
mFrequencyBandMap.put(bandIdentifier.getIdentifier(), bandIdentifier);
}
}
}
/**

View File

@@ -23,7 +23,7 @@ import io.github.dsheirer.module.decode.p25.phase1.P25P1DataUnitID;
import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HDUMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU1Message;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU2Message;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULCMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.vselp.VSELP1Message;
import io.github.dsheirer.module.decode.p25.phase1.message.vselp.VSELP2Message;
@@ -65,7 +65,7 @@ public class P25MessageFactory
case TERMINATOR_DATA_UNIT:
return new TDUMessage(message, nac, timestamp);
case TERMINATOR_DATA_UNIT_LINK_CONTROL:
return new TDULinkControlMessage(message, nac, timestamp);
return new TDULCMessage(message, nac, timestamp);
case TRUNKING_SIGNALING_BLOCK_1:
case TRUNKING_SIGNALING_BLOCK_2:
case TRUNKING_SIGNALING_BLOCK_3:

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@ import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlOpcode;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULCMessage;
import java.util.Collection;
import java.util.function.Function;
@@ -63,7 +63,7 @@ public class TerminatorMessageFilter extends Filter<IMessage, LinkControlOpcode>
@Override
public LinkControlOpcode apply(IMessage message)
{
if(message instanceof TDULinkControlMessage tdulc)
if(message instanceof TDULCMessage tdulc)
{
return tdulc.getLinkControlWord().getOpcode();
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@ package io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlOpcode;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULCMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
import java.util.ArrayList;
import java.util.List;
@@ -59,6 +59,6 @@ public class TerminatorMessageFilterSet extends FilterSet<IMessage>
@Override
public boolean canProcess(IMessage message)
{
return (message instanceof TDUMessage || message instanceof TDULinkControlMessage) && super.canProcess(message);
return (message instanceof TDUMessage || message instanceof TDULCMessage) && super.canProcess(message);
}
}

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 Dennis Sheirer
* *
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.hdu;
@@ -25,11 +22,11 @@ package io.github.dsheirer.module.decode.p25.phase1.message.hdu;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.encryption.EncryptionKeyIdentifier;
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
import io.github.dsheirer.module.decode.p25.identifier.encryption.APCO25EncryptionKey;
import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25Talkgroup;
import io.github.dsheirer.module.decode.p25.reference.Encryption;
import io.github.dsheirer.module.decode.p25.reference.Vendor;
import java.util.ArrayList;
import java.util.List;
@@ -49,8 +46,8 @@ public class HeaderData
private boolean mValid = true;
private BinaryMessage mMessage;
private Identifier mEncryptionKey;
private Identifier mTalkgroup;
private EncryptionKeyIdentifier mEncryptionKey;
private TalkgroupIdentifier mTalkgroup;
private List<Identifier> mIdentifiers;
public HeaderData(BinaryMessage message)
@@ -129,9 +126,9 @@ public class HeaderData
return Vendor.fromValue(getMessage().getInt(VENDOR_ID));
}
public Identifier getEncryptionKey()
public EncryptionKeyIdentifier getEncryptionKey()
{
if(mEncryptionKey == null && isEncryptedAudio())
if(mEncryptionKey == null)
{
mEncryptionKey = EncryptionKeyIdentifier.create(APCO25EncryptionKey.create(getMessage().getInt(ALGORITHM_ID),
getMessage().getInt(KEY_ID)));
@@ -150,7 +147,7 @@ public class HeaderData
return getEncryption() != Encryption.UNENCRYPTED;
}
public Identifier getTalkgroup()
public TalkgroupIdentifier getTalkgroup()
{
if(mTalkgroup == null)
{

View File

@@ -32,7 +32,8 @@ import java.util.List;
*/
public class LCCallTermination extends LinkControlWord
{
private static final int MOTOROLA_SYSTEM_CONTROLLER = 0xFFFFFD;
private static final int MOTOROLA_SYSTEM_CONTROLLER_1 = 0xFFFFFD;
private static final int MOTOROLA_SYSTEM_CONTROLLER_2 = 0xFFFFFF;
private static final int HARRIS_SYSTEM_CONTROLLER = 0;
private static final IntField ADDRESS = IntField.length24(OCTET_6_BIT_48);
private Identifier mAddress;
@@ -75,7 +76,7 @@ public class LCCallTermination extends LinkControlWord
public boolean isNetworkCommandedTeardown()
{
int address = getInt(ADDRESS);
return address == MOTOROLA_SYSTEM_CONTROLLER || address == HARRIS_SYSTEM_CONTROLLER;
return address == MOTOROLA_SYSTEM_CONTROLLER_1 || address == MOTOROLA_SYSTEM_CONTROLLER_2 || address == HARRIS_SYSTEM_CONTROLLER;
}
/**

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 Dennis Sheirer
* *
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.ldu;
@@ -27,7 +24,7 @@ import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.encryption.EncryptionKeyIdentifier;
import io.github.dsheirer.module.decode.p25.identifier.encryption.APCO25EncryptionKey;
import io.github.dsheirer.module.decode.p25.reference.Encryption;
import java.util.ArrayList;
import java.util.List;
@@ -36,6 +33,7 @@ import java.util.List;
*/
public class EncryptionSyncParameters implements IEncryptionSyncParameters
{
private static final String EMPTY_MESSAGE_INDICATOR = "000000000000000000";
private static final int[] MESSAGE_INDICATOR_1 = {0, 1, 2, 3, 4, 5, 6, 7};
private static final int[] MESSAGE_INDICATOR_2 = {8, 9, 10, 11, 12, 13, 14, 15};
private static final int[] MESSAGE_INDICATOR_3 = {16, 17, 18, 19, 20, 21, 22, 23};
@@ -134,8 +132,16 @@ public class EncryptionSyncParameters implements IEncryptionSyncParameters
{
if(mEncryptionKey == null)
{
mEncryptionKey = EncryptionKeyIdentifier.create(APCO25EncryptionKey.create(getMessage().getInt(ALGORITHM_ID),
getMessage().getInt(KEY_ID)));
int algorithm = getMessage().getInt(ALGORITHM_ID);
int key = getMessage().getInt(KEY_ID);
//Detect when algorithm, key and MI are all zeros and override algorithm to set as unencrypted.
if(algorithm == 0 && key == 0 && getMessageIndicator().contains(EMPTY_MESSAGE_INDICATOR))
{
algorithm = Encryption.UNENCRYPTED.getValue(); //0x80
}
mEncryptionKey = EncryptionKeyIdentifier.create(APCO25EncryptionKey.create(algorithm, key));
}
return mEncryptionKey;

View File

@@ -31,7 +31,7 @@ import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWordFac
import java.util.Collections;
import java.util.List;
public class TDULinkControlMessage extends P25P1Message implements IFrequencyBandReceiver
public class TDULCMessage extends P25P1Message implements IFrequencyBandReceiver
{
public static final int[] LC_HEX_0 = {0, 1, 2, 3, 4, 5};
public static final int[] LC_HEX_1 = {6, 7, 8, 9, 10, 11};
@@ -63,7 +63,7 @@ public class TDULinkControlMessage extends P25P1Message implements IFrequencyBan
private LinkControlWord mLinkControlWord;
public TDULinkControlMessage(CorrectedBinaryMessage message, int nac, long timestamp)
public TDULCMessage(CorrectedBinaryMessage message, int nac, long timestamp)
{
super(message, nac, timestamp);
}