From 5e32450ccf9186e86a7596a7d774621cf81c62ff Mon Sep 17 00:00:00 2001 From: vnugent Date: Mon, 10 Jun 2024 22:08:52 -0400 Subject: feat: Begin migrating noscrypt c# library --- .../src/Nip44Util.cs | 144 +++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Util.cs (limited to 'wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Util.cs') diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Util.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Util.cs new file mode 100644 index 0000000..23c9127 --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Util.cs @@ -0,0 +1,144 @@ +// Copyright (C) 2024 Vaughn Nugent +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +using System; +using System.Buffers.Binary; +using System.Runtime.InteropServices; + +using VNLib.Utils.Memory; + +using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt; + +namespace VNLib.Utils.Cryptography.Noscrypt +{ + + /// + /// Provides a set of utility methods for working with the Noscrypt library + /// + public static class Nip44Util + { + /// + /// Calculates the required NIP44 encryption buffer size for + /// the specified input data size + /// + /// The size (in bytes) of the encoded data to encrypt + /// The exact size of the padded buffer output + public static int CalcBufferSize(int dataSize) + { + /* + * Taken from https://github.com/nostr-protocol/nips/blob/master/44.md + * + * Not gonna lie, kinda dumb branches. I guess they want to save space + * with really tiny messages... Dunno, but whatever RTFM + */ + + //Min message size is 32 bytes + int minSize = Math.Max(dataSize, 32); + + //find the next power of 2 that will fit the min size + int nexPower = 1 << ((int)Math.Log2(minSize - 1)) + 1; + + int chunk = nexPower <= 256 ? 32 : nexPower / 8; + + return (chunk * ((int)Math.Floor((double)((minSize - 1) / chunk)) + 1)) + sizeof(ushort); + } + + /// + /// Calculates the final buffer size required to hold the encrypted data + /// + /// The size (in bytes) of plaintext data to encrypt + /// The number of bytes required to store the final nip44 message + public static int CalcFinalBufferSize(int dataSize) + { + /* version + nonce + payload + mac */ + return CalcBufferSize(dataSize) + NC_ENCRYPTION_NONCE_SIZE + NC_ENCRYPTION_MAC_SIZE + 1; + } + + /// + /// Formats the plaintext data into a buffer that can be properly encrypted. + /// The output buffer must be zeroed, or can be zeroed using the + /// parameter. Use + /// to determine the required output buffer size. + /// + /// A buffer containing plaintext data to copy to the output + /// The output data buffer to format + /// A value that indicates if the buffer should be zeroed before use + public static void FormatBuffer(ReadOnlySpan plaintextData, Span output, bool zeroOutput) + { + //First zero out the buffer + if (zeroOutput) + { + MemoryUtil.InitializeBlock(output); + } + + //Make sure the output buffer is large enough so we dont overrun it + ArgumentOutOfRangeException.ThrowIfLessThan(output.Length, plaintextData.Length + sizeof(ushort), nameof(output)); + + //Write the data size to the first 2 bytes + ushort dataSize = (ushort)plaintextData.Length; + BinaryPrimitives.WriteUInt16BigEndian(output, dataSize); + + //Copy the plaintext data to the output buffer after the data size + MemoryUtil.Memmove( + in MemoryMarshal.GetReference(plaintextData), + 0, + ref MemoryMarshal.GetReference(output), + sizeof(ushort), + (uint)plaintextData.Length + ); + + //We assume the remaining buffer is zeroed out + } + + public static ReadOnlySpan GetNonceFromPayload(ReadOnlySpan message) + { + //The nonce is 32 bytes following the 1st byte version number of the message + return message.Slice(1, NC_ENCRYPTION_NONCE_SIZE); + } + + public static ReadOnlySpan GetCiphertextFromPayload(ReadOnlySpan message) + { + //Message is between the nonce and the trailing mac + int payloadSize = message.Length - (1 + NC_ENCRYPTION_NONCE_SIZE + NC_ENCRYPTION_MAC_SIZE); + return message.Slice(1 + NC_ENCRYPTION_NONCE_SIZE, payloadSize); + } + + public static ReadOnlySpan GetMacFromPayload(ReadOnlySpan message) + { + //The mac is the last 32 bytes of the message + return message[^NC_ENCRYPTION_MAC_SIZE..]; + } + + public static ReadOnlySpan GetNonceAndCiphertext(ReadOnlySpan message) + { + //The nonce is 32 bytes following the 1st byte version number of the message + return message.Slice(1, NC_ENCRYPTION_NONCE_SIZE + GetCiphertextFromPayload(message).Length); + } + + public static byte GetMessageVersion(ReadOnlySpan message) + { + //The first byte is the message version + return message[0]; + } + + public static ReadOnlySpan GetPlaintextMessage(ReadOnlySpan plaintextPayload) + { + ushort ptLength = BinaryPrimitives.ReadUInt16BigEndian(plaintextPayload); + return plaintextPayload.Slice(sizeof(ushort), ptLength); + } + + } + +} -- cgit