Add support for AES (0x84); refactor keys to use dvmfne KMM's

pull/1/head
firealarmss 11 months ago
parent 3abc4aa463
commit 6cadbdf897

@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using fnecore.DMR;
using fnecore;
using fnecore.P25.kmm;
namespace WhackerLinkConsoleV2
{
@ -180,6 +181,23 @@ namespace WhackerLinkConsoleV2
return;
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected override void KeyResponse(object sender, KeyResponseEvent e)
{
byte[] payload = e.Data.Skip(11).ToArray();
//Console.WriteLine(FneUtils.HexDump(payload));
if (e.MessageId == (byte)KmmMessageType.MODIFY_KEY_CMD)
{
mainWindow.KeyResponseReceived(e);
}
}
/// <summary>
/// Returns a new stream ID
/// </summary>

@ -0,0 +1,20 @@
<Window x:Class="WhackerLinkConsoleV2.KeyStatusWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="Key Status Window" Height="450" Width="800">
<Grid>
<ListView x:Name="KeyStatusListView" ItemsSource="{Binding KeyStatusItems}" Margin="10">
<ListView.View>
<GridView>
<GridViewColumn Header="Channel Name" DisplayMemberBinding="{Binding ChannelName}" Width="200"/>
<GridViewColumn Header="Algorithm ID" DisplayMemberBinding="{Binding AlgId}" Width="100"/>
<GridViewColumn Header="Key ID" DisplayMemberBinding="{Binding KeyId}" Width="100"/>
<GridViewColumn Header="Status" DisplayMemberBinding="{Binding KeyStatus}" Width="150"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>

@ -0,0 +1,92 @@
using System;
using System.Collections.ObjectModel;
using System.Windows;
using WhackerLinkConsoleV2.Controls;
using WhackerLinkLib.Models.Radio;
namespace WhackerLinkConsoleV2
{
public partial class KeyStatusWindow : Window
{
public ObservableCollection<KeyStatusItem> KeyStatusItems { get; private set; } = new ObservableCollection<KeyStatusItem>();
private Codeplug Codeplug;
private MainWindow mainWindow;
public KeyStatusWindow(Codeplug codeplug, MainWindow mainWindow)
{
InitializeComponent();
this.Codeplug = codeplug;
this.mainWindow = mainWindow;
DataContext = this;
LoadKeyStatus();
}
private void LoadKeyStatus()
{
Dispatcher.Invoke(() =>
{
KeyStatusItems.Clear();
foreach (var child in mainWindow.ChannelsCanvas.Children)
{
if (child == null)
{
Console.WriteLine("A child in ChannelsCanvas.Children is null.");
continue;
}
if (!(child is ChannelBox channelBox))
{
continue;
}
Codeplug.System system = Codeplug.GetSystemForChannel(channelBox.ChannelName);
if (system == null)
{
Console.WriteLine($"System not found for {channelBox.ChannelName}");
continue;
}
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channelBox.ChannelName);
if (cpgChannel == null)
{
Console.WriteLine($"Channel not found for {channelBox.ChannelName}");
continue;
}
if (!system.IsDvm)
continue;
if (cpgChannel.GetKeyId() == 0 || cpgChannel.GetAlgoId() == 0)
continue;
if (channelBox.crypter == null)
{
Console.WriteLine($"Crypter is null for channel {channelBox.ChannelName}");
continue;
}
bool hasKey = channelBox.crypter.HasKey(cpgChannel.GetKeyId());
KeyStatusItems.Add(new KeyStatusItem
{
ChannelName = channelBox.ChannelName,
AlgId = $"0x{cpgChannel.GetAlgoId():X2}",
KeyId = $"0x{cpgChannel.GetKeyId():X4}",
KeyStatus = hasKey ? "Key Available" : "No Key"
});
}
});
}
}
public class KeyStatusItem
{
public string ChannelName { get; set; }
public string AlgId { get; set; }
public string KeyId { get; set; }
public string KeyStatus { get; set; }
}
}

@ -175,6 +175,19 @@
</LinearGradientBrush>
</Button.Background>
</Button>
<Button VerticalContentAlignment="Center" Content="Key Stats" HorizontalAlignment="Left" Margin="696,0,0,0" VerticalAlignment="Center" Height="46" Width="44" Click="KeyStatus_Click" BorderBrush="#FFC1C1C1" BorderThickness="1,1,1,1" FontSize="10" FontFamily="Arial" Grid.Row="1">
<Button.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="2"/>
</Style>
</Button.Resources>
<Button.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFF0F0F0" Offset="0.485"/>
<GradientStop Color="#FFDBDBDB" Offset="0.517"/>
</LinearGradientBrush>
</Button.Background>
</Button>
<Image HorizontalAlignment="Left" Margin="514,3,0,3" Grid.Row="1" Width="38" Source="/Assets/page.png" IsHitTestVisible="False"/>
<Button Content="" VerticalContentAlignment="Bottom" HorizontalAlignment="Left" Margin="556,0,0,0" VerticalAlignment="Center" Height="46" Width="44" Click="AudioSettings_Click" BorderBrush="#FFC1C1C1" BorderThickness="1,1,1,1" Grid.Row="1" FontSize="10" FontFamily="Arial">
<Button.Resources>

@ -93,7 +93,7 @@ namespace WhackerLinkConsoleV2
public MainWindow()
{
#if !DEBUG
#if DEBUG
ConsoleNative.ShowConsole();
#endif
InitializeComponent();
@ -291,6 +291,7 @@ namespace WhackerLinkConsoleV2
});
};
peer.peer.PeerDisconnected += (response) =>
{
Console.WriteLine("FNE Peer disconnected");
@ -322,7 +323,7 @@ namespace WhackerLinkConsoleV2
{
var channelBox = new ChannelBox(_selectedChannelsManager, _audioManager, channel.Name, channel.System, channel.Tgid);
channelBox.crypter.AddKey(channel.GetKeyId(), channel.GetAlgoId(), channel.GetEncryptionKey());
//channelBox.crypter.AddKey(channel.GetKeyId(), channel.GetAlgoId(), channel.GetEncryptionKey());
if (_settingsManager.ChannelPositions.TryGetValue(channel.Name, out var position))
{
@ -537,6 +538,9 @@ namespace WhackerLinkConsoleV2
uint newTgid = UInt32.Parse(cpgChannel.Tgid);
bool exists = fneAffs.Any(aff => aff.Item2 == newTgid);
if (cpgChannel.GetAlgoId() != 0 && cpgChannel.GetKeyId() != 0)
fne.peer.SendMasterKeyRequest(cpgChannel.GetAlgoId(), cpgChannel.GetKeyId());
if (!exists)
fneAffs.Add(new Tuple<uint, uint>(GetUniqueRid(system.Rid), newTgid));
@ -554,7 +558,7 @@ namespace WhackerLinkConsoleV2
if (system.IsDvm)
{
PeerSystem fne = _fneSystemManager.GetFneSystem(system.Name);
fne.peer.SendMasterAffiliationUpdate(fneAffs);
//fne.peer.SendMasterAffiliationUpdate(fneAffs);
}
}
}
@ -1839,13 +1843,7 @@ namespace WhackerLinkConsoleV2
short[] samples = new short[FneSystemBase.MBE_SAMPLES_LENGTH];
if (cryptodev)
{
//Console.WriteLine($"MI: {FneUtils.HexDump(channel.mi)}");
//Console.WriteLine($"Algorithm ID: {channel.algId}");
//Console.WriteLine($"Key ID: {channel.kId}");
channel.crypter.Process(imbe, frameType, n);
}
channel.crypter.Process(imbe, frameType, n);
#if WIN32
if (channel.extFullRateVocoder == null)
@ -1919,6 +1917,53 @@ namespace WhackerLinkConsoleV2
return rid;
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
public void KeyResponseReceived(KeyResponseEvent e)
{
//Console.WriteLine($"Message ID: {e.KmmKey.MessageId}");
//Console.WriteLine($"Decrypt Info Format: {e.KmmKey.DecryptInfoFmt}");
//Console.WriteLine($"Algorithm ID: {e.KmmKey.AlgId}");
//Console.WriteLine($"Key ID: {e.KmmKey.KeyId}");
//Console.WriteLine($"Keyset ID: {e.KmmKey.KeysetItem.KeysetId}");
//Console.WriteLine($"Keyset Alg ID: {e.KmmKey.KeysetItem.AlgId}");
//Console.WriteLine($"Keyset Key Length: {e.KmmKey.KeysetItem.KeyLength}");
//Console.WriteLine($"Number of Keys: {e.KmmKey.KeysetItem.Keys.Count}");
foreach (var key in e.KmmKey.KeysetItem.Keys)
{
//Console.WriteLine($" Key Format: {key.KeyFormat}");
//Console.WriteLine($" SLN: {key.Sln}");
//Console.WriteLine($" Key ID: {key.KeyId}");
//Console.WriteLine($" Key Data: {BitConverter.ToString(key.GetKey())}");
Dispatcher.Invoke(() =>
{
foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
{
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
if (!system.IsDvm)
continue;
PeerSystem handler = _fneSystemManager.GetFneSystem(system.Name);
if (cpgChannel.GetKeyId() != 0 && cpgChannel.GetAlgoId() != 0)
channel.crypter.AddKey(key.KeyId, e.KmmKey.KeysetItem.AlgId, key.GetKey());
}
});
}
}
private void KeyStatus_Click(object sender, RoutedEventArgs e)
{
KeyStatusWindow keyStatus = new KeyStatusWindow(Codeplug, this);
keyStatus.Show();
}
/// <summary>
/// Event handler used to process incoming P25 data.
/// </summary>
@ -2136,13 +2181,11 @@ namespace WhackerLinkConsoleV2
Buffer.BlockCopy(data, count, channel.netLDU2, 200, 16);
count += 16;
if (channel.p25Errs > 0) // temp, need to actually get erros I guess
if (channel.p25Errs > 0) // temp, need to actually get errors I guess
P25Crypto.CycleP25Lfsr(channel.mi);
else
Array.Copy(newMI, channel.mi, P25Defines.P25_MI_LENGTH);
Console.WriteLine(channel.p25Errs);
// decode 9 IMBE codewords into PCM samples
P25DecodeAudioFrame(channel.netLDU2, e, handler, channel, isEmergency, P25Crypto.FrameType.LDU2);
}
@ -2150,7 +2193,7 @@ namespace WhackerLinkConsoleV2
break;
}
if (channel.mi != null && cryptodev)
if (channel.mi != null)
channel.crypter.Prepare(channel.algId, channel.kId, P25Crypto.ProtocolType.P25Phase1, channel.mi);
slot.RxRFS = e.SrcId;

@ -15,16 +15,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Derrived from https://github.com/boatbod/op25/op25/gr-op25_repeater/lib/p25_crypt_algs.cc
* Derrived from https://github.com/boatbod/op25/blob/master/op25/gr-op25_repeater/lib/op25_crypt_aes.cc
*
* Copyright (C) 2025 Caleb, K4PHP
*
*/
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Linq;
namespace WhackerLinkConsoleV2
{
/// <summary>
/// P25 Crypto class
/// </summary>
public class P25Crypto
{
private ProtocolType protocol;
@ -32,34 +35,25 @@ namespace WhackerLinkConsoleV2
private ushort keyId;
private byte[] messageIndicator = new byte[9];
private Dictionary<ushort, KeyInfo> keys = new Dictionary<ushort, KeyInfo>();
private byte[] adpKeystream = new byte[469];
private byte[] aesKeystream = new byte[240]; // AES buffer
private byte[] adpKeystream = new byte[469]; // ADP buffer
private int aesPosition;
private int adpPosition;
/// <summary>
/// Creates an instance of <see cref="P25Crypto"/>
/// </summary>
public P25Crypto()
{
this.protocol = ProtocolType.Unknown;
this.algId = 0x80;
this.keyId = 0;
this.aesPosition = 0;
this.adpPosition = 0;
}
/// <summary>
/// Clear keys
/// </summary>
public void Reset()
{
keys.Clear();
}
/// <summary>
/// Add key to keys list
/// </summary>
/// <param name="keyid"></param>
/// <param name="algid"></param>
/// <param name="key"></param>
public void AddKey(ushort keyid, byte algid, byte[] key)
{
if (keyid == 0 || algid == 0x80)
@ -68,91 +62,161 @@ namespace WhackerLinkConsoleV2
keys[keyid] = new KeyInfo(algid, key);
}
/// <summary>
/// Prepare P25 encryption meta data info
/// </summary>
/// <param name="algid"></param>
/// <param name="keyid"></param>
/// <param name="protocol"></param>
/// <param name="MI"></param>
/// <returns></returns>
public bool HasKey(ushort keyId)
{
return keys.ContainsKey(keyId);
}
public bool Prepare(byte algid, ushort keyid, ProtocolType protocol, byte[] MI)
{
this.algId = algid;
this.keyId = keyid;
this.protocol = protocol;
Array.Copy(MI, this.messageIndicator, Math.Min(MI.Length, this.messageIndicator.Length));
if (!keys.ContainsKey(keyid))
{
return false;
}
if (algid == 0xAA)
if (algid == 0x84) // AES-256
{
this.aesPosition = 0;
GenerateAesKeystream();
return true;
}
else if (algid == 0xAA) // ADP (RC4)
{
this.adpPosition = 0;
this.protocol = protocol;
AdpKeystreamGen();
GenerateAdpKeystream();
return true;
}
return false;
}
/// <summary>
/// Process P25 frames for crypto
/// </summary>
/// <param name="PCW"></param>
/// <param name="frameType"></param>
/// <param name="voiceSubframe"></param>
/// <returns></returns>
public bool Process(byte[] PCW, FrameType frameType, int voiceSubframe)
{
if (!keys.ContainsKey(keyId))
return false;
if (algId == 0xAA)
return AdpProcess(PCW, frameType, voiceSubframe);
return false;
return algId switch
{
0x84 => AesProcess(PCW, frameType, voiceSubframe),
0xAA => AdpProcess(PCW, frameType, voiceSubframe),
_ => false
};
}
/// <summary>
/// Cycles the P25 LFSR (Linear Feedback Shift Register) based on the given polynomial.
/// Create ADP key stream
/// </summary>
/// <param name="MI">The message indicator array to be processed.</param>
public static void CycleP25Lfsr(byte[] MI)
private void GenerateAdpKeystream()
{
if (MI == null || MI.Length < 9)
throw new ArgumentException("MI must be at least 9 bytes long.");
byte[] adpKey = new byte[13];
byte[] S = new byte[256];
byte[] K = new byte[256];
ulong lfsr = 0;
if (!keys.ContainsKey(keyId))
return;
// Load the first 8 bytes into the LFSR
for (int i = 0; i < 8; i++)
byte[] keyData = keys[keyId].Key;
int keySize = keyData.Length;
int padding = Math.Max(5 - keySize, 0);
int i, j = 0, k;
for (i = 0; i < padding; i++)
adpKey[i] = 0;
for (; i < 5; i++)
adpKey[i] = keySize > 0 ? keyData[i - padding] : (byte)0;
for (i = 5; i < 13; ++i)
{
lfsr = (lfsr << 8) | MI[i];
adpKey[i] = messageIndicator[i - 5];
}
// Perform 64-bit LFSR cycling using the polynomial:
// C(x) = x^64 + x^62 + x^46 + x^38 + x^27 + x^15 + 1
for (int cnt = 0; cnt < 64; cnt++)
for (i = 0; i < 256; ++i)
{
ulong bit = ((lfsr >> 63) ^ (lfsr >> 61) ^ (lfsr >> 45) ^ (lfsr >> 37) ^ (lfsr >> 26) ^ (lfsr >> 14)) & 0x1;
lfsr = (lfsr << 1) | bit;
K[i] = adpKey[i % 13];
S[i] = (byte)i;
}
// Store the result back into MI
for (int i = 7; i >= 0; i--)
for (i = 0; i < 256; ++i)
{
MI[i] = (byte)(lfsr & 0xFF);
lfsr >>= 8;
j = (j + S[i] + K[i]) & 0xFF;
Swap(S, i, j);
}
MI[8] = 0; // Last byte is always set to zero
i = j = 0;
for (k = 0; k < 469; ++k)
{
i = (i + 1) & 0xFF;
j = (j + S[i]) & 0xFF;
Swap(S, i, j);
adpKeystream[k] = S[(S[i] + S[j]) & 0xFF];
}
}
/// <summary>
/// Preform a swap
/// </summary>
/// <param name="S"></param>
/// <param name="i"></param>
/// <param name="j"></param>
private void Swap(byte[] S, int i, int j)
{
byte temp = S[i];
S[i] = S[j];
S[j] = temp;
}
/// <summary>
/// Process AES256
/// </summary>
/// <param name="PCW"></param>
/// <param name="frameType"></param>
/// <param name="voiceSubframe"></param>
/// <returns></returns>
private bool AesProcess(byte[] PCW, FrameType frameType, int voiceSubframe)
{
int offset = 16;
switch (frameType)
{
case FrameType.LDU1: offset += 0; break;
case FrameType.LDU2: offset += 101; break;
case FrameType.V4_0: offset += 7 * voiceSubframe; break;
case FrameType.V4_1: offset += 7 * (voiceSubframe + 4); break;
case FrameType.V4_2: offset += 7 * (voiceSubframe + 8); break;
case FrameType.V4_3: offset += 7 * (voiceSubframe + 12); break;
case FrameType.V2: offset += 7 * (voiceSubframe + 16); break;
default: return false;
}
if (protocol == ProtocolType.P25Phase1)
{
offset += (aesPosition * 11) + 11 + (aesPosition < 8 ? 0 : 2);
aesPosition = (aesPosition + 1) % 9;
for (int j = 0; j < 11; ++j)
{
PCW[j] ^= aesKeystream[j + offset];
}
}
else if (protocol == ProtocolType.P25Phase2)
{
for (int j = 0; j < 7; ++j)
{
PCW[j] ^= aesKeystream[j + offset];
}
PCW[6] &= 0x80;
}
return true;
}
/// <summary>
/// Process RC4
/// Process ADP
/// </summary>
/// <param name="PCW"></param>
/// <param name="frameType"></param>
@ -178,6 +242,7 @@ namespace WhackerLinkConsoleV2
{
offset += (adpPosition * 11) + 267 + (adpPosition < 8 ? 0 : 2);
adpPosition = (adpPosition + 1) % 9;
for (int j = 0; j < 11; ++j)
{
PCW[j] ^= adpKeystream[j + offset];
@ -196,72 +261,152 @@ namespace WhackerLinkConsoleV2
}
/// <summary>
/// Create RC4 key stream
/// Create AES key stream
/// </summary>
private void AdpKeystreamGen()
private void GenerateAesKeystream()
{
byte[] adpKey = new byte[13];
byte[] S = new byte[256];
byte[] K = new byte[256];
if (!keys.ContainsKey(keyId))
return;
byte[] keyData = keys[keyId].Key;
byte[] key = keys[keyId].Key;
byte[] iv = ExpandMiTo128(messageIndicator);
int keySize = keyData.Length;
int padding = Math.Max(5 - keySize, 0);
int i, j = 0, k;
using (var aes = Aes.Create())
{
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Key = key.Length == 32 ? key : key.Concat(new byte[32 - key.Length]).ToArray();
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
for (i = 0; i < padding; i++)
adpKey[i] = 0;
using (var encryptor = aes.CreateEncryptor())
{
byte[] input = new byte[16];
Array.Copy(iv, input, 16);
byte[] output = new byte[16];
for (int i = 0; i < aesKeystream.Length / 16; i++)
{
encryptor.TransformBlock(input, 0, 16, output, 0);
Buffer.BlockCopy(output, 0, aesKeystream, i * 16, 16);
Array.Copy(output, input, 16);
}
}
}
}
for (; i < 5; i++)
adpKey[i] = keySize > 0 ? keyData[i - padding] : (byte)0;
/// <summary>
/// Cycle P25 LFSR
/// </summary>
/// <param name="MI"></param>
/// <exception cref="ArgumentException"></exception>
public static void CycleP25Lfsr(byte[] MI)
{
// TODO: use step LFSR
if (MI == null || MI.Length < 9)
throw new ArgumentException("MI must be at least 9 bytes long.");
for (i = 5; i < 13; ++i)
{
adpKey[i] = messageIndicator[i - 5];
}
ulong lfsr = 0;
for (i = 0; i < 256; ++i)
// Load the first 8 bytes into the LFSR
for (int i = 0; i < 8; i++)
{
K[i] = adpKey[i % 13];
S[i] = (byte)i;
lfsr = (lfsr << 8) | MI[i];
}
for (i = 0; i < 256; ++i)
// Perform 64-bit LFSR cycling using the polynomial:
// C(x) = x^64 + x^62 + x^46 + x^38 + x^27 + x^15 + 1
for (int cnt = 0; cnt < 64; cnt++)
{
j = (j + S[i] + K[i]) & 0xFF;
Swap(S, i, j);
ulong bit = ((lfsr >> 63) ^ (lfsr >> 61) ^ (lfsr >> 45) ^ (lfsr >> 37) ^ (lfsr >> 26) ^ (lfsr >> 14)) & 0x1;
lfsr = (lfsr << 1) | bit;
}
i = j = 0;
for (k = 0; k < 469; ++k)
// Store the result back into MI
for (int i = 7; i >= 0; i--)
{
i = (i + 1) & 0xFF;
j = (j + S[i]) & 0xFF;
Swap(S, i, j);
adpKeystream[k] = S[(S[i] + S[j]) & 0xFF];
MI[i] = (byte)(lfsr & 0xFF);
lfsr >>= 8;
}
MI[8] = 0; // Last byte is always set to zero
}
/// <summary>
/// Preform a swap
/// Step LFSR
/// </summary>
/// <param name="S"></param>
/// <param name="i"></param>
/// <param name="j"></param>
private void Swap(byte[] S, int i, int j)
/// <param name="lfsr"></param>
/// <returns></returns>
private static ulong StepP25Lfsr(ref ulong lfsr)
{
byte temp = S[i];
S[i] = S[j];
S[j] = temp;
// Extract overflow bit (bit 63)
ulong ovBit = (lfsr >> 63) & 0x1;
// Compute feedback bit using polynomial: x^64 + x^62 + x^46 + x^38 + x^27 + x^15 + 1
ulong fbBit = ((lfsr >> 63) ^ (lfsr >> 61) ^ (lfsr >> 45) ^ (lfsr >> 37) ^
(lfsr >> 26) ^ (lfsr >> 14)) & 0x1;
// Shift LFSR left and insert feedback bit
lfsr = (lfsr << 1) | fbBit;
return ovBit;
}
/// <summary>
/// P25 protocol type
/// Expland MI to 128 IV
/// </summary>
/// <param name="mi"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
private static byte[] ExpandMiTo128(byte[] mi)
{
if (mi == null || mi.Length < 8)
throw new ArgumentException("MI must be at least 8 bytes long.");
byte[] iv = new byte[16];
// Copy first 64 bits of MI into LFSR
ulong lfsr = 0;
for (int i = 0; i < 8; i++)
{
lfsr = (lfsr << 8) | mi[i];
}
// Use LFSR routine to compute the expansion
ulong overflow = 0;
for (int i = 0; i < 64; i++)
{
overflow = (overflow << 1) | StepP25Lfsr(ref lfsr);
}
// Copy expansion and LFSR to IV
for (int i = 7; i >= 0; i--)
{
iv[i] = (byte)(overflow & 0xFF);
overflow >>= 8;
}
for (int i = 15; i >= 8; i--)
{
iv[i] = (byte)(lfsr & 0xFF);
lfsr >>= 8;
}
return iv;
}
private class KeyInfo
{
public byte AlgId { get; }
public byte[] Key { get; }
public KeyInfo(byte algid, byte[] key)
{
AlgId = algid;
Key = key;
}
}
public enum ProtocolType
{
Unknown = 0,
@ -269,9 +414,6 @@ namespace WhackerLinkConsoleV2
P25Phase2
}
/// <summary>
/// P25 frame type
/// </summary>
public enum FrameType
{
Unknown = 0,
@ -283,20 +425,5 @@ namespace WhackerLinkConsoleV2
V4_2,
V4_3
}
/// <summary>
/// Key info object
/// </summary>
private class KeyInfo
{
public byte AlgId { get; }
public byte[] Key { get; }
public KeyInfo(byte algid, byte[] key)
{
AlgId = algid;
Key = key;
}
}
}
}

@ -23,6 +23,7 @@ using Serilog;
using fnecore;
using WhackerLinkLib.Models.Radio;
using static WhackerLinkLib.Models.Radio.Codeplug;
using Microsoft.AspNetCore.Mvc.Formatters;
namespace WhackerLinkConsoleV2
{
@ -75,6 +76,15 @@ namespace WhackerLinkConsoleV2
// set configuration parameters
peer.Passphrase = system.AuthKey;
peer.Information = new PeerInformation
{
Details = new PeerDetails
{
ConventionalPeer = true,
Software = "WLINKCONSOLE",
Identity = "CONS OP"
}
};
peer.PingTime = 5;

@ -52,8 +52,8 @@ zones:
system: "System 1"
tgid: "2001"
keyId: 0x50
algoId: 0xaa
encryptionKey: 0x01, 0x23, 0x45, 0x67, 0x89
algoId: 0xaa # 0xAA or 0x84 (RC4 or AES)
encryptionKey: null # Ignored now, we use dvmfne KMM support
- name: "Channel 2"
system: "System 1"
tgid: "15002"

@ -1 +1 @@
Subproject commit b4f7f377a71198a78f1859d84b2c30f388969502
Subproject commit 50165cc03b5e51516a64b879bd8408084d8c33bd
Loading…
Cancel
Save

Powered by TurnKey Linux.