Write out the high-level API I need.

This commit is contained in:
Pieter-Jan Briers
2022-03-28 01:13:24 +02:00
parent abe853722d
commit d19f9c9e5b
7 changed files with 285 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
namespace SpaceWizards.Sodium;
using static Interop.Libsodium;
/// <summary>
/// Wrappers around the <c>crypto_aead_xchacha20poly1305_ietf_</c> APIs.
/// </summary>
public static class CryptoAeadXChaCha20Poly1305Ietf
{
static CryptoAeadXChaCha20Poly1305Ietf()
{
SodiumCore.EnsureInit();
}
public const int NoncePublicBytes = (int)crypto_aead_xchacha20poly1305_ietf_NPUBBYTES;
public const int KeyBytes = (int)crypto_aead_xchacha20poly1305_ietf_KEYBYTES;
public const int AddBytes = (int)crypto_aead_xchacha20poly1305_ietf_ABYTES;
public static unsafe void Keygen(Span<byte> key)
{
if (key.Length != KeyBytes)
throw new ArgumentException($"Key must be {nameof(KeyBytes)} bytes");
fixed (byte* k = key)
{
crypto_aead_xchacha20poly1305_ietf_keygen(k);
}
}
public static byte[] Keygen()
{
var key = new byte[KeyBytes];
Keygen(key);
return key;
}
public static unsafe bool Encrypt(
Span<byte> cipher,
out int cipherLength,
ReadOnlySpan<byte> message,
ReadOnlySpan<byte> additionalData,
ReadOnlySpan<byte> noncePublic,
ReadOnlySpan<byte> key)
{
if (cipher.Length < checked(message.Length + AddBytes))
throw new ArgumentException("Destination is too short");
if (key.Length != KeyBytes)
throw new ArgumentException($"Key must be {nameof(KeyBytes)} bytes");
if (noncePublic.Length != NoncePublicBytes)
throw new ArgumentException($"Nonce must be {nameof(NoncePublicBytes)} bytes");
fixed (byte* c = cipher)
fixed (byte* m = message)
fixed (byte* ad = additionalData)
fixed (byte* npub = noncePublic)
fixed (byte* k = key)
{
ulong clen;
var ret = crypto_aead_xchacha20poly1305_ietf_encrypt(
c, &clen,
m, (ulong)message.Length,
ad, (ulong)additionalData.Length,
null,
npub,
k);
cipherLength = (int)clen;
return ret == 0;
}
}
public static unsafe bool Decrypt(
Span<byte> message,
out int messageLength,
ReadOnlySpan<byte> cipher,
ReadOnlySpan<byte> additionalData,
ReadOnlySpan<byte> noncePublic,
ReadOnlySpan<byte> key)
{
if (cipher.Length < AddBytes)
throw new ArgumentException("Input is too short");
if (message.Length < cipher.Length - AddBytes)
throw new ArgumentException("Output is too short");
if (key.Length != KeyBytes)
throw new ArgumentException($"Key must be {nameof(KeyBytes)} bytes");
if (noncePublic.Length != NoncePublicBytes)
throw new ArgumentException($"Nonce must be {nameof(NoncePublicBytes)} bytes");
fixed (byte* c = cipher)
fixed (byte* m = message)
fixed (byte* ad = additionalData)
fixed (byte* npub = noncePublic)
fixed (byte* k = key)
{
ulong mlen;
var ret = crypto_aead_xchacha20poly1305_ietf_decrypt(
m, &mlen,
null,
c, (ulong)cipher.Length,
ad, (ulong)additionalData.Length,
npub,
k);
messageLength = (int)mlen;
return ret == 0;
}
}
}

View File

@@ -0,0 +1,94 @@
namespace SpaceWizards.Sodium;
using static Interop.Libsodium;
/// <summary>
/// Wrappers around the <c>crypto_box</c> APIs.
/// </summary>
public static class CryptoBox
{
static CryptoBox()
{
SodiumCore.EnsureInit();
}
public const int SealBytes = (int)crypto_box_SEALBYTES;
public const int PublicKeyBytes = (int)crypto_box_PUBLICKEYBYTES;
public const int SecretKeyBytes = (int)crypto_box_SECRETKEYBYTES;
public static unsafe bool KeyPair(Span<byte> publicKey, Span<byte> secretKey)
{
if (publicKey.Length != PublicKeyBytes)
throw new ArgumentException($"Public key must be {nameof(PublicKeyBytes)} bytes.");
if (secretKey.Length != SecretKeyBytes)
throw new ArgumentException($"Secret key must be {nameof(SecretKeyBytes)} bytes.");
fixed (byte* pk = publicKey)
fixed (byte* sk = secretKey)
{
return crypto_box_keypair(pk, sk) == 0;
}
}
public static unsafe bool Seal(Span<byte> cipher, ReadOnlySpan<byte> message, ReadOnlySpan<byte> publicKey)
{
if (cipher.Length < checked(message.Length + SealBytes))
throw new ArgumentException("Destination is too short");
fixed (byte* c = cipher)
fixed (byte* m = message)
fixed (byte* pk = publicKey)
{
return crypto_box_seal(c, m, (ulong)message.Length, pk) == 0;
}
}
public static byte[] Seal(ReadOnlySpan<byte> message, ReadOnlySpan<byte> publicKey)
{
var cipher = new byte[message.Length + SealBytes];
if (!Seal(cipher, message, publicKey))
throw new SodiumException("Seal failed");
return cipher;
}
public static unsafe bool SealOpen(
Span<byte> message,
ReadOnlySpan<byte> cipher,
ReadOnlySpan<byte> publicKey,
ReadOnlySpan<byte> secretKey)
{
if (cipher.Length < SealBytes)
throw new ArgumentException("Input is too short");
if (message.Length < (cipher.Length - SealBytes))
throw new ArgumentException("Destination is too short");
if (publicKey.Length != PublicKeyBytes)
throw new ArgumentException($"Public key must be {nameof(PublicKeyBytes)} bytes.");
if (secretKey.Length != SecretKeyBytes)
throw new ArgumentException($"Secret key must be {nameof(SecretKeyBytes)} bytes.");
fixed (byte* m = message)
fixed (byte* c = cipher)
fixed (byte* pk = publicKey)
fixed (byte* sk = secretKey)
{
return crypto_box_seal_open(m, c, (ulong)cipher.Length, pk, sk) == 0;
}
}
public static byte[] SealOpen(ReadOnlySpan<byte> cipher, ReadOnlySpan<byte> publicKey, ReadOnlySpan<byte> secretKey)
{
if (cipher.Length < SealBytes)
throw new ArgumentException("Input is too short");
var message = new byte[cipher.Length - SealBytes];
if (!SealOpen(message, cipher, publicKey, secretKey))
throw new SodiumException("SealOpen failed");
return message;
}
}

View File

@@ -0,0 +1,26 @@
using static SpaceWizards.Sodium.Interop.Libsodium;
namespace SpaceWizards.Sodium;
public static class SodiumCore
{
/// <summary>
/// Directly call <see cref="sodium_init"/>.
/// </summary>
/// <returns>0 on success, 1 if already initialized, -1 on initialize failure.</returns>
/// <seealso cref="EnsureInit"/>
public static int Init()
{
return sodium_init();
}
/// <summary>
/// Try to ensure libsodium is initialized, throwing if it fails to initialize.
/// </summary>
/// <exception cref="SodiumInitException">Thrown if initialization of libsodium failed.</exception>
public static void EnsureInit()
{
if (Init() == -1)
throw new SodiumInitException("Failed to init libsodium!");
}
}

View File

@@ -0,0 +1,16 @@
namespace SpaceWizards.Sodium;
public sealed class SodiumException : Exception
{
public SodiumException()
{
}
public SodiumException(string message) : base(message)
{
}
public SodiumException(string message, Exception inner) : base(message, inner)
{
}
}

View File

@@ -0,0 +1,17 @@
namespace SpaceWizards.Sodium;
[Serializable]
public sealed class SodiumInitException : Exception
{
public SodiumInitException()
{
}
public SodiumInitException(string message) : base(message)
{
}
public SodiumInitException(string message, Exception inner) : base(message, inner)
{
}
}

View File

@@ -4,6 +4,17 @@
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PackageId>SpaceWizards.Sodium</PackageId>
<Version>0.1.0</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Description>Simple API binding for libsodium.</Description>
<NoWarn>CS1591</NoWarn>
<PackageTags>libsodium</PackageTags>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SpaceWizards.Sodium.Interop\SpaceWizards.Sodium.Interop.csproj" />
</ItemGroup>
</Project>