Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2013 Enhance SUID extension message handling and correct encryption … #2014

Merged
merged 1 commit into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
/*
* *****************************************************************************
* 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.identifier;

import io.github.dsheirer.identifier.configuration.AliasListConfigurationIdentifier;
import io.github.dsheirer.identifier.radio.FullyQualifiedRadioIdentifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.sample.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Identifier collection with methods for changing or updating managed identifiers
Expand Down Expand Up @@ -209,6 +207,15 @@ public void update(Identifier identifier)
remove(existing);
add(identifier);
}
//Always replace a radio identifier with a fully qualified variant of itself
else if(existing instanceof RadioIdentifier &&
!(existing instanceof FullyQualifiedRadioIdentifier) &&
identifier instanceof FullyQualifiedRadioIdentifier &&
existing.getValue().equals(identifier.getValue()))
{
remove(existing);
add(identifier);
}
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
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;
Expand Down Expand Up @@ -709,6 +711,78 @@ public void processP1ControlDirectedChannelGrant(APCO25Channel apco25Channel, Se
}
}

/**
* 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.
*
Expand Down Expand Up @@ -755,6 +829,55 @@ public void processP1TrafficCurrentUser(long frequency, Identifier identifier, l
}
}

/**
* 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
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;
Expand Down Expand Up @@ -119,9 +119,9 @@ public void process(P25P1Message message)
{
process((LDUMessage)message);
}
else if(message instanceof TDULinkControlMessage)
else if(message instanceof TDULCMessage)
{
process((TDULinkControlMessage)message);
process((TDULCMessage)message);
}
else if(message instanceof TDUMessage)
{
Expand All @@ -133,7 +133,7 @@ else if (message instanceof HDUMessage)
}
}

private void process(TDULinkControlMessage tdulc)
private void process(TDULCMessage tdulc)
{
process(tdulc.getLinkControlWord());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
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;
Expand Down Expand Up @@ -113,7 +112,7 @@
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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -316,10 +316,6 @@ public void receive(IMessage iMessage)
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());
Expand Down Expand Up @@ -413,7 +409,6 @@ private void processLCChannelUser(LinkControlWord lcw, long timestamp)
getIdentifierCollection().update(updated);
DecodeEventType decodeEventType = getLCDecodeEventType(lcw);


ServiceOptions serviceOptions = null;

if(lcw instanceof IServiceOptionsProvider sop)
Expand Down Expand Up @@ -831,17 +826,31 @@ private void processHDU(IMessage message)
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())
{
broadcast(new DecoderStateEvent(this, Event.START, State.ENCRYPTED));
}
else
if(headerData.isValid())
{
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));
}
}
}
}
Expand All @@ -862,6 +871,8 @@ private void processLDU(P25P1Message message)
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)
Expand Down Expand Up @@ -917,7 +928,7 @@ private void processTDULC(P25P1Message message)
getIdentifierCollection().remove(IdentifierClass.USER, Role.FROM);
}

if(message instanceof TDULinkControlMessage tdulc)
if(message instanceof TDULCMessage tdulc)
{
LinkControlWord lcw = tdulc.getLinkControlWord();

Expand Down Expand Up @@ -1831,15 +1842,20 @@ private void processLC(LinkControlWord lcw, long timestamp, boolean isTerminator
{
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:
case UNIT_TO_UNIT_VOICE_CHANNEL_USER:
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
{
Expand Down Expand Up @@ -1941,11 +1957,6 @@ private void processLC(LinkControlWord lcw, long timestamp, boolean isTerminator
getIdentifierCollection().update(frequencyID);
}

if(isTerminator)
{
closeCurrentCallEvent(timestamp, lcw.toString());
}

mNetworkConfigurationMonitor.process(lcw);
break;
case NETWORK_STATUS_BROADCAST_EXPLICIT:
Expand Down Expand Up @@ -2162,9 +2173,6 @@ private void processLC(LinkControlWord lcw, long timestamp, boolean isTerminator
closeCurrentCallEvent(timestamp, lcw.toString());
}
break;
case SOURCE_ID_EXTENSION:
//Ignore - handled elsewhere
break;
default:
if(isTerminator)
{
Expand Down
Loading
Loading