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/NostrEncryptedMessage.cs | 234 +++++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrEncryptedMessage.cs (limited to 'wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrEncryptedMessage.cs') diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrEncryptedMessage.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrEncryptedMessage.cs new file mode 100644 index 0000000..c70839c --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrEncryptedMessage.cs @@ -0,0 +1,234 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using VNLib.Utils.Extensions; +using VNLib.Utils.Memory; + +using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt; + +namespace VNLib.Utils.Cryptography.Noscrypt +{ + + public sealed class NostrEncryptedMessage(IEncryptionVersion version, INostrCrypto lib) : VnDisposeable + { + private readonly INostrCrypto library = lib; + + private NCSecretKey _fromKey; + private NCPublicKey _toKey; + private Buffer32 _nonce32; + + /// + /// The message encryption version used by this instance + /// + public uint Version { get; } = version.Version; + + /// + /// The message nonce created during encryption event + /// + public unsafe Span Nonce + { + get + { + Debug.Assert(NC_ENCRYPTION_NONCE_SIZE == sizeof(Buffer32)); + return MemoryMarshal.CreateSpan(ref GetNonceRef(), sizeof(Buffer32)); + } + } + + /// + /// Gets the size of the buffer required to encrypt the specified data size + /// + /// The size of the message raw plaintext message to send + /// The minimum number of bytes required for message encryption output + /// + public int GetOutputBufferSize(int dataSize) + => version.CalcBufferSize(dataSize); + + /// + /// Sets the encryption secret key for the message + /// + /// The secret key buffer + /// The current instance for chaining + /// + public NostrEncryptedMessage SetSecretKey(ReadOnlySpan secKey) + => SetSecretKey(in NCUtil.AsSecretKey(secKey)); + + /// + /// Sets the encryption secret key for the message + /// + /// The secret key structure to copy + /// The current instance for chaining + /// + public NostrEncryptedMessage SetSecretKey(ref readonly NCSecretKey secKey) + { + MemoryUtil.CloneStruct(in secKey, ref _fromKey); + return this; + } + + /// + /// Assigns the public key used to encrypt the message as the + /// receiver of the message + /// + /// The user's public key receiving the message + /// The current instance for chaining + /// + public NostrEncryptedMessage SetPublicKey(ReadOnlySpan pubKey) + => SetPublicKey(in NCUtil.AsPublicKey(pubKey)); + + /// + /// Assigns the public key used to encrypt the message as the + /// receiver of the message + /// + /// The user's public key receiving the message + /// The current instance for chaining + /// + public NostrEncryptedMessage SetPublicKey(ref readonly NCPublicKey pubKey) + { + MemoryUtil.CloneStruct(in pubKey, ref _toKey); + return this; + } + + /// + /// Assigns the nonce to the message. Must be + /// in length + /// + /// The nonce value to copy + /// The current instance for chaining + /// + public NostrEncryptedMessage SetNonce(ReadOnlySpan nonce) + { + MemoryUtil.CopyStruct(nonce, ref _nonce32); + return this; + } + + /// + /// Assigns a random nonce using the specified random source + /// + /// The random source to genrate a random nonce from + /// The current instance for chaining + public NostrEncryptedMessage SetRandomNonce(IRandomSource rng) + { + rng.GetRandomBytes(Nonce); + return this; + } + + /// + /// Encrypts the plaintext message and writes the encrypted message to the + /// specified buffer, along with a 32 byte mac of the message + /// + /// The plaintext data to encrypt + /// The message output buffer to write encrypted data to + /// A buffer to write the computed message mac to + /// The number of bytes writtn to the message output buffer + /// + /// The message buffer must be at-least the size of the output buffer, and it is not + /// initialized before the encryption operation. + /// + /// + public int EncryptMessage(ReadOnlySpan plaintext, Span message, Span macOut32) + { + return Version switch + { + NC_ENC_VERSION_NIP44 => EncryptNip44(plaintext, message, macOut32), + _ => throw new NotSupportedException("NIP04 encryption is not supported"), + }; + } + + private int EncryptNip44(ReadOnlySpan plaintext, Span message, Span macOut32) + { + int payloadSize = GetOutputBufferSize(plaintext.Length); + + ArgumentOutOfRangeException.ThrowIfZero(plaintext.Length, nameof(plaintext)); + ArgumentOutOfRangeException.ThrowIfZero(message.Length, nameof(message)); + ArgumentOutOfRangeException.ThrowIfLessThan(message.Length, payloadSize, nameof(message)); + ArgumentOutOfRangeException.ThrowIfLessThan(macOut32.Length, NC_ENCRYPTION_MAC_SIZE, nameof(macOut32)); + + /* + * Alloc temp buffer to copy formatted payload to data to for the encryption + * operation. Encryption will write directly to the message buffer + */ + + using UnsafeMemoryHandle ptPayloadBuf = MemoryUtil.UnsafeAllocNearestPage(payloadSize, true); + using UnsafeMemoryHandle hmacKeyBuf = MemoryUtil.UnsafeAlloc(NC_HMAC_KEY_SIZE, true); + Debug.Assert(hmacKeyBuf.Length == NC_HMAC_KEY_SIZE); + + Nip44Util.FormatBuffer(plaintext, ptPayloadBuf.Span, false); + + library.EncryptNip44( + in _fromKey, + in _toKey, + in GetNonceRef(), + in ptPayloadBuf.GetReference(), + ref MemoryMarshal.GetReference(message), + (uint)payloadSize, //IMPORTANT: Format buffer will pad the buffer to the exact size + ref hmacKeyBuf.GetReference() //Must set the hmac key buffer + ); + + //Safe to clear the plain text copy buffer + MemoryUtil.InitializeBlock( + ref ptPayloadBuf.GetReference(), + ptPayloadBuf.GetIntLength() + ); + + + //Compute message mac, key should be set by the encryption operation + library.ComputeMac( + in hmacKeyBuf.GetReference(), + in MemoryMarshal.GetReference(message), + (uint)payloadSize, //Again set exact playload size + ref MemoryMarshal.GetReference(macOut32) + ); + + //Safe to clear the hmac key buffer + MemoryUtil.InitializeBlock( + ref hmacKeyBuf.GetReference(), + hmacKeyBuf.GetIntLength() + ); + + return payloadSize; + } + + private ref byte GetNonceRef() => ref Unsafe.As(ref _nonce32); + + protected override void Free() + { + //Zero all internal memory + MemoryUtil.ZeroStruct(ref _fromKey); + MemoryUtil.ZeroStruct(ref _toKey); + MemoryUtil.ZeroStruct(ref _nonce32); + } + + /// + /// Initializes a new with the nip44 encryption + /// method. + /// + /// The nostr crypto implementation instance to use + /// The intialzied message instance + public static NostrEncryptedMessage CreateNip44Cipher(INostrCrypto lib) + => new(NCNip44EncryptionVersion.Instance, lib); + + + [StructLayout(LayoutKind.Sequential, Size = 32)] + unsafe struct Buffer32 + { + fixed byte value[32]; + } + } + +} -- cgit