Skip to content

Commit 605db6c

Browse files
committed
Meshtastic crypto / udp WIP and simulator
1 parent 7ef1a60 commit 605db6c

File tree

13 files changed

+212
-8
lines changed

13 files changed

+212
-8
lines changed

Directory.Packages.props

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,32 @@
33
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
44
</PropertyGroup>
55
<ItemGroup>
6+
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.5.1" />
67
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
78
<PackageVersion Include="FluentAssertions" Version="7.1.0" />
89
<PackageVersion Include="ILogger.Moq" Version="1.1.10" />
9-
<PackageVersion Include="Google.Protobuf" Version="3.29.3" />
10-
<PackageVersion Include="Google.Protobuf.Tools" Version="3.29.3" />
11-
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.1" />
12-
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.1" />
13-
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.1" />
14-
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="9.0.1" />
15-
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
10+
<PackageVersion Include="Google.Protobuf" Version="3.30.1" />
11+
<PackageVersion Include="Google.Protobuf.Tools" Version="3.30.1" />
12+
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.3" />
13+
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.3" />
14+
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.3" />
15+
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.3" />
16+
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="9.0.3" />
17+
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
18+
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
1619
<PackageVersion Include="Moq" Version="4.20.72" />
1720
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
1821
<PackageVersion Include="NUnit" Version="4.3.2" />
1922
<PackageVersion Include="NUnit3TestAdapter" Version="5.0.0" />
2023
<PackageVersion Include="NUnit.Analyzers" Version="4.6.0" />
2124
<PackageVersion Include="MQTTnet" Version="5.0.1.1416" />
25+
<PackageVersion Include="Portable.BouncyCastle" Version="1.9.0" />
2226
<PackageVersion Include="QRCoder" Version="1.6.0" />
2327
<PackageVersion Include="SimpleExec" Version="12.0.0" />
2428
<PackageVersion Include="Spectre.Console" Version="0.49.1" />
2529
<PackageVersion Include="Spectre.Console.Json" Version="0.49.1" />
2630
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
27-
<PackageVersion Include="System.IO.Ports" Version="9.0.1" />
31+
<PackageVersion Include="System.IO.Ports" Version="9.0.3" />
2832
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
2933
</ItemGroup>
3034
</Project>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
2+
3+
# This stage is used when running from VS in fast mode (Default for Debug configuration)
4+
FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base
5+
USER $APP_UID
6+
WORKDIR /app
7+
8+
9+
# This stage is used to build the service project
10+
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
11+
ARG BUILD_CONFIGURATION=Release
12+
WORKDIR /src
13+
COPY ["Directory.Packages.props", "."]
14+
COPY ["Meshtastic.Simulator.Service/Meshtastic.Simulator.Service.csproj", "Meshtastic.Simulator.Service/"]
15+
RUN dotnet restore "./Meshtastic.Simulator.Service/Meshtastic.Simulator.Service.csproj"
16+
COPY . .
17+
WORKDIR "/src/Meshtastic.Simulator.Service"
18+
RUN dotnet build "./Meshtastic.Simulator.Service.csproj" -c $BUILD_CONFIGURATION -o /app/build
19+
20+
# This stage is used to publish the service project to be copied to the final stage
21+
FROM build AS publish
22+
ARG BUILD_CONFIGURATION=Release
23+
RUN dotnet publish "./Meshtastic.Simulator.Service.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
24+
25+
# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
26+
FROM base AS final
27+
WORKDIR /app
28+
COPY --from=publish /app/publish .
29+
ENTRYPOINT ["dotnet", "Meshtastic.Simulator.Service.dll"]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Worker">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<UserSecretsId>dotnet-Meshtastic.Simulator.Service-f04ea7a8-15a0-4fec-b79a-3106e594e1a5</UserSecretsId>
8+
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.Extensions.Hosting" />
13+
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<ProjectReference Include="..\Meshtastic\Meshtastic.csproj" />
18+
</ItemGroup>
19+
</Project>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
using Meshtastic.Simulator.Service;
2+
3+
var builder = Host.CreateApplicationBuilder(args);
4+
builder.Services.AddHostedService<SimulatorWorker>();
5+
6+
var host = builder.Build();
7+
host.Run();
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"profiles": {
3+
"Meshtastic.Simulator.Service": {
4+
"commandName": "Project",
5+
"environmentVariables": {
6+
"DOTNET_ENVIRONMENT": "Development"
7+
},
8+
"dotnetRunMessages": true
9+
},
10+
"Container (Dockerfile)": {
11+
"commandName": "Docker"
12+
}
13+
},
14+
"$schema": "https://json.schemastore.org/launchsettings.json"
15+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using Meshtastic.Crypto;
2+
using Meshtastic.Protobufs;
3+
using System.Net;
4+
using System.Net.Sockets;
5+
6+
namespace Meshtastic.Simulator.Service;
7+
8+
public class SimulatorWorker(ILogger<SimulatorWorker> logger) : BackgroundService
9+
{
10+
private UdpClient? udpClient;
11+
private IPEndPoint remoteEndpoint = new(IPAddress.Any, Resources.DEFAULT_TCP_PORT);
12+
13+
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
14+
{
15+
while (!stoppingToken.IsCancellationRequested)
16+
{
17+
logger.LogDebug("Butt");
18+
19+
if (udpClient == null)
20+
{
21+
udpClient = new UdpClient(Resources.DEFAULT_TCP_PORT)
22+
{
23+
EnableBroadcast = true,
24+
Ttl = 64,
25+
};
26+
udpClient.Connect(IPAddress.Parse("224.0.0.69"), Resources.DEFAULT_TCP_PORT);
27+
}
28+
29+
if (udpClient.Available > 0)
30+
{
31+
var data = udpClient.Receive(ref remoteEndpoint);
32+
var packet = MeshPacket.Parser.ParseFrom(data);
33+
var nonce = new NonceGenerator(packet.From, packet.Id).Create();
34+
byte[] decrypted = PacketEncryption.TransformPacket(packet.Encrypted.Span.ToArray(), nonce, Convert.FromBase64String("AQ=="));
35+
36+
logger.LogInformation(packet.ToString());
37+
//udpClient.Send([1], 1, remoteEndpoint); // if data is received reply letting the client know that we got his data
38+
}
39+
await Task.Delay(100, stoppingToken);
40+
}
41+
}
42+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.Hosting.Lifetime": "Information"
6+
}
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.Hosting.Lifetime": "Information"
6+
}
7+
}
8+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Meshtastic.Crypto;
2+
using Meshtastic.Protobufs;
3+
4+
namespace Meshtastic.Test.Crypto;
5+
6+
public class PacketCryptoTests
7+
{
8+
[SetUp]
9+
public void Setup()
10+
{
11+
}
12+
13+
[Test]
14+
public void TestDefaultKeyDecrypt()
15+
{
16+
//{ "packet": { "from": 4202784164, "to": 4294967295, "channel": 8, "encrypted": "kiDV39nDDsi8AON+Czei6zUpy+F/7E+lyIpicxJR40KXBFmPkqFUEnobI5voQadha+s=", "id": 1777428186, "hopLimit": 3, "priority": "BACKGROUND", "hopStart": 3 }, "channelId": "LongFast", "gatewayId": "!fa8165a4" }
17+
var nonce = new NonceGenerator(4202784164, 1777428186).Create();
18+
var key = new byte[16] { 0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59, 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x1 };
19+
20+
var decrypted = PacketEncryption.TransformPacket(Convert.FromBase64String("kiDV39nDDsi8AON+Czei6zUpy+F/7E+lyIpicxJR40KXBFmPkqFUEnobI5voQadha+s="), nonce, key);
21+
var testMessage = Meshtastic.Protobufs.Data.Parser.ParseFrom(decrypted);
22+
testMessage.Portnum.Should().Be(PortNum.NodeinfoApp);
23+
var nodeInfo = User.Parser.ParseFrom(testMessage.Payload);
24+
nodeInfo.LongName.Should().Be("Meshtastic 65a4");
25+
}
26+
}

Meshtastic.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
1515
Directory.Packages.props = Directory.Packages.props
1616
EndProjectSection
1717
EndProject
18+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meshtastic.Simulator.Service", "Meshtastic.Simulator.Service\Meshtastic.Simulator.Service.csproj", "{F2226007-E2D7-4832-A94A-C7427EF2A3F1}"
19+
EndProject
1820
Global
1921
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2022
Debug|Any CPU = Debug|Any CPU
@@ -33,6 +35,10 @@ Global
3335
{E48770C0-4390-4B24-B9CB-610E5E7EC68A}.Debug|Any CPU.Build.0 = Debug|Any CPU
3436
{E48770C0-4390-4B24-B9CB-610E5E7EC68A}.Release|Any CPU.ActiveCfg = Release|Any CPU
3537
{E48770C0-4390-4B24-B9CB-610E5E7EC68A}.Release|Any CPU.Build.0 = Release|Any CPU
38+
{F2226007-E2D7-4832-A94A-C7427EF2A3F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39+
{F2226007-E2D7-4832-A94A-C7427EF2A3F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
40+
{F2226007-E2D7-4832-A94A-C7427EF2A3F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
41+
{F2226007-E2D7-4832-A94A-C7427EF2A3F1}.Release|Any CPU.Build.0 = Release|Any CPU
3642
EndGlobalSection
3743
GlobalSection(SolutionProperties) = preSolution
3844
HideSolutionNode = FALSE

Meshtastic/Crypto/NonceGenerator.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace Meshtastic.Crypto;
2+
public class NonceGenerator
3+
{
4+
private readonly byte[] nonce = new byte[16];
5+
6+
public NonceGenerator(uint fromNum, ulong packetId)
7+
{
8+
InitNonce(fromNum, packetId);
9+
}
10+
11+
public byte[] Create() => nonce;
12+
13+
private void InitNonce(uint fromNode, ulong packetId)
14+
{
15+
Array.Clear(nonce, 0, nonce.Length);
16+
17+
Buffer.BlockCopy(BitConverter.GetBytes(packetId), 0, nonce, 0, sizeof(ulong));
18+
Buffer.BlockCopy(BitConverter.GetBytes(fromNode), 0, nonce, sizeof(ulong), sizeof(uint));
19+
}
20+
}

Meshtastic/Crypto/PacketEncryption.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Org.BouncyCastle.Crypto;
2+
using Org.BouncyCastle.Crypto.Parameters;
3+
using Org.BouncyCastle.Security;
4+
5+
namespace Meshtastic.Crypto;
6+
7+
public static class PacketEncryption
8+
{
9+
private static readonly IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CTR/NoPadding");
10+
11+
public static byte[] TransformPacket(byte[] cypherText, byte[] nonce, byte[] key)
12+
{
13+
cipher.Init(false, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES", key), nonce));
14+
byte[] output = new byte[cipher.GetOutputSize(cypherText.Length)];
15+
_ = cipher.DoFinal(cypherText, output);
16+
17+
return output;
18+
}
19+
}

Meshtastic/Meshtastic.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
</ItemGroup>
3030

3131
<ItemGroup>
32+
<PackageReference Include="BouncyCastle.Cryptography" />
3233
<PackageReference Include="Google.Protobuf" />
3334
<PackageReference Include="Google.Protobuf.Tools" />
3435
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />

0 commit comments

Comments
 (0)