Add Wireguard KeyGen Util
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
bin/
|
||||
obj/
|
||||
|
||||
|
||||
# MCP Vector Search index directory
|
||||
.mcp-vector-search/
|
||||
5
AS1024.Wireguard.Utils.slnx
Normal file
5
AS1024.Wireguard.Utils.slnx
Normal file
@@ -0,0 +1,5 @@
|
||||
<Solution>
|
||||
<Project Path="src/AS1024.Wireguard.Utils/AS1024.Wireguard.Utils.csproj" />
|
||||
<Project Path="tests/AS1024.Wireguard.Utils.Tests/AS1024.Wireguard.Utils.Tests.csproj" />
|
||||
<Project Path="tools/AS1024.Wireguard.Utils.Cli/AS1024.Wireguard.Utils.Cli.csproj" />
|
||||
</Solution>
|
||||
13
src/AS1024.Wireguard.Utils/AS1024.Wireguard.Utils.csproj
Normal file
13
src/AS1024.Wireguard.Utils/AS1024.Wireguard.Utils.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
45
src/AS1024.Wireguard.Utils/WireGuardKeyUtils.cs
Normal file
45
src/AS1024.Wireguard.Utils/WireGuardKeyUtils.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.Security.Cryptography;
|
||||
using Org.BouncyCastle.Math.EC.Rfc7748;
|
||||
|
||||
namespace AS1024.Wireguard.Utils;
|
||||
|
||||
// WireGuard / Curve25519 keys. RFC 7748.
|
||||
public static class WireGuardKeyUtils
|
||||
{
|
||||
public const int KeySize = 32;
|
||||
|
||||
public static byte[] GeneratePrivateKey()
|
||||
{
|
||||
var key = RandomNumberGenerator.GetBytes(KeySize);
|
||||
Clamp(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
public static byte[] GetPublicKey(byte[] privateKey)
|
||||
{
|
||||
if (privateKey.Length != KeySize)
|
||||
throw new ArgumentException($"WireGuard keys are {KeySize} bytes.", nameof(privateKey));
|
||||
|
||||
var scalar = (byte[])privateKey.Clone();
|
||||
Clamp(scalar);
|
||||
|
||||
var pub = new byte[KeySize];
|
||||
X25519.ScalarMultBase(scalar, 0, pub, 0);
|
||||
return pub;
|
||||
}
|
||||
|
||||
public static byte[] FromBase64(string s)
|
||||
{
|
||||
var key = Convert.FromBase64String(s);
|
||||
if (key.Length != KeySize)
|
||||
throw new ArgumentException($"WireGuard keys are {KeySize} bytes.", nameof(s));
|
||||
return key;
|
||||
}
|
||||
|
||||
static void Clamp(Span<byte> scalar)
|
||||
{
|
||||
// Curve25519 clamp, RFC 7748 §5.
|
||||
scalar[0] &= 0xF8;
|
||||
scalar[31] = (byte)((scalar[31] & 0x7F) | 0x40);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\AS1024.Wireguard.Utils\AS1024.Wireguard.Utils.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
31
tests/AS1024.Wireguard.Utils.Tests/WireGuardKeyUtilsTests.cs
Normal file
31
tests/AS1024.Wireguard.Utils.Tests/WireGuardKeyUtilsTests.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
namespace AS1024.Wireguard.Utils.Tests;
|
||||
|
||||
public class WireGuardKeyUtilsTests
|
||||
{
|
||||
[Fact]
|
||||
public void RoundTrip()
|
||||
{
|
||||
var priv = WireGuardKeyUtils.GeneratePrivateKey();
|
||||
var pub = WireGuardKeyUtils.GetPublicKey(priv);
|
||||
|
||||
var privB64 = Convert.ToBase64String(priv);
|
||||
Assert.Equal(priv, WireGuardKeyUtils.FromBase64(privB64));
|
||||
Assert.Equal(WireGuardKeyUtils.KeySize, pub.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rfc7748Vector()
|
||||
{
|
||||
// RFC 7748 §6.1.
|
||||
var priv = Convert.FromHexString("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
|
||||
var expected = Convert.FromHexString("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a");
|
||||
|
||||
Assert.Equal(expected, WireGuardKeyUtils.GetPublicKey(priv));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RejectsWrongLength()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => WireGuardKeyUtils.GetPublicKey(new byte[31]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\AS1024.Wireguard.Utils\AS1024.Wireguard.Utils.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
47
tools/AS1024.Wireguard.Utils.Cli/Program.cs
Normal file
47
tools/AS1024.Wireguard.Utils.Cli/Program.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using AS1024.Wireguard.Utils;
|
||||
|
||||
const string Help =
|
||||
"""
|
||||
wgkey - WireGuard key utility
|
||||
|
||||
wgkey gen
|
||||
wgkey pub <private-base64>
|
||||
wgkey decode <key-base64>
|
||||
""";
|
||||
|
||||
if (args.Length == 0 || args[0] is "-h" or "--help" or "help")
|
||||
{
|
||||
Console.WriteLine(Help);
|
||||
return 0;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: stdin mode?
|
||||
switch (args[0])
|
||||
{
|
||||
case "gen":
|
||||
{
|
||||
var priv = WireGuardKeyUtils.GeneratePrivateKey();
|
||||
var pub = WireGuardKeyUtils.GetPublicKey(priv);
|
||||
Console.WriteLine($"private={Convert.ToBase64String(priv)}");
|
||||
Console.WriteLine($"public={Convert.ToBase64String(pub)}");
|
||||
return 0;
|
||||
}
|
||||
case "pub" when args.Length == 2:
|
||||
Console.WriteLine(Convert.ToBase64String(
|
||||
WireGuardKeyUtils.GetPublicKey(WireGuardKeyUtils.FromBase64(args[1]))));
|
||||
return 0;
|
||||
case "decode" when args.Length == 2:
|
||||
Console.WriteLine(Convert.ToHexString(WireGuardKeyUtils.FromBase64(args[1])).ToLowerInvariant());
|
||||
return 0;
|
||||
default:
|
||||
Console.Error.WriteLine(Help);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
catch (Exception e) when (e is ArgumentException or FormatException)
|
||||
{
|
||||
Console.Error.WriteLine(e.Message);
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user