From a6d77c306938e88b5053ef0917e02b47a64d9a99 Mon Sep 17 00:00:00 2001 From: ektrah Date: Mon, 16 Dec 2024 22:19:24 +0100 Subject: [PATCH] Argon2id.cs: Provide overloads that accept/return strings --- src/Geralt.Tests/Argon2idTests.cs | 28 +++++++++++++++++++++----- src/Geralt/Crypto/Argon2id.cs | 16 ++++++++------- src/Geralt/Interop/Interop.Argon2id.cs | 2 +- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/Geralt.Tests/Argon2idTests.cs b/src/Geralt.Tests/Argon2idTests.cs index d3cfb93..8957950 100644 --- a/src/Geralt.Tests/Argon2idTests.cs +++ b/src/Geralt.Tests/Argon2idTests.cs @@ -189,7 +189,7 @@ public void NeedsRehash_Invalid(int hashSize, int iterations, int memorySize) [TestMethod] [DataRow("correct horse battery staple", Argon2id.MinIterations, Argon2id.MinMemorySize)] - public void ComputeHash_Valid_String(string password, int iterations, int memorySize) + public void ComputeHash_String_Valid(string password, int iterations, int memorySize) { Span p = Encoding.UTF8.GetBytes(password); @@ -206,7 +206,7 @@ public void ComputeHash_Valid_String(string password, int iterations, int memory [TestMethod] [DynamicData(nameof(StringTestVectors), DynamicDataSourceType.Method)] - public void VerifyHash_Valid_String(bool expected, string hash, string password) + public void VerifyHash_String_Valid(bool expected, string hash, string password) { Span p = Encoding.UTF8.GetBytes(password); @@ -218,7 +218,7 @@ public void VerifyHash_Valid_String(bool expected, string hash, string password) [TestMethod] [DataRow("$argon2i$v=19$m=4096,t=3,p=1$eXNtbzQwOTFzajAwMDAwMA$Bb7qAql9aguCTBpLP4PVnlBd+ehJ5rX0R7smB/FggOM", "password")] [DataRow("$argon2d$v=19$m=4096,t=3,p=1$YTBxd2k1bXBhZHIwMDAwMA$3MM5BChSl8q+MQED0fql0nwP5ykjHdBrGE0mVJHFEUE", "password")] - public void VerifyHash_Tampered_String(string hash, string password) + public void VerifyHash_String_Tampered(string hash, string password) { var p = Encoding.UTF8.GetBytes(password); @@ -235,10 +235,28 @@ public void VerifyHash_Tampered_String(string hash, string password) [DataRow("$argon2id$v=19$m=4882,t=2,p=1$bA81arsiX", "")] [DataRow("$argon2id$v=19$m=4882,t=2,p=1$bA81arsiXysd3WbTRzmEOw$Nm8QBM+7", "")] [DataRow("$argon2id$v=19$m=4882,t=2,p=1$bA81arsiXysd3WbTRzmEOw$Nm8QBM+7RH1DXo9rvp5cwKEOOOfD2g6JuxlXihoNcp", "")] - public void VerifyHash_Invalid_String(string hash, string password) + public void VerifyHash_String_Invalid(string hash, string password) { var p = Encoding.UTF8.GetBytes(password); - Assert.IsFalse(Argon2id.VerifyHash(hash, p)); + bool valid = Argon2id.VerifyHash(hash, p); + Assert.IsFalse(valid); + } + + [TestMethod] + [DataRow("$argon2id$", "")] + [DataRow("$argon2id$v=1", "")] + [DataRow("$argon2id$v=19", "")] + [DataRow("$argon2id$v=19$", "")] + [DataRow("$argon2id$v=19$m=4882,t=", "")] + [DataRow("$argon2id$v=19$m=4882,t=2,p=1$", "")] + [DataRow("$argon2id$v=19$m=4882,t=2,p=1$bA81arsiX", "")] + [DataRow("$argon2id$v=19$m=4882,t=2,p=1$bA81arsiXysd3WbTRzmEOw$Nm8QBM+7", "")] + [DataRow("$argon2id$v=19$m=4882,t=2,p=1$bA81arsiXysd3WbTRzmEOw$Nm8QBM+7RH1DXo9rvp5cwKEOOOfD2g6JuxlXihoNcp", "")] + public void NeedsRehash_String_Invalid(string hash, string password) + { + var p = Encoding.UTF8.GetBytes(password); + + Assert.ThrowsException(() => Argon2id.NeedsRehash(hash, Argon2id.MinIterations, Argon2id.MinMemorySize)); } } diff --git a/src/Geralt/Crypto/Argon2id.cs b/src/Geralt/Crypto/Argon2id.cs index 3bbbea7..746f1e5 100644 --- a/src/Geralt/Crypto/Argon2id.cs +++ b/src/Geralt/Crypto/Argon2id.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Runtime.InteropServices; +using System.Text; using static Interop.Libsodium; namespace Geralt; @@ -40,13 +41,15 @@ public static string ComputeHash(ReadOnlySpan password, int iterations, in Validation.NotLessThanMin(nameof(iterations), iterations, MinIterations); Validation.NotLessThanMin(nameof(memorySize), memorySize, MinMemorySize); Sodium.Initialize(); - unsafe - { - sbyte* hash = stackalloc sbyte[crypto_pwhash_STRBYTES]; + nint hash = Marshal.AllocHGlobal(MaxHashSize); + try { int ret = crypto_pwhash_str_alg(hash, password, (ulong)password.Length, (ulong)iterations, (nuint)memorySize, crypto_pwhash_argon2id_ALG_ARGON2ID13); if (ret != 0) { throw new InsufficientMemoryException("Insufficient memory to perform password hashing."); } - return new string(hash); + return Marshal.PtrToStringAnsi(hash)!; } + finally { + Marshal.FreeHGlobal(hash); + } } public static bool VerifyHash(ReadOnlySpan hash, ReadOnlySpan password) @@ -96,8 +99,7 @@ private static void ThrowIfInvalidHashPrefix(ReadOnlySpan hash) private static void ThrowIfInvalidHashPrefix(string hash) { - if (!hash.StartsWith(HashPrefix)) - { + if (!hash.StartsWith(HashPrefix)) { throw new FormatException("Invalid encoded password hash prefix."); } } diff --git a/src/Geralt/Interop/Interop.Argon2id.cs b/src/Geralt/Interop/Interop.Argon2id.cs index bb1ad9a..c56041d 100644 --- a/src/Geralt/Interop/Interop.Argon2id.cs +++ b/src/Geralt/Interop/Interop.Argon2id.cs @@ -23,7 +23,7 @@ internal static partial class Libsodium [LibraryImport(DllName)] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - internal static unsafe partial int crypto_pwhash_str_alg(sbyte* hash, ReadOnlySpan password, ulong passwordLength, ulong iterations, nuint memorySize, int algorithm); + internal static partial int crypto_pwhash_str_alg(nint hash, ReadOnlySpan password, ulong passwordLength, ulong iterations, nuint memorySize, int algorithm); [LibraryImport(DllName)] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]