From 304c7c60ddcad8f8dc7802fbf33609754e3be2a4 Mon Sep 17 00:00:00 2001 From: joaomatossilva Date: Fri, 26 May 2023 17:12:58 +0100 Subject: [PATCH 01/26] Implementing ed25519ph --- src/Cryptography/Ed25519.cs | 24 ++- src/Cryptography/Ed25519Ph.cs | 127 ++++++++++++++ src/Cryptography/Error.cs | 5 + src/Cryptography/Error.resx | 3 + src/Cryptography/IncrementalSignature.cs | 136 +++++++++++++++ src/Cryptography/SignatureAlgorithm.cs | 37 ++++ src/Interop/Interop.Sign.Ed25519.cs | 28 +++ tests/Base/IncrementalSignatureTests.cs | 206 +++++++++++++++++++++++ tests/Examples/Incremental.cs | 78 +++++++++ tests/Registry.cs | 7 + 10 files changed, 650 insertions(+), 1 deletion(-) create mode 100644 src/Cryptography/Ed25519Ph.cs create mode 100644 src/Cryptography/IncrementalSignature.cs create mode 100644 tests/Base/IncrementalSignatureTests.cs diff --git a/src/Cryptography/Ed25519.cs b/src/Cryptography/Ed25519.cs index dc7fac40..e8feb060 100644 --- a/src/Cryptography/Ed25519.cs +++ b/src/Cryptography/Ed25519.cs @@ -32,7 +32,7 @@ namespace NSec.Cryptography // // Signature Size - 64 bytes. // - public sealed class Ed25519 : SignatureAlgorithm + public class Ed25519 : SignatureAlgorithm { private static readonly PrivateKeyFormatter s_nsecPrivateKeyFormatter = new Ed25519PrivateKeyFormatter(new byte[] { 0xDE, 0x64, 0x42, 0xDE, crypto_sign_ed25519_SEEDBYTES, 0, crypto_sign_ed25519_BYTES, 0 }); @@ -66,6 +66,8 @@ public sealed class Ed25519 : SignatureAlgorithm private static int s_selfTest; + public override bool SupportsPartialUpdated => false; + public Ed25519() : base( privateKeySize: crypto_sign_ed25519_SEEDBYTES, publicKeySize: crypto_sign_ed25519_PUBLICKEYBYTES, @@ -223,6 +225,26 @@ private protected unsafe override bool VerifyCore( } } + internal override void InitializeCore(out IncrementalSignatureState state) + { + throw Error.NotSupported_Algorithm(); + } + + internal override void UpdateCore(ref IncrementalSignatureState state, ReadOnlySpan data) + { + throw Error.NotSupported_Algorithm(); + } + + internal override void FinalSignCore(ref IncrementalSignatureState state, SecureMemoryHandle keyHandle, Span signature) + { + throw Error.NotSupported_Algorithm(); + } + + internal override bool FinalVerifyCore(ref IncrementalSignatureState state, in PublicKeyBytes publicKeyBytes, ReadOnlySpan signature) + { + throw Error.NotSupported_Algorithm(); + } + private static void SelfTest() { if ((crypto_sign_ed25519_bytes() != crypto_sign_ed25519_BYTES) || diff --git a/src/Cryptography/Ed25519Ph.cs b/src/Cryptography/Ed25519Ph.cs new file mode 100644 index 00000000..dba05665 --- /dev/null +++ b/src/Cryptography/Ed25519Ph.cs @@ -0,0 +1,127 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; +using NSec.Cryptography.Formatting; +using static Interop.Libsodium; + +namespace NSec.Cryptography +{ + // + // Ed25519ph + // + // Digital Signature Algorithm (EdDSA) pre-hashed based on the edwards25519 curve + // + // References: + // + // RFC 8032 - Edwards-Curve Digital Signature Algorithm (EdDSA) + // + // RFC 5958 - Asymmetric Key Packages + // + // RFC 8410 - Algorithm Identifiers for Ed25519, Ed448, X25519, and + // X448 for Use in the Internet X.509 Public Key Infrastructure + // + // Parameters: + // + // Private Key Size - The private key is 32 bytes (256 bits). However, + // the libsodium representation of a private key is 64 bytes. We + // expose private keys as 32-byte byte strings and internally + // convert from/to the libsodium format as necessary. + // + // Public Key Size - 32 bytes. + // + // Signature Size - 64 bytes. + // + public sealed class Ed25519Ph : Ed25519 + { + public override bool SupportsPartialUpdated => true; + + private protected override void SignCore( + SecureMemoryHandle keyHandle, + ReadOnlySpan data, + Span signature) + { + Debug.Assert(keyHandle.Size == crypto_sign_ed25519_SECRETKEYBYTES); + Debug.Assert(signature.Length == crypto_sign_ed25519_BYTES); + + InitializeCore(out IncrementalSignatureState state); + UpdateCore(ref state, data); + FinalSignCore(ref state, keyHandle, signature); + } + + private protected override bool VerifyCore( + in PublicKeyBytes publicKeyBytes, + ReadOnlySpan data, + ReadOnlySpan signature) + { + if (Unsafe.SizeOf() != crypto_sign_ed25519_PUBLICKEYBYTES) + { + throw Error.InvalidOperation_InternalError(); + } + + Debug.Assert(signature.Length == crypto_sign_ed25519_BYTES); + + InitializeCore(out IncrementalSignatureState state); + UpdateCore(ref state, data); + return FinalVerifyCore(ref state, publicKeyBytes, signature); + } + + internal unsafe override void InitializeCore( + out IncrementalSignatureState state) + { + fixed (crypto_sign_ed25519ph_state* state_ = &state.ed25519PhState) + { + int error = crypto_sign_ed25519ph_init(state_); + + Debug.Assert(error == 0); + } + } + + internal unsafe override void UpdateCore( + ref IncrementalSignatureState state, + ReadOnlySpan data) + { + fixed (crypto_sign_ed25519ph_state* state_ = &state.ed25519PhState) + fixed (byte* @in = data) + { + int error = crypto_sign_ed25519ph_update(state_, @in, (ulong)data.Length); + + Debug.Assert(error == 0); + } + } + + internal unsafe override void FinalSignCore(ref IncrementalSignatureState state, SecureMemoryHandle keyHandle, Span signature) + { + Debug.Assert(keyHandle.Size == crypto_sign_ed25519_SECRETKEYBYTES); + Debug.Assert(signature.Length == crypto_sign_ed25519_BYTES); + + fixed (crypto_sign_ed25519ph_state* state_ = &state.ed25519PhState) + fixed (byte* sig = signature) + { + int error = crypto_sign_ed25519ph_final_create(state_, sig, out ulong signatureLength, keyHandle); + + Debug.Assert(error == 0); + Debug.Assert((ulong)signature.Length == signatureLength); + } + } + + internal unsafe override bool FinalVerifyCore(ref IncrementalSignatureState state, in PublicKeyBytes publicKeyBytes, ReadOnlySpan signature) + { + if (Unsafe.SizeOf() != crypto_sign_ed25519_PUBLICKEYBYTES) + { + throw Error.InvalidOperation_InternalError(); + } + + Debug.Assert(signature.Length == crypto_sign_ed25519_BYTES); + + fixed (crypto_sign_ed25519ph_state* state_ = &state.ed25519PhState) + fixed (byte* sig = signature) + fixed (PublicKeyBytes* pk = &publicKeyBytes) + { + int error = crypto_sign_ed25519ph_final_verify(state_, sig, pk); + + return error == 0; + } + } + } +} diff --git a/src/Cryptography/Error.cs b/src/Cryptography/Error.cs index d988ac92..a39235ac 100644 --- a/src/Cryptography/Error.cs +++ b/src/Cryptography/Error.cs @@ -415,6 +415,11 @@ internal static NotSupportedException NotSupported_KeyConversion( return new NotSupportedException(string.Format(ResourceManager.GetString(nameof(NotSupported_KeyConversion))!, arg0, arg1)); } + internal static NotSupportedException NotSupported_Algorithm() + { + return new NotSupportedException(ResourceManager.GetString(nameof(NotSupported_Algorithm))); + } + internal static NotSupportedException NotSupported_Operation() { return new NotSupportedException(ResourceManager.GetString(nameof(NotSupported_Operation))); diff --git a/src/Cryptography/Error.resx b/src/Cryptography/Error.resx index ad728a24..d582ef66 100644 --- a/src/Cryptography/Error.resx +++ b/src/Cryptography/Error.resx @@ -321,4 +321,7 @@ Could not initialize platform-specific components. NSec may not be supported on this platform. See https://nsec.rocks/docs/install for more information. + + The specified algorithm is not supported for this opeation. + \ No newline at end of file diff --git a/src/Cryptography/IncrementalSignature.cs b/src/Cryptography/IncrementalSignature.cs new file mode 100644 index 00000000..c0d6804a --- /dev/null +++ b/src/Cryptography/IncrementalSignature.cs @@ -0,0 +1,136 @@ +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace NSec.Cryptography +{ + [StructLayout(LayoutKind.Sequential)] + public struct IncrementalSignature + { + private readonly IncrementalSignatureState _state; + private readonly SignatureAlgorithm? _algorithm; + + public SignatureAlgorithm? Algorithm => _algorithm; + + [EditorBrowsable(EditorBrowsableState.Never)] + public static new bool Equals( + object? objA, + object? objB) + { + return object.Equals(objA, objB); + } + + public static void Initialize( + SignatureAlgorithm algorithm, + out IncrementalSignature state) + { + if (algorithm == null) + { + throw Error.ArgumentNull_Algorithm(nameof(algorithm)); + } + + if (!algorithm.SupportsPartialUpdated) + { + throw Error.NotSupported_Algorithm(); + } + + state = default; + algorithm.InitializeCore(out Unsafe.AsRef(in state._state)); + Unsafe.AsRef(in state._algorithm) = algorithm; + } + + public static void Update( + ref IncrementalSignature state, + ReadOnlySpan data) + { + if (state._algorithm == null) + { + throw Error.InvalidOperation_UninitializedState(); + } + + state._algorithm.UpdateCore(ref Unsafe.AsRef(in state._state), data); + } + + public static byte[] FinalSignature( + ref IncrementalSignature state, + Key key) + { + if (state._algorithm == null) + { + throw Error.InvalidOperation_UninitializedState(); + } + + try + { + byte[] signature = new byte[state._algorithm.SignatureSize]; + state._algorithm.FinalSignCore(ref Unsafe.AsRef(in state._state), key.Handle, signature); + return signature; + } + finally + { + Unsafe.AsRef(in state._algorithm) = null; + } + } + + public static void FinalSignature( + ref IncrementalSignature state, + Key key, + Span signature) + { + if (state._algorithm == null) + { + throw Error.InvalidOperation_UninitializedState(); + } + if (signature.Length != state._algorithm.SignatureSize) + { + throw Error.Argument_SignatureLength(nameof(signature), state._algorithm.SignatureSize); + } + + try + { + state._algorithm.FinalSignCore(ref Unsafe.AsRef(in state._state), key.Handle, signature); + } + finally + { + Unsafe.AsRef(in state._algorithm) = null; + } + } + + public static bool FinalVerify( + ref IncrementalSignature state, + PublicKey publicKey, + ReadOnlySpan signature) + { + if (state._algorithm == null) + { + throw Error.InvalidOperation_UninitializedState(); + } + if (publicKey == null) + { + throw Error.ArgumentNull_Key(nameof(publicKey)); + } + if (publicKey.Algorithm != state._algorithm) + { + throw Error.Argument_PublicKeyAlgorithmMismatch(nameof(publicKey), nameof(publicKey)); + } + + try + { + return signature.Length == state._algorithm.SignatureSize && + state._algorithm.FinalVerifyCore(ref Unsafe.AsRef(in state._state), publicKey.GetPinnableReference(), signature); + } + finally + { + Unsafe.AsRef(in state._algorithm) = null; + } + } + } + + [StructLayout(LayoutKind.Explicit)] + internal struct IncrementalSignatureState + { + [FieldOffset(0)] + internal Interop.Libsodium.crypto_sign_ed25519ph_state ed25519PhState; + } +} diff --git a/src/Cryptography/SignatureAlgorithm.cs b/src/Cryptography/SignatureAlgorithm.cs index 0bd0a583..d89ad512 100644 --- a/src/Cryptography/SignatureAlgorithm.cs +++ b/src/Cryptography/SignatureAlgorithm.cs @@ -20,6 +20,7 @@ namespace NSec.Cryptography public abstract class SignatureAlgorithm : Algorithm { private static Ed25519? s_Ed25519; + private static Ed25519Ph? s_Ed25519ph; private readonly int _privateKeySize; private readonly int _publicKeySize; @@ -53,6 +54,25 @@ public static Ed25519 Ed25519 } } + public static Ed25519Ph Ed25519ph + { + get + { + Ed25519Ph? instance = s_Ed25519ph; + if (instance == null) + { + Interlocked.CompareExchange(ref s_Ed25519ph, new Ed25519Ph(), null); + instance = s_Ed25519ph; + } + return instance; + } + } + + public abstract bool SupportsPartialUpdated + { + get; + } + public int PrivateKeySize => _privateKeySize; public int PublicKeySize => _publicKeySize; @@ -136,5 +156,22 @@ private protected abstract bool VerifyCore( in PublicKeyBytes publicKeyBytes, ReadOnlySpan data, ReadOnlySpan signature); + + internal abstract void InitializeCore( + out IncrementalSignatureState state); + + internal abstract void UpdateCore( + ref IncrementalSignatureState state, + ReadOnlySpan data); + + internal abstract void FinalSignCore( + ref IncrementalSignatureState state, + SecureMemoryHandle keyHandle, + Span signature); + + internal abstract bool FinalVerifyCore( + ref IncrementalSignatureState state, + in PublicKeyBytes publicKeyBytes, + ReadOnlySpan signature); } } diff --git a/src/Interop/Interop.Sign.Ed25519.cs b/src/Interop/Interop.Sign.Ed25519.cs index 4a5afca2..1b4dc217 100644 --- a/src/Interop/Interop.Sign.Ed25519.cs +++ b/src/Interop/Interop.Sign.Ed25519.cs @@ -57,5 +57,33 @@ internal static unsafe extern int crypto_sign_ed25519_verify_detached( byte* m, ulong mlen, PublicKeyBytes* pk); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static unsafe extern int crypto_sign_ed25519ph_init( + crypto_sign_ed25519ph_state* state); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static unsafe extern int crypto_sign_ed25519ph_update( + crypto_sign_ed25519ph_state* state, + byte* m, + ulong mlen); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static unsafe extern int crypto_sign_ed25519ph_final_create( + crypto_sign_ed25519ph_state* state, + byte* sig, + out ulong siglen_p, + SecureMemoryHandle sk); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static unsafe extern int crypto_sign_ed25519ph_final_verify( + crypto_sign_ed25519ph_state* state, + byte* sig, + PublicKeyBytes* pk); + + [StructLayout(LayoutKind.Explicit, Size = 208)] + internal struct crypto_sign_ed25519ph_state + { + } } } diff --git a/tests/Base/IncrementalSignatureTests.cs b/tests/Base/IncrementalSignatureTests.cs new file mode 100644 index 00000000..77eb84ee --- /dev/null +++ b/tests/Base/IncrementalSignatureTests.cs @@ -0,0 +1,206 @@ +using System; +using NSec.Cryptography; +using Xunit; + +namespace NSec.Tests.Base +{ + public static class IncrementalSignatureTests + { + public static readonly TheoryData IncrementalSignatureAlgorithms = Registry.IncrementalSignatureAlgorithms; + + #region Initialize + + [Fact] + public static void InitializeWithNullAlgorithm() + { + Assert.Throws("algorithm", () => IncrementalSignature.Initialize(null!, out _)); + } + + [Fact] + public static void InitializeWithUnsupportedAlgorithm() + { + Assert.Throws(() => IncrementalSignature.Initialize(SignatureAlgorithm.Ed25519, out _)); + } + + #endregion + + #region FinalSignature #1 + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalSignatureInvalid(SignatureAlgorithm a) + { + var state = default(IncrementalSignature); + + var key = new Key(a); + + Assert.Throws(() => IncrementalSignature.FinalSignature(ref state, key)); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalSignatureSuccess(SignatureAlgorithm a) + { + var state = default(IncrementalSignature); + + Assert.Null(state.Algorithm); + + IncrementalSignature.Initialize(a, out state); + + Assert.Same(a, state.Algorithm); + + IncrementalSignature.Update(ref state, Utilities.RandomBytes.Slice(0, 100)); + IncrementalSignature.Update(ref state, Utilities.RandomBytes.Slice(100, 100)); + IncrementalSignature.Update(ref state, Utilities.RandomBytes.Slice(200, 100)); + + var key = new Key(a); + var actual = IncrementalSignature.FinalSignature(ref state, key); + + Assert.Null(state.Algorithm); + + var expected = a.Sign(key, Utilities.RandomBytes.Slice(0, 300)); + + Assert.Equal(expected, actual); + } + + #endregion + + #region FinalSignature #2 + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalSignatureWithSpanInvalid(SignatureAlgorithm a) + { + var state = default(IncrementalSignature); + + var key = new Key(a); + + Assert.Throws(() => IncrementalSignature.FinalSignature(ref state, key, Span.Empty)); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalSignatureWithSpanTooSmall(SignatureAlgorithm a) + { + var key = new Key(a); + + IncrementalSignature.Initialize(a, out var state); + + Assert.Throws("signature", () => IncrementalSignature.FinalSignature(ref state, key, new byte[a.SignatureSize - 1])); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalizeWithSpanTooLarge(SignatureAlgorithm a) + { + var key = new Key(a); + + IncrementalSignature.Initialize(a, out var state); + + Assert.Throws("signature", () => IncrementalSignature.FinalSignature(ref state, key, new byte[a.SignatureSize + 1])); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalSignatureWithSpanSuccess(SignatureAlgorithm a) + { + var state = default(IncrementalSignature); + + Assert.Null(state.Algorithm); + + IncrementalSignature.Initialize(a, out state); + + Assert.Same(a, state.Algorithm); + + IncrementalSignature.Update(ref state, Utilities.RandomBytes.Slice(0, 100)); + IncrementalSignature.Update(ref state, Utilities.RandomBytes.Slice(100, 100)); + IncrementalSignature.Update(ref state, Utilities.RandomBytes.Slice(200, 100)); + + var key = new Key(a); + + var actual = new byte[a.SignatureSize]; + IncrementalSignature.FinalSignature(ref state, key, actual); + + Assert.Null(state.Algorithm); + + var expected = a.Sign(key, Utilities.RandomBytes.Slice(0, 300)); + + Assert.Equal(expected, actual); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalSignatureWithSpanSuccessNoUpdate(SignatureAlgorithm a) + { + var state = default(IncrementalSignature); + + Assert.Null(state.Algorithm); + + IncrementalSignature.Initialize(a, out state); + + Assert.Same(a, state.Algorithm); + + var key = new Key(a); + + var actual = new byte[a.SignatureSize]; + IncrementalSignature.FinalSignature(ref state, key, actual); + + Assert.Null(state.Algorithm); + + var expected = a.Sign(key, ReadOnlySpan.Empty); + + Assert.Equal(expected, actual); + } + + #endregion + + #region FinalVerify + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalVerifyInvalid(SignatureAlgorithm a) + { + var state = default(IncrementalSignature); + + var key = new Key(a); + + Assert.Throws(() => IncrementalSignature.FinalVerify(ref state, key.PublicKey, Span.Empty)); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalVerifyNullKey(SignatureAlgorithm a) + { + var state = default(IncrementalSignature); + + Assert.Throws(() => IncrementalSignature.FinalVerify(ref state, null!, Span.Empty)); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalVerifyKeyFromdifferentAlgorithm(SignatureAlgorithm a) + { + var state = default(IncrementalSignature); + + var key = new Key(SignatureAlgorithm.Ed25519); + + Assert.Throws(() => IncrementalSignature.FinalVerify(ref state, key.PublicKey, Span.Empty)); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalVerifySuccess(SignatureAlgorithm a) + { + IncrementalSignature.Initialize(a, out var state); + + var key = new Key(a); + var signature = a.Sign(key, ReadOnlySpan.Empty); + + Assert.True(IncrementalSignature.FinalVerify(ref state, key.PublicKey, signature)); + + Assert.Null(state.Algorithm); + } + + #endregion + } +} diff --git a/tests/Examples/Incremental.cs b/tests/Examples/Incremental.cs index e798a492..90e12691 100644 --- a/tests/Examples/Incremental.cs +++ b/tests/Examples/Incremental.cs @@ -77,5 +77,83 @@ public static void Mac() Assert.Equal(algorithm.Mac(key, Encoding.UTF8.GetBytes(string.Concat(lines))), mac); } + + [Fact] + public static void Signature() + { + #region Incremental Signature + + // select the Ed25519ph algorithm + var algorithm = SignatureAlgorithm.Ed25519ph; + + // create a new key + using var key = Key.Create(algorithm); + + // initialize the state with the key + IncrementalSignature.Initialize(algorithm, out var state); + + // incrementally update the state with some data + var lines = new[] + { + "It is a dark time for the\n", + "Rebellion. Although the Death\n", + "Star has been destroyed,\n", + "Imperial troops have driven the\n", + "Rebel forces from their hidden\n", + "base and pursued them across\n", + "the galaxy.\n" + }; + foreach (var line in lines) + { + IncrementalSignature.Update(ref state, Encoding.UTF8.GetBytes(line)); + } + + // finalize the computation and get the result + var signature = IncrementalSignature.FinalSignature(ref state, key); + + #endregion + + Assert.Equal(algorithm.Sign(key, Encoding.UTF8.GetBytes(string.Concat(lines))), signature); + } + + [Fact] + public static void Verify() + { + #region Incremental Signature Verification + + // select the Ed25519ph algorithm + var algorithm = SignatureAlgorithm.Ed25519ph; + + // create a new key + using var key = Key.Create(algorithm); + + // initialize the state with the key + IncrementalSignature.Initialize(algorithm, out var state); + + // incrementally update the state with some data + var lines = new[] + { + "It is a dark time for the\n", + "Rebellion. Although the Death\n", + "Star has been destroyed,\n", + "Imperial troops have driven the\n", + "Rebel forces from their hidden\n", + "base and pursued them across\n", + "the galaxy.\n" + }; + foreach (var line in lines) + { + IncrementalSignature.Update(ref state, Encoding.UTF8.GetBytes(line)); + } + + // calculate the signature + var signature = algorithm.Sign(key, Encoding.UTF8.GetBytes(string.Concat(lines))); + + //verify the signature + var isValid = IncrementalSignature.FinalVerify(ref state, key.PublicKey, signature); + #endregion + + Assert.True(isValid); + } } } diff --git a/tests/Registry.cs b/tests/Registry.cs index 04bbbc52..f4a7c43f 100644 --- a/tests/Registry.cs +++ b/tests/Registry.cs @@ -80,6 +80,12 @@ internal static class Registry public static readonly TheoryData SignatureAlgorithms = new() { SignatureAlgorithm.Ed25519, + SignatureAlgorithm.Ed25519ph, + }; + + public static readonly TheoryData IncrementalSignatureAlgorithms = new() + { + SignatureAlgorithm.Ed25519ph, }; #endregion @@ -90,6 +96,7 @@ internal static class Registry { KeyAgreementAlgorithm.X25519, SignatureAlgorithm.Ed25519, + SignatureAlgorithm.Ed25519ph, }; public static readonly TheoryData SymmetricAlgorithms = new() From 86bffc0d51b45803b5c70f7794e1935358d1b302 Mon Sep 17 00:00:00 2001 From: ektrah Date: Sat, 10 Jun 2023 11:42:20 +0200 Subject: [PATCH 02/26] Improve implementation of ed25519ph --- src/Cryptography/Ed25519.cs | 27 +-- src/Cryptography/Ed25519Ph.cs | 127 ---------- src/Cryptography/Ed25519ph.cs | 295 +++++++++++++++++++++++ src/Cryptography/Error.cs | 5 - src/Cryptography/Error.resx | 3 - src/Cryptography/IncrementalSignature.cs | 41 ++-- src/Cryptography/SignatureAlgorithm.cs | 30 +-- src/Cryptography/SignatureAlgorithm2.cs | 45 ++++ src/Interop/Interop.Sign.Ed25519.cs | 20 +- src/Interop/Interop.yaml | 6 + tests/Base/IncrementalSignatureTests.cs | 143 +++++++---- tests/Examples/Incremental.cs | 69 ++++-- tests/Formatting/NSecTests.cs | 16 ++ tests/Registry.cs | 6 +- 14 files changed, 548 insertions(+), 285 deletions(-) delete mode 100644 src/Cryptography/Ed25519Ph.cs create mode 100644 src/Cryptography/Ed25519ph.cs create mode 100644 src/Cryptography/SignatureAlgorithm2.cs diff --git a/src/Cryptography/Ed25519.cs b/src/Cryptography/Ed25519.cs index e8feb060..795e713a 100644 --- a/src/Cryptography/Ed25519.cs +++ b/src/Cryptography/Ed25519.cs @@ -10,7 +10,8 @@ namespace NSec.Cryptography // // Ed25519 // - // Digital Signature Algorithm (EdDSA) based on the edwards25519 curve + // Digital Signature Algorithm based on the edwards25519 curve in + // pure mode (PureEdDSA) // // References: // @@ -32,7 +33,7 @@ namespace NSec.Cryptography // // Signature Size - 64 bytes. // - public class Ed25519 : SignatureAlgorithm + public sealed class Ed25519 : SignatureAlgorithm { private static readonly PrivateKeyFormatter s_nsecPrivateKeyFormatter = new Ed25519PrivateKeyFormatter(new byte[] { 0xDE, 0x64, 0x42, 0xDE, crypto_sign_ed25519_SEEDBYTES, 0, crypto_sign_ed25519_BYTES, 0 }); @@ -66,8 +67,6 @@ public class Ed25519 : SignatureAlgorithm private static int s_selfTest; - public override bool SupportsPartialUpdated => false; - public Ed25519() : base( privateKeySize: crypto_sign_ed25519_SEEDBYTES, publicKeySize: crypto_sign_ed25519_PUBLICKEYBYTES, @@ -225,26 +224,6 @@ private protected unsafe override bool VerifyCore( } } - internal override void InitializeCore(out IncrementalSignatureState state) - { - throw Error.NotSupported_Algorithm(); - } - - internal override void UpdateCore(ref IncrementalSignatureState state, ReadOnlySpan data) - { - throw Error.NotSupported_Algorithm(); - } - - internal override void FinalSignCore(ref IncrementalSignatureState state, SecureMemoryHandle keyHandle, Span signature) - { - throw Error.NotSupported_Algorithm(); - } - - internal override bool FinalVerifyCore(ref IncrementalSignatureState state, in PublicKeyBytes publicKeyBytes, ReadOnlySpan signature) - { - throw Error.NotSupported_Algorithm(); - } - private static void SelfTest() { if ((crypto_sign_ed25519_bytes() != crypto_sign_ed25519_BYTES) || diff --git a/src/Cryptography/Ed25519Ph.cs b/src/Cryptography/Ed25519Ph.cs deleted file mode 100644 index dba05665..00000000 --- a/src/Cryptography/Ed25519Ph.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Threading; -using NSec.Cryptography.Formatting; -using static Interop.Libsodium; - -namespace NSec.Cryptography -{ - // - // Ed25519ph - // - // Digital Signature Algorithm (EdDSA) pre-hashed based on the edwards25519 curve - // - // References: - // - // RFC 8032 - Edwards-Curve Digital Signature Algorithm (EdDSA) - // - // RFC 5958 - Asymmetric Key Packages - // - // RFC 8410 - Algorithm Identifiers for Ed25519, Ed448, X25519, and - // X448 for Use in the Internet X.509 Public Key Infrastructure - // - // Parameters: - // - // Private Key Size - The private key is 32 bytes (256 bits). However, - // the libsodium representation of a private key is 64 bytes. We - // expose private keys as 32-byte byte strings and internally - // convert from/to the libsodium format as necessary. - // - // Public Key Size - 32 bytes. - // - // Signature Size - 64 bytes. - // - public sealed class Ed25519Ph : Ed25519 - { - public override bool SupportsPartialUpdated => true; - - private protected override void SignCore( - SecureMemoryHandle keyHandle, - ReadOnlySpan data, - Span signature) - { - Debug.Assert(keyHandle.Size == crypto_sign_ed25519_SECRETKEYBYTES); - Debug.Assert(signature.Length == crypto_sign_ed25519_BYTES); - - InitializeCore(out IncrementalSignatureState state); - UpdateCore(ref state, data); - FinalSignCore(ref state, keyHandle, signature); - } - - private protected override bool VerifyCore( - in PublicKeyBytes publicKeyBytes, - ReadOnlySpan data, - ReadOnlySpan signature) - { - if (Unsafe.SizeOf() != crypto_sign_ed25519_PUBLICKEYBYTES) - { - throw Error.InvalidOperation_InternalError(); - } - - Debug.Assert(signature.Length == crypto_sign_ed25519_BYTES); - - InitializeCore(out IncrementalSignatureState state); - UpdateCore(ref state, data); - return FinalVerifyCore(ref state, publicKeyBytes, signature); - } - - internal unsafe override void InitializeCore( - out IncrementalSignatureState state) - { - fixed (crypto_sign_ed25519ph_state* state_ = &state.ed25519PhState) - { - int error = crypto_sign_ed25519ph_init(state_); - - Debug.Assert(error == 0); - } - } - - internal unsafe override void UpdateCore( - ref IncrementalSignatureState state, - ReadOnlySpan data) - { - fixed (crypto_sign_ed25519ph_state* state_ = &state.ed25519PhState) - fixed (byte* @in = data) - { - int error = crypto_sign_ed25519ph_update(state_, @in, (ulong)data.Length); - - Debug.Assert(error == 0); - } - } - - internal unsafe override void FinalSignCore(ref IncrementalSignatureState state, SecureMemoryHandle keyHandle, Span signature) - { - Debug.Assert(keyHandle.Size == crypto_sign_ed25519_SECRETKEYBYTES); - Debug.Assert(signature.Length == crypto_sign_ed25519_BYTES); - - fixed (crypto_sign_ed25519ph_state* state_ = &state.ed25519PhState) - fixed (byte* sig = signature) - { - int error = crypto_sign_ed25519ph_final_create(state_, sig, out ulong signatureLength, keyHandle); - - Debug.Assert(error == 0); - Debug.Assert((ulong)signature.Length == signatureLength); - } - } - - internal unsafe override bool FinalVerifyCore(ref IncrementalSignatureState state, in PublicKeyBytes publicKeyBytes, ReadOnlySpan signature) - { - if (Unsafe.SizeOf() != crypto_sign_ed25519_PUBLICKEYBYTES) - { - throw Error.InvalidOperation_InternalError(); - } - - Debug.Assert(signature.Length == crypto_sign_ed25519_BYTES); - - fixed (crypto_sign_ed25519ph_state* state_ = &state.ed25519PhState) - fixed (byte* sig = signature) - fixed (PublicKeyBytes* pk = &publicKeyBytes) - { - int error = crypto_sign_ed25519ph_final_verify(state_, sig, pk); - - return error == 0; - } - } - } -} diff --git a/src/Cryptography/Ed25519ph.cs b/src/Cryptography/Ed25519ph.cs new file mode 100644 index 00000000..181035f1 --- /dev/null +++ b/src/Cryptography/Ed25519ph.cs @@ -0,0 +1,295 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; +using NSec.Cryptography.Formatting; +using static Interop.Libsodium; + +namespace NSec.Cryptography +{ + // + // Ed25519ph + // + // Digital Signature Algorithm based on the edwards25519 curve in + // pre-hash mode (HashEdDSA) + // + // References: + // + // RFC 8032 - Edwards-Curve Digital Signature Algorithm (EdDSA) + // + // Parameters: + // + // Private Key Size - The private key is 32 bytes (256 bits). However, + // the libsodium representation of a private key is 64 bytes. We + // expose private keys as 32-byte byte strings and internally + // convert from/to the libsodium format as necessary. + // + // Public Key Size - 32 bytes. + // + // Signature Size - 64 bytes. + // + public sealed class Ed25519ph : SignatureAlgorithm2 + { + private static readonly PrivateKeyFormatter s_nsecPrivateKeyFormatter = new Ed25519PrivateKeyFormatter(new byte[] { 0xDE, 0x64, 0x48, 0xDE, crypto_sign_ed25519_SEEDBYTES, 0, crypto_sign_ed25519_BYTES, 0 }); + + private static readonly PublicKeyFormatter s_nsecPublicKeyFormatter = new Ed25519PublicKeyFormatter(new byte[] { 0xDE, 0x65, 0x48, 0xDE, crypto_sign_ed25519_PUBLICKEYBYTES, 0, crypto_sign_ed25519_BYTES, 0 }); + + private static readonly PrivateKeyFormatter s_rawPrivateKeyFormatter = new Ed25519PrivateKeyFormatter(Array.Empty()); + + private static readonly PublicKeyFormatter s_rawPublicKeyFormatter = new Ed25519PublicKeyFormatter(Array.Empty()); + + private static int s_selfTest; + + public Ed25519ph() : base( + privateKeySize: crypto_sign_ed25519_SEEDBYTES, + publicKeySize: crypto_sign_ed25519_PUBLICKEYBYTES, + signatureSize: crypto_sign_ed25519_BYTES) + { + if (s_selfTest == 0) + { + SelfTest(); + Interlocked.Exchange(ref s_selfTest, 1); + } + } + + internal override unsafe void CreateKey( + ReadOnlySpan seed, + out SecureMemoryHandle keyHandle, + out PublicKey? publicKey) + { + if (Unsafe.SizeOf() != crypto_sign_ed25519_PUBLICKEYBYTES) + { + throw Error.InvalidOperation_InternalError(); + } + + Debug.Assert(seed.Length == crypto_sign_ed25519_SEEDBYTES); + + publicKey = new PublicKey(this); + keyHandle = SecureMemoryHandle.Create(crypto_sign_ed25519_SECRETKEYBYTES); + + fixed (PublicKeyBytes* pk = publicKey) + fixed (byte* seed_ = seed) + { + int error = crypto_sign_ed25519_seed_keypair(pk, keyHandle, seed_); + + Debug.Assert(error == 0); + } + } + + internal override int GetSeedSize() + { + return crypto_sign_ed25519_SEEDBYTES; + } + + private protected unsafe override void SignCore( + SecureMemoryHandle keyHandle, + ReadOnlySpan data, + Span signature) + { + Debug.Assert(keyHandle.Size == crypto_sign_ed25519_SECRETKEYBYTES); + Debug.Assert(signature.Length == crypto_sign_ed25519_BYTES); + + fixed (byte* sig = signature) + fixed (byte* m = data) + { + crypto_sign_ed25519ph_state state; + + crypto_sign_ed25519ph_init( + &state); + + crypto_sign_ed25519ph_update( + &state, + m, + (ulong)data.Length); + + int error = crypto_sign_ed25519ph_final_create( + &state, + sig, + out ulong signatureLength, + keyHandle); + + Debug.Assert(error == 0); + Debug.Assert((ulong)signature.Length == signatureLength); + } + } + + private protected unsafe override bool VerifyCore( + in PublicKeyBytes publicKeyBytes, + ReadOnlySpan data, + ReadOnlySpan signature) + { + if (Unsafe.SizeOf() != crypto_sign_ed25519_PUBLICKEYBYTES) + { + throw Error.InvalidOperation_InternalError(); + } + + Debug.Assert(signature.Length == crypto_sign_ed25519_BYTES); + + fixed (byte* sig = signature) + fixed (byte* m = data) + fixed (PublicKeyBytes* pk = &publicKeyBytes) + { + crypto_sign_ed25519ph_state state; + + crypto_sign_ed25519ph_init( + &state); + + crypto_sign_ed25519ph_update( + &state, + m, + (ulong)data.Length); + + int error = crypto_sign_ed25519ph_final_verify( + &state, + sig, + pk); + + return error == 0; + } + } + + internal unsafe override void InitializeCore( + out IncrementalSignatureState state) + { + fixed (crypto_sign_ed25519ph_state* state_ = &state.ed25519ph) + { + int error = crypto_sign_ed25519ph_init(state_); + + Debug.Assert(error == 0); + } + } + + internal unsafe override void UpdateCore( + ref IncrementalSignatureState state, + ReadOnlySpan data) + { + fixed (crypto_sign_ed25519ph_state* state_ = &state.ed25519ph) + fixed (byte* @in = data) + { + int error = crypto_sign_ed25519ph_update( + state_, + @in, + (ulong)data.Length); + + Debug.Assert(error == 0); + } + } + + internal unsafe override void FinalSignCore( + ref IncrementalSignatureState state, + SecureMemoryHandle keyHandle, + Span signature) + { + Debug.Assert(keyHandle.Size == crypto_sign_ed25519_SECRETKEYBYTES); + Debug.Assert(signature.Length == crypto_sign_ed25519_BYTES); + + fixed (crypto_sign_ed25519ph_state* state_ = &state.ed25519ph) + fixed (byte* sig = signature) + { + int error = crypto_sign_ed25519ph_final_create( + state_, + sig, + out ulong signatureLength, + keyHandle); + + Debug.Assert(error == 0); + Debug.Assert((ulong)signature.Length == signatureLength); + } + } + + internal unsafe override bool FinalVerifyCore( + ref IncrementalSignatureState state, + in PublicKeyBytes publicKeyBytes, + ReadOnlySpan signature) + { + if (Unsafe.SizeOf() != crypto_sign_ed25519_PUBLICKEYBYTES) + { + throw Error.InvalidOperation_InternalError(); + } + + Debug.Assert(signature.Length == crypto_sign_ed25519_BYTES); + + fixed (crypto_sign_ed25519ph_state* state_ = &state.ed25519ph) + fixed (byte* sig = signature) + fixed (PublicKeyBytes* pk = &publicKeyBytes) + { + int error = crypto_sign_ed25519ph_final_verify( + state_, + sig, + pk); + + return error == 0; + } + } + + internal override bool TryExportKey( + SecureMemoryHandle keyHandle, + KeyBlobFormat format, + Span blob, + out int blobSize) + { + return format switch + { + KeyBlobFormat.RawPrivateKey => s_rawPrivateKeyFormatter.TryExport(keyHandle, blob, out blobSize), + KeyBlobFormat.NSecPrivateKey => s_nsecPrivateKeyFormatter.TryExport(keyHandle, blob, out blobSize), + _ => throw Error.Argument_FormatNotSupported(nameof(format), format.ToString()), + }; + } + + internal override bool TryExportPublicKey( + PublicKey publicKey, + KeyBlobFormat format, + Span blob, + out int blobSize) + { + return format switch + { + KeyBlobFormat.RawPublicKey => s_rawPublicKeyFormatter.TryExport(in publicKey.GetPinnableReference(), blob, out blobSize), + KeyBlobFormat.NSecPublicKey => s_nsecPublicKeyFormatter.TryExport(in publicKey.GetPinnableReference(), blob, out blobSize), + _ => throw Error.Argument_FormatNotSupported(nameof(format), format.ToString()), + }; + } + + internal override bool TryImportKey( + ReadOnlySpan blob, + KeyBlobFormat format, + out SecureMemoryHandle? keyHandle, + out PublicKey? publicKey) + { + publicKey = new PublicKey(this); + + return format switch + { + KeyBlobFormat.RawPrivateKey => s_rawPrivateKeyFormatter.TryImport(blob, out keyHandle, out publicKey.GetPinnableReference()), + KeyBlobFormat.NSecPrivateKey => s_nsecPrivateKeyFormatter.TryImport(blob, out keyHandle, out publicKey.GetPinnableReference()), + _ => throw Error.Argument_FormatNotSupported(nameof(format), format.ToString()), + }; + } + + internal override bool TryImportPublicKey( + ReadOnlySpan blob, + KeyBlobFormat format, + out PublicKey publicKey) + { + publicKey = new PublicKey(this); + + return format switch + { + KeyBlobFormat.RawPublicKey => s_rawPublicKeyFormatter.TryImport(blob, out publicKey.GetPinnableReference()), + KeyBlobFormat.NSecPublicKey => s_nsecPublicKeyFormatter.TryImport(blob, out publicKey.GetPinnableReference()), + _ => throw Error.Argument_FormatNotSupported(nameof(format), format.ToString()), + }; + } + + private static void SelfTest() + { + if ((crypto_sign_ed25519_bytes() != crypto_sign_ed25519_BYTES) || + (crypto_sign_ed25519_publickeybytes() != crypto_sign_ed25519_PUBLICKEYBYTES) || + (crypto_sign_ed25519_secretkeybytes() != crypto_sign_ed25519_SECRETKEYBYTES) || + (crypto_sign_ed25519_seedbytes() != crypto_sign_ed25519_SEEDBYTES)) + { + throw Error.InvalidOperation_InitializationFailed(); + } + } + } +} diff --git a/src/Cryptography/Error.cs b/src/Cryptography/Error.cs index a39235ac..d988ac92 100644 --- a/src/Cryptography/Error.cs +++ b/src/Cryptography/Error.cs @@ -415,11 +415,6 @@ internal static NotSupportedException NotSupported_KeyConversion( return new NotSupportedException(string.Format(ResourceManager.GetString(nameof(NotSupported_KeyConversion))!, arg0, arg1)); } - internal static NotSupportedException NotSupported_Algorithm() - { - return new NotSupportedException(ResourceManager.GetString(nameof(NotSupported_Algorithm))); - } - internal static NotSupportedException NotSupported_Operation() { return new NotSupportedException(ResourceManager.GetString(nameof(NotSupported_Operation))); diff --git a/src/Cryptography/Error.resx b/src/Cryptography/Error.resx index d582ef66..ad728a24 100644 --- a/src/Cryptography/Error.resx +++ b/src/Cryptography/Error.resx @@ -321,7 +321,4 @@ Could not initialize platform-specific components. NSec may not be supported on this platform. See https://nsec.rocks/docs/install for more information. - - The specified algorithm is not supported for this opeation. - \ No newline at end of file diff --git a/src/Cryptography/IncrementalSignature.cs b/src/Cryptography/IncrementalSignature.cs index c0d6804a..85533777 100644 --- a/src/Cryptography/IncrementalSignature.cs +++ b/src/Cryptography/IncrementalSignature.cs @@ -9,9 +9,9 @@ namespace NSec.Cryptography public struct IncrementalSignature { private readonly IncrementalSignatureState _state; - private readonly SignatureAlgorithm? _algorithm; + private readonly SignatureAlgorithm2? _algorithm; - public SignatureAlgorithm? Algorithm => _algorithm; + public SignatureAlgorithm2? Algorithm => _algorithm; [EditorBrowsable(EditorBrowsableState.Never)] public static new bool Equals( @@ -22,7 +22,7 @@ public struct IncrementalSignature } public static void Initialize( - SignatureAlgorithm algorithm, + SignatureAlgorithm2 algorithm, out IncrementalSignature state) { if (algorithm == null) @@ -30,11 +30,6 @@ public static void Initialize( throw Error.ArgumentNull_Algorithm(nameof(algorithm)); } - if (!algorithm.SupportsPartialUpdated) - { - throw Error.NotSupported_Algorithm(); - } - state = default; algorithm.InitializeCore(out Unsafe.AsRef(in state._state)); Unsafe.AsRef(in state._algorithm) = algorithm; @@ -52,7 +47,7 @@ public static void Update( state._algorithm.UpdateCore(ref Unsafe.AsRef(in state._state), data); } - public static byte[] FinalSignature( + public static byte[] Finalize( ref IncrementalSignature state, Key key) { @@ -60,6 +55,14 @@ public static byte[] FinalSignature( { throw Error.InvalidOperation_UninitializedState(); } + if (key == null) + { + throw Error.ArgumentNull_Key(nameof(key)); + } + if (key.Algorithm != state._algorithm) + { + throw Error.Argument_KeyAlgorithmMismatch(nameof(key), nameof(key)); + } try { @@ -69,11 +72,11 @@ public static byte[] FinalSignature( } finally { - Unsafe.AsRef(in state._algorithm) = null; + Unsafe.AsRef(in state._algorithm) = null; } } - public static void FinalSignature( + public static void Finalize( ref IncrementalSignature state, Key key, Span signature) @@ -82,6 +85,14 @@ public static void FinalSignature( { throw Error.InvalidOperation_UninitializedState(); } + if (key == null) + { + throw Error.ArgumentNull_Key(nameof(key)); + } + if (key.Algorithm != state._algorithm) + { + throw Error.Argument_KeyAlgorithmMismatch(nameof(key), nameof(key)); + } if (signature.Length != state._algorithm.SignatureSize) { throw Error.Argument_SignatureLength(nameof(signature), state._algorithm.SignatureSize); @@ -93,11 +104,11 @@ public static void FinalSignature( } finally { - Unsafe.AsRef(in state._algorithm) = null; + Unsafe.AsRef(in state._algorithm) = null; } } - public static bool FinalVerify( + public static bool FinalizeAndVerify( ref IncrementalSignature state, PublicKey publicKey, ReadOnlySpan signature) @@ -122,7 +133,7 @@ public static bool FinalVerify( } finally { - Unsafe.AsRef(in state._algorithm) = null; + Unsafe.AsRef(in state._algorithm) = null; } } } @@ -131,6 +142,6 @@ public static bool FinalVerify( internal struct IncrementalSignatureState { [FieldOffset(0)] - internal Interop.Libsodium.crypto_sign_ed25519ph_state ed25519PhState; + internal Interop.Libsodium.crypto_sign_ed25519ph_state ed25519ph; } } diff --git a/src/Cryptography/SignatureAlgorithm.cs b/src/Cryptography/SignatureAlgorithm.cs index d89ad512..967a05b8 100644 --- a/src/Cryptography/SignatureAlgorithm.cs +++ b/src/Cryptography/SignatureAlgorithm.cs @@ -20,7 +20,7 @@ namespace NSec.Cryptography public abstract class SignatureAlgorithm : Algorithm { private static Ed25519? s_Ed25519; - private static Ed25519Ph? s_Ed25519ph; + private static Ed25519ph? s_Ed25519ph; private readonly int _privateKeySize; private readonly int _publicKeySize; @@ -54,25 +54,20 @@ public static Ed25519 Ed25519 } } - public static Ed25519Ph Ed25519ph + public static Ed25519ph Ed25519ph { get { - Ed25519Ph? instance = s_Ed25519ph; + Ed25519ph? instance = s_Ed25519ph; if (instance == null) { - Interlocked.CompareExchange(ref s_Ed25519ph, new Ed25519Ph(), null); + Interlocked.CompareExchange(ref s_Ed25519ph, new Ed25519ph(), null); instance = s_Ed25519ph; } return instance; } } - public abstract bool SupportsPartialUpdated - { - get; - } - public int PrivateKeySize => _privateKeySize; public int PublicKeySize => _publicKeySize; @@ -156,22 +151,5 @@ private protected abstract bool VerifyCore( in PublicKeyBytes publicKeyBytes, ReadOnlySpan data, ReadOnlySpan signature); - - internal abstract void InitializeCore( - out IncrementalSignatureState state); - - internal abstract void UpdateCore( - ref IncrementalSignatureState state, - ReadOnlySpan data); - - internal abstract void FinalSignCore( - ref IncrementalSignatureState state, - SecureMemoryHandle keyHandle, - Span signature); - - internal abstract bool FinalVerifyCore( - ref IncrementalSignatureState state, - in PublicKeyBytes publicKeyBytes, - ReadOnlySpan signature); } } diff --git a/src/Cryptography/SignatureAlgorithm2.cs b/src/Cryptography/SignatureAlgorithm2.cs new file mode 100644 index 00000000..851aa2ce --- /dev/null +++ b/src/Cryptography/SignatureAlgorithm2.cs @@ -0,0 +1,45 @@ +using System; +using System.Diagnostics; +using System.Threading; +using static Interop.Libsodium; + +namespace NSec.Cryptography +{ + // + // A digital signature algorithm supporting the "init, update, final" interface + // + // Candidates + // + // | Algorithm | Reference | Key Size | Signature Size | + // | ----------- | ---------- | -------- | -------------- | + // | Ed25519ph | RFC 8032 | 32 | 64 | + // | Ed448ph | RFC 8032 | 57 | 114 | + // + public abstract class SignatureAlgorithm2 : SignatureAlgorithm + { + private protected SignatureAlgorithm2( + int privateKeySize, + int publicKeySize, + int signatureSize) + : base(privateKeySize, publicKeySize, signatureSize) + { + } + + internal abstract void InitializeCore( + out IncrementalSignatureState state); + + internal abstract void UpdateCore( + ref IncrementalSignatureState state, + ReadOnlySpan data); + + internal abstract void FinalSignCore( + ref IncrementalSignatureState state, + SecureMemoryHandle keyHandle, + Span signature); + + internal abstract bool FinalVerifyCore( + ref IncrementalSignatureState state, + in PublicKeyBytes publicKeyBytes, + ReadOnlySpan signature); + } +} diff --git a/src/Interop/Interop.Sign.Ed25519.cs b/src/Interop/Interop.Sign.Ed25519.cs index 1b4dc217..bd527a01 100644 --- a/src/Interop/Interop.Sign.Ed25519.cs +++ b/src/Interop/Interop.Sign.Ed25519.cs @@ -58,16 +58,6 @@ internal static unsafe extern int crypto_sign_ed25519_verify_detached( ulong mlen, PublicKeyBytes* pk); - [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] - internal static unsafe extern int crypto_sign_ed25519ph_init( - crypto_sign_ed25519ph_state* state); - - [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] - internal static unsafe extern int crypto_sign_ed25519ph_update( - crypto_sign_ed25519ph_state* state, - byte* m, - ulong mlen); - [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] internal static unsafe extern int crypto_sign_ed25519ph_final_create( crypto_sign_ed25519ph_state* state, @@ -81,6 +71,16 @@ internal static unsafe extern int crypto_sign_ed25519ph_final_verify( byte* sig, PublicKeyBytes* pk); + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static unsafe extern int crypto_sign_ed25519ph_init( + crypto_sign_ed25519ph_state* state); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static unsafe extern int crypto_sign_ed25519ph_update( + crypto_sign_ed25519ph_state* state, + byte* m, + ulong mlen); + [StructLayout(LayoutKind.Explicit, Size = 208)] internal struct crypto_sign_ed25519ph_state { diff --git a/src/Interop/Interop.yaml b/src/Interop/Interop.yaml index 5d04e90c..73fdf09e 100644 --- a/src/Interop/Interop.yaml +++ b/src/Interop/Interop.yaml @@ -293,6 +293,12 @@ Interop.Sign.Ed25519.cs: - crypto_sign_ed25519_seed_keypair (PublicKeyBytes* pk, SecureMemoryHandle sk) - crypto_sign_ed25519_seedbytes - crypto_sign_ed25519_verify_detached (PublicKeyBytes* pk) + - crypto_sign_ed25519ph_init + - crypto_sign_ed25519ph_update + - crypto_sign_ed25519ph_final_create (out ulong siglen_p, SecureMemoryHandle sk) + - crypto_sign_ed25519ph_final_verify (PublicKeyBytes* pk) + structs: + - crypto_sign_ed25519ph_state #Interop.Sign.Edwards25519Sha512Batch.cs: # include: include/sodium/crypto_sign_edwards25519sha512batch.h diff --git a/tests/Base/IncrementalSignatureTests.cs b/tests/Base/IncrementalSignatureTests.cs index 77eb84ee..9219bd40 100644 --- a/tests/Base/IncrementalSignatureTests.cs +++ b/tests/Base/IncrementalSignatureTests.cs @@ -6,7 +6,7 @@ namespace NSec.Tests.Base { public static class IncrementalSignatureTests { - public static readonly TheoryData IncrementalSignatureAlgorithms = Registry.IncrementalSignatureAlgorithms; + public static readonly TheoryData IncrementalSignatureAlgorithms = Registry.IncrementalSignatureAlgorithms; #region Initialize @@ -16,31 +16,45 @@ public static void InitializeWithNullAlgorithm() Assert.Throws("algorithm", () => IncrementalSignature.Initialize(null!, out _)); } - [Fact] - public static void InitializeWithUnsupportedAlgorithm() + #endregion + + #region Finalize #1 + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalizeInvalid(SignatureAlgorithm2 a) { - Assert.Throws(() => IncrementalSignature.Initialize(SignatureAlgorithm.Ed25519, out _)); + using var k = new Key(a); + var state = default(IncrementalSignature); + + Assert.Throws(() => IncrementalSignature.Finalize(ref state, k)); } - #endregion + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalizeWithNullKey(SignatureAlgorithm2 a) + { + IncrementalSignature.Initialize(a, out var state); - #region FinalSignature #1 + Assert.Throws("key", () => IncrementalSignature.Finalize(ref state, null!)); + } [Theory] [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalSignatureInvalid(SignatureAlgorithm a) + public static void FinalizeWithWrongKey(SignatureAlgorithm2 a) { - var state = default(IncrementalSignature); + using var k = new Key(SignatureAlgorithm.Ed25519); - var key = new Key(a); + IncrementalSignature.Initialize(a, out var state); - Assert.Throws(() => IncrementalSignature.FinalSignature(ref state, key)); + Assert.Throws("key", () => IncrementalSignature.Finalize(ref state, k)); } [Theory] [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalSignatureSuccess(SignatureAlgorithm a) + public static void FinalizeSuccess(SignatureAlgorithm2 a) { + using var k = new Key(a); var state = default(IncrementalSignature); Assert.Null(state.Algorithm); @@ -53,57 +67,76 @@ public static void FinalSignatureSuccess(SignatureAlgorithm a) IncrementalSignature.Update(ref state, Utilities.RandomBytes.Slice(100, 100)); IncrementalSignature.Update(ref state, Utilities.RandomBytes.Slice(200, 100)); - var key = new Key(a); - var actual = IncrementalSignature.FinalSignature(ref state, key); + var actual = IncrementalSignature.Finalize(ref state, k); Assert.Null(state.Algorithm); - var expected = a.Sign(key, Utilities.RandomBytes.Slice(0, 300)); + var expected = a.Sign(k, Utilities.RandomBytes.Slice(0, 300)); Assert.Equal(expected, actual); } #endregion - #region FinalSignature #2 + #region Finalize #2 [Theory] [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalSignatureWithSpanInvalid(SignatureAlgorithm a) + public static void FinalizeWithSpanInvalid(SignatureAlgorithm2 a) { + using var k = new Key(a); var state = default(IncrementalSignature); - var key = new Key(a); + Assert.Throws(() => IncrementalSignature.Finalize(ref state, k, new byte[a.SignatureSize])); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalizeWithSpanAndNullKey(SignatureAlgorithm2 a) + { + IncrementalSignature.Initialize(a, out var state); - Assert.Throws(() => IncrementalSignature.FinalSignature(ref state, key, Span.Empty)); + Assert.Throws("key", () => IncrementalSignature.Finalize(ref state, null!, new byte[a.SignatureSize])); } [Theory] [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalSignatureWithSpanTooSmall(SignatureAlgorithm a) + public static void FinalizeWithSpanAndWrongKey(SignatureAlgorithm2 a) { - var key = new Key(a); + using var k = new Key(SignatureAlgorithm.Ed25519); IncrementalSignature.Initialize(a, out var state); - Assert.Throws("signature", () => IncrementalSignature.FinalSignature(ref state, key, new byte[a.SignatureSize - 1])); + Assert.Throws("key", () => IncrementalSignature.Finalize(ref state, k, new byte[a.SignatureSize])); } [Theory] [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalizeWithSpanTooLarge(SignatureAlgorithm a) + public static void FinalizeWithSpanTooSmall(SignatureAlgorithm2 a) { - var key = new Key(a); + using var k = new Key(a); IncrementalSignature.Initialize(a, out var state); - Assert.Throws("signature", () => IncrementalSignature.FinalSignature(ref state, key, new byte[a.SignatureSize + 1])); + Assert.Throws("signature", () => IncrementalSignature.Finalize(ref state, k, new byte[a.SignatureSize - 1])); } [Theory] [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalSignatureWithSpanSuccess(SignatureAlgorithm a) + public static void FinalizeWithSpanTooLarge(SignatureAlgorithm2 a) { + using var k = new Key(a); + + IncrementalSignature.Initialize(a, out var state); + + Assert.Throws("signature", () => IncrementalSignature.Finalize(ref state, k, new byte[a.SignatureSize + 1])); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalizeWithSpanSuccess(SignatureAlgorithm2 a) + { + using var k = new Key(a); var state = default(IncrementalSignature); Assert.Null(state.Algorithm); @@ -116,22 +149,22 @@ public static void FinalSignatureWithSpanSuccess(SignatureAlgorithm a) IncrementalSignature.Update(ref state, Utilities.RandomBytes.Slice(100, 100)); IncrementalSignature.Update(ref state, Utilities.RandomBytes.Slice(200, 100)); - var key = new Key(a); - var actual = new byte[a.SignatureSize]; - IncrementalSignature.FinalSignature(ref state, key, actual); + + IncrementalSignature.Finalize(ref state, k, actual); Assert.Null(state.Algorithm); - var expected = a.Sign(key, Utilities.RandomBytes.Slice(0, 300)); + var expected = a.Sign(k, Utilities.RandomBytes.Slice(0, 300)); Assert.Equal(expected, actual); } [Theory] [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalSignatureWithSpanSuccessNoUpdate(SignatureAlgorithm a) + public static void FinalizeWithSpanSuccessNoUpdate(SignatureAlgorithm2 a) { + using var k = new Key(a); var state = default(IncrementalSignature); Assert.Null(state.Algorithm); @@ -140,63 +173,73 @@ public static void FinalSignatureWithSpanSuccessNoUpdate(SignatureAlgorithm a) Assert.Same(a, state.Algorithm); - var key = new Key(a); - var actual = new byte[a.SignatureSize]; - IncrementalSignature.FinalSignature(ref state, key, actual); + + IncrementalSignature.Finalize(ref state, k, actual); Assert.Null(state.Algorithm); - var expected = a.Sign(key, ReadOnlySpan.Empty); + var expected = a.Sign(k, ReadOnlySpan.Empty); Assert.Equal(expected, actual); } #endregion - #region FinalVerify + #region FinalizeAndVerify [Theory] [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalVerifyInvalid(SignatureAlgorithm a) + public static void FinalizeAndVerifyInvalid(SignatureAlgorithm2 a) { + using var k = new Key(a); var state = default(IncrementalSignature); - var key = new Key(a); - - Assert.Throws(() => IncrementalSignature.FinalVerify(ref state, key.PublicKey, Span.Empty)); + Assert.Throws(() => IncrementalSignature.FinalizeAndVerify(ref state, k.PublicKey, new byte[a.SignatureSize])); } [Theory] [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalVerifyNullKey(SignatureAlgorithm a) + public static void FinalizeAndVerifyWithNullKey(SignatureAlgorithm2 a) { - var state = default(IncrementalSignature); + IncrementalSignature.Initialize(a, out var state); - Assert.Throws(() => IncrementalSignature.FinalVerify(ref state, null!, Span.Empty)); + Assert.Throws("publicKey", () => IncrementalSignature.FinalizeAndVerify(ref state, null!, new byte[a.SignatureSize])); } [Theory] [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalVerifyKeyFromdifferentAlgorithm(SignatureAlgorithm a) + public static void FinalizeAndVerifyWithWrongKey(SignatureAlgorithm2 a) { - var state = default(IncrementalSignature); + using var k = new Key(SignatureAlgorithm.Ed25519); - var key = new Key(SignatureAlgorithm.Ed25519); + IncrementalSignature.Initialize(a, out var state); - Assert.Throws(() => IncrementalSignature.FinalVerify(ref state, key.PublicKey, Span.Empty)); + Assert.Throws("publicKey", () => IncrementalSignature.FinalizeAndVerify(ref state, k.PublicKey, new byte[a.SignatureSize])); } [Theory] [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalVerifySuccess(SignatureAlgorithm a) + public static void FinalizeAndVerifyFail(SignatureAlgorithm2 a) { + using var k = new Key(a); + IncrementalSignature.Initialize(a, out var state); - var key = new Key(a); - var signature = a.Sign(key, ReadOnlySpan.Empty); + Assert.False(IncrementalSignature.FinalizeAndVerify(ref state, k.PublicKey, new byte[a.SignatureSize])); + + Assert.Null(state.Algorithm); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalizeAndVerifySuccess(SignatureAlgorithm2 a) + { + using var k = new Key(a); + + IncrementalSignature.Initialize(a, out var state); - Assert.True(IncrementalSignature.FinalVerify(ref state, key.PublicKey, signature)); + Assert.True(IncrementalSignature.FinalizeAndVerify(ref state, k.PublicKey, a.Sign(k, ReadOnlySpan.Empty))); Assert.Null(state.Algorithm); } diff --git a/tests/Examples/Incremental.cs b/tests/Examples/Incremental.cs index 90e12691..dc6f786a 100644 --- a/tests/Examples/Incremental.cs +++ b/tests/Examples/Incremental.cs @@ -86,10 +86,10 @@ public static void Signature() // select the Ed25519ph algorithm var algorithm = SignatureAlgorithm.Ed25519ph; - // create a new key + // create a new key pair using var key = Key.Create(algorithm); - // initialize the state with the key + // initialize the state IncrementalSignature.Initialize(algorithm, out var state); // incrementally update the state with some data @@ -108,8 +108,8 @@ public static void Signature() IncrementalSignature.Update(ref state, Encoding.UTF8.GetBytes(line)); } - // finalize the computation and get the result - var signature = IncrementalSignature.FinalSignature(ref state, key); + // finalize the computation using the private key and get the result + var signature = IncrementalSignature.Finalize(ref state, key); #endregion @@ -124,36 +124,57 @@ public static void Verify() // select the Ed25519ph algorithm var algorithm = SignatureAlgorithm.Ed25519ph; - // create a new key + // create a new key pair using var key = Key.Create(algorithm); - // initialize the state with the key - IncrementalSignature.Initialize(algorithm, out var state); - - // incrementally update the state with some data + // create some data to be signed var lines = new[] { - "It is a dark time for the\n", - "Rebellion. Although the Death\n", - "Star has been destroyed,\n", - "Imperial troops have driven the\n", - "Rebel forces from their hidden\n", - "base and pursued them across\n", - "the galaxy.\n" + "Luke Skywalker has returned to\n", + "his home planet of Tatooine in\n", + "an attempt to rescue his\n", + "friend Han Solo from the\n", + "clutches of the vile gangster\n", + "Jabba the Hutt.\n", }; - foreach (var line in lines) + + byte[] signature; + + // sign { - IncrementalSignature.Update(ref state, Encoding.UTF8.GetBytes(line)); + // initialize the state + IncrementalSignature.Initialize(algorithm, out var state); + + // incrementally update the state with the data + foreach (var line in lines) + { + IncrementalSignature.Update(ref state, Encoding.UTF8.GetBytes(line)); + } + + // finalize the computation using the private key and get the result + signature = IncrementalSignature.Finalize(ref state, key); } - // calculate the signature - var signature = algorithm.Sign(key, Encoding.UTF8.GetBytes(string.Concat(lines))); + // verify + { + // initialize the state + IncrementalSignature.Initialize(algorithm, out var state); + + // incrementally update the state with the data + foreach (var line in lines) + { + IncrementalSignature.Update(ref state, Encoding.UTF8.GetBytes(line)); + } + + // verify the data using the signature and the public key + if (IncrementalSignature.FinalizeAndVerify(ref state, key.PublicKey, signature)) + { + // verified! + /*{*//*}*/ + } + } - //verify the signature - var isValid = IncrementalSignature.FinalVerify(ref state, key.PublicKey, signature); #endregion - - Assert.True(isValid); } } } diff --git a/tests/Formatting/NSecTests.cs b/tests/Formatting/NSecTests.cs index 12ae4be0..93b0c5bb 100644 --- a/tests/Formatting/NSecTests.cs +++ b/tests/Formatting/NSecTests.cs @@ -43,6 +43,22 @@ public static void Ed25519Public() Test(a, a.PrivateKeySize, KeyBlobFormat.RawPrivateKey, a.PublicKeySize, a.SignatureSize, KeyBlobFormat.NSecPublicKey, new byte[] { 0xDE, 0x65, 0x42, 0xDE }); } + [Fact] + public static void Ed25519phPrivate() + { + var a = SignatureAlgorithm.Ed25519ph; + + Test(a, a.PrivateKeySize, KeyBlobFormat.RawPrivateKey, a.PrivateKeySize, a.SignatureSize, KeyBlobFormat.NSecPrivateKey, new byte[] { 0xDE, 0x64, 0x48, 0xDE }); + } + + [Fact] + public static void Ed25519phPublic() + { + var a = SignatureAlgorithm.Ed25519ph; + + Test(a, a.PrivateKeySize, KeyBlobFormat.RawPrivateKey, a.PublicKeySize, a.SignatureSize, KeyBlobFormat.NSecPublicKey, new byte[] { 0xDE, 0x65, 0x48, 0xDE }); + } + [Fact] public static void X25519Private() { diff --git a/tests/Registry.cs b/tests/Registry.cs index f4a7c43f..f8ccd24a 100644 --- a/tests/Registry.cs +++ b/tests/Registry.cs @@ -83,7 +83,7 @@ internal static class Registry SignatureAlgorithm.Ed25519ph, }; - public static readonly TheoryData IncrementalSignatureAlgorithms = new() + public static readonly TheoryData IncrementalSignatureAlgorithms = new() { SignatureAlgorithm.Ed25519ph, }; @@ -147,6 +147,8 @@ internal static class Registry { SignatureAlgorithm.Ed25519, KeyBlobFormat.NSecPublicKey }, { SignatureAlgorithm.Ed25519, KeyBlobFormat.PkixPublicKey }, { SignatureAlgorithm.Ed25519, KeyBlobFormat.PkixPublicKeyText }, + { SignatureAlgorithm.Ed25519ph, KeyBlobFormat.RawPublicKey }, + { SignatureAlgorithm.Ed25519ph, KeyBlobFormat.NSecPublicKey }, }; public static readonly TheoryData PrivateKeyBlobFormats = new() @@ -159,6 +161,8 @@ internal static class Registry { SignatureAlgorithm.Ed25519, KeyBlobFormat.NSecPrivateKey }, { SignatureAlgorithm.Ed25519, KeyBlobFormat.PkixPrivateKey }, { SignatureAlgorithm.Ed25519, KeyBlobFormat.PkixPrivateKeyText }, + { SignatureAlgorithm.Ed25519ph, KeyBlobFormat.RawPrivateKey }, + { SignatureAlgorithm.Ed25519ph, KeyBlobFormat.NSecPrivateKey }, }; public static readonly TheoryData SymmetricKeyBlobFormats = new() From 625f429755139ac219e7ef524638394ba44774fc Mon Sep 17 00:00:00 2001 From: joaomatossilva Date: Mon, 12 Jun 2023 21:29:05 +0100 Subject: [PATCH 03/26] Split IncrementalSignature in two --- src/Cryptography/IncrementalSignature.cs | 78 ++++------ src/Cryptography/IncrementalSignatureState.cs | 11 ++ .../IncrementalSignatureVerification.cs | 89 ++++++++++++ ...Tests.cs => IncrementalSignatureTests2.cs} | 136 ++++-------------- .../IncrementalSignatureVerificationTests.cs | 79 ++++++++++ tests/Examples/Incremental.cs | 14 +- 6 files changed, 237 insertions(+), 170 deletions(-) create mode 100644 src/Cryptography/IncrementalSignatureState.cs create mode 100644 src/Cryptography/IncrementalSignatureVerification.cs rename tests/Base/{IncrementalSignatureTests.cs => IncrementalSignatureTests2.cs} (50%) create mode 100644 tests/Base/IncrementalSignatureVerificationTests.cs diff --git a/src/Cryptography/IncrementalSignature.cs b/src/Cryptography/IncrementalSignature.cs index 85533777..a2614f7b 100644 --- a/src/Cryptography/IncrementalSignature.cs +++ b/src/Cryptography/IncrementalSignature.cs @@ -6,11 +6,13 @@ namespace NSec.Cryptography { [StructLayout(LayoutKind.Sequential)] - public struct IncrementalSignature + public readonly struct IncrementalSignature { private readonly IncrementalSignatureState _state; private readonly SignatureAlgorithm2? _algorithm; + private readonly Key? _privateKey; + public SignatureAlgorithm2? Algorithm => _algorithm; [EditorBrowsable(EditorBrowsableState.Never)] @@ -23,6 +25,7 @@ public struct IncrementalSignature public static void Initialize( SignatureAlgorithm2 algorithm, + Key privateKey, out IncrementalSignature state) { if (algorithm == null) @@ -30,9 +33,19 @@ public static void Initialize( throw Error.ArgumentNull_Algorithm(nameof(algorithm)); } + if (privateKey == null) + { + throw Error.ArgumentNull_Key(nameof(privateKey)); + } + if (privateKey.Algorithm != algorithm) + { + throw Error.Argument_KeyAlgorithmMismatch(nameof(privateKey), nameof(algorithm)); + } + state = default; algorithm.InitializeCore(out Unsafe.AsRef(in state._state)); Unsafe.AsRef(in state._algorithm) = algorithm; + Unsafe.AsRef(in state._privateKey) = privateKey; } public static void Update( @@ -48,88 +61,52 @@ public static void Update( } public static byte[] Finalize( - ref IncrementalSignature state, - Key key) + ref IncrementalSignature state) { if (state._algorithm == null) { throw Error.InvalidOperation_UninitializedState(); } - if (key == null) + if (state._privateKey == null) { - throw Error.ArgumentNull_Key(nameof(key)); - } - if (key.Algorithm != state._algorithm) - { - throw Error.Argument_KeyAlgorithmMismatch(nameof(key), nameof(key)); + throw Error.InvalidOperation_UninitializedState(); } try { byte[] signature = new byte[state._algorithm.SignatureSize]; - state._algorithm.FinalSignCore(ref Unsafe.AsRef(in state._state), key.Handle, signature); + state._algorithm.FinalSignCore(ref Unsafe.AsRef(in state._state), state._privateKey.Handle, signature); return signature; } finally { Unsafe.AsRef(in state._algorithm) = null; + Unsafe.AsRef(in state._privateKey) = null; } } public static void Finalize( ref IncrementalSignature state, - Key key, Span signature) { if (state._algorithm == null) { throw Error.InvalidOperation_UninitializedState(); } - if (key == null) - { - throw Error.ArgumentNull_Key(nameof(key)); - } - if (key.Algorithm != state._algorithm) - { - throw Error.Argument_KeyAlgorithmMismatch(nameof(key), nameof(key)); - } - if (signature.Length != state._algorithm.SignatureSize) - { - throw Error.Argument_SignatureLength(nameof(signature), state._algorithm.SignatureSize); - } - - try - { - state._algorithm.FinalSignCore(ref Unsafe.AsRef(in state._state), key.Handle, signature); - } - finally - { - Unsafe.AsRef(in state._algorithm) = null; - } - } - public static bool FinalizeAndVerify( - ref IncrementalSignature state, - PublicKey publicKey, - ReadOnlySpan signature) - { - if (state._algorithm == null) + if (state._privateKey == null) { throw Error.InvalidOperation_UninitializedState(); } - if (publicKey == null) - { - throw Error.ArgumentNull_Key(nameof(publicKey)); - } - if (publicKey.Algorithm != state._algorithm) + + if (signature.Length != state._algorithm.SignatureSize) { - throw Error.Argument_PublicKeyAlgorithmMismatch(nameof(publicKey), nameof(publicKey)); + throw Error.Argument_SignatureLength(nameof(signature), state._algorithm.SignatureSize); } try { - return signature.Length == state._algorithm.SignatureSize && - state._algorithm.FinalVerifyCore(ref Unsafe.AsRef(in state._state), publicKey.GetPinnableReference(), signature); + state._algorithm.FinalSignCore(ref Unsafe.AsRef(in state._state), state._privateKey.Handle, signature); } finally { @@ -137,11 +114,4 @@ public static bool FinalizeAndVerify( } } } - - [StructLayout(LayoutKind.Explicit)] - internal struct IncrementalSignatureState - { - [FieldOffset(0)] - internal Interop.Libsodium.crypto_sign_ed25519ph_state ed25519ph; - } } diff --git a/src/Cryptography/IncrementalSignatureState.cs b/src/Cryptography/IncrementalSignatureState.cs new file mode 100644 index 00000000..b8eaf5d8 --- /dev/null +++ b/src/Cryptography/IncrementalSignatureState.cs @@ -0,0 +1,11 @@ +namespace NSec.Cryptography +{ + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Explicit)] + internal struct IncrementalSignatureState + { + [FieldOffset(0)] + internal Interop.Libsodium.crypto_sign_ed25519ph_state ed25519ph; + } +} diff --git a/src/Cryptography/IncrementalSignatureVerification.cs b/src/Cryptography/IncrementalSignatureVerification.cs new file mode 100644 index 00000000..d1378434 --- /dev/null +++ b/src/Cryptography/IncrementalSignatureVerification.cs @@ -0,0 +1,89 @@ +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace NSec.Cryptography +{ + [StructLayout(LayoutKind.Sequential)] + public readonly struct IncrementalSignatureVerification + { + private readonly IncrementalSignatureState _state; + private readonly SignatureAlgorithm2? _algorithm; + + private readonly PublicKey? _publicKey; + + public SignatureAlgorithm2? Algorithm => _algorithm; + + [EditorBrowsable(EditorBrowsableState.Never)] + public static new bool Equals( + object? objA, + object? objB) + { + return object.Equals(objA, objB); + } + + public static void Initialize( + SignatureAlgorithm2 algorithm, + PublicKey publicKey, + out IncrementalSignatureVerification state) + { + if (algorithm == null) + { + throw Error.ArgumentNull_Algorithm(nameof(algorithm)); + } + + if (publicKey == null) + { + throw Error.ArgumentNull_Key(nameof(publicKey)); + } + if (publicKey.Algorithm != algorithm) + { + throw Error.Argument_PublicKeyAlgorithmMismatch(nameof(publicKey), nameof(publicKey)); + } + + state = default; + algorithm.InitializeCore(out Unsafe.AsRef(in state._state)); + Unsafe.AsRef(in state._algorithm) = algorithm; + Unsafe.AsRef(in state._publicKey) = publicKey; + } + + public static void Update( + ref IncrementalSignatureVerification state, + ReadOnlySpan data) + { + if (state._algorithm == null) + { + throw Error.InvalidOperation_UninitializedState(); + } + + state._algorithm.UpdateCore(ref Unsafe.AsRef(in state._state), data); + } + + public static bool FinalizeAndVerify( + ref IncrementalSignatureVerification state, + ReadOnlySpan signature) + { + if (state._algorithm == null) + { + throw Error.InvalidOperation_UninitializedState(); + } + + if (state._publicKey == null) + { + throw Error.InvalidOperation_UninitializedState(); + } + + try + { + return signature.Length == state._algorithm.SignatureSize && + state._algorithm.FinalVerifyCore(ref Unsafe.AsRef(in state._state), state._publicKey.GetPinnableReference(), signature); + } + finally + { + Unsafe.AsRef(in state._algorithm) = null; + Unsafe.AsRef(in state._publicKey) = null; + } + } + } +} diff --git a/tests/Base/IncrementalSignatureTests.cs b/tests/Base/IncrementalSignatureTests2.cs similarity index 50% rename from tests/Base/IncrementalSignatureTests.cs rename to tests/Base/IncrementalSignatureTests2.cs index 9219bd40..342afd87 100644 --- a/tests/Base/IncrementalSignatureTests.cs +++ b/tests/Base/IncrementalSignatureTests2.cs @@ -13,41 +13,39 @@ public static class IncrementalSignatureTests [Fact] public static void InitializeWithNullAlgorithm() { - Assert.Throws("algorithm", () => IncrementalSignature.Initialize(null!, out _)); - } - - #endregion + using var k = new Key(SignatureAlgorithm.Ed25519ph); - #region Finalize #1 + Assert.Throws("algorithm", () => IncrementalSignature.Initialize(null!, k, out _)); + } [Theory] [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalizeInvalid(SignatureAlgorithm2 a) + public static void InitializeWithNullKey(SignatureAlgorithm2 a) { - using var k = new Key(a); - var state = default(IncrementalSignature); - - Assert.Throws(() => IncrementalSignature.Finalize(ref state, k)); + Assert.Throws("privateKey", () => IncrementalSignature.Initialize(a, null!, out _)); } [Theory] [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalizeWithNullKey(SignatureAlgorithm2 a) + public static void InitializeWithWrongKey(SignatureAlgorithm2 a) { - IncrementalSignature.Initialize(a, out var state); + using var k = new Key(SignatureAlgorithm.Ed25519); - Assert.Throws("key", () => IncrementalSignature.Finalize(ref state, null!)); + Assert.Throws("privateKey", () => IncrementalSignature.Initialize(a, k, out var state)); } + #endregion + + #region Finalize #1 + [Theory] [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalizeWithWrongKey(SignatureAlgorithm2 a) + public static void FinalizeInvalid(SignatureAlgorithm2 a) { - using var k = new Key(SignatureAlgorithm.Ed25519); - - IncrementalSignature.Initialize(a, out var state); + using var k = new Key(a); + var state = default(IncrementalSignature); - Assert.Throws("key", () => IncrementalSignature.Finalize(ref state, k)); + Assert.Throws(() => IncrementalSignature.Finalize(ref state)); } [Theory] @@ -59,7 +57,7 @@ public static void FinalizeSuccess(SignatureAlgorithm2 a) Assert.Null(state.Algorithm); - IncrementalSignature.Initialize(a, out state); + IncrementalSignature.Initialize(a, k, out state); Assert.Same(a, state.Algorithm); @@ -67,7 +65,7 @@ public static void FinalizeSuccess(SignatureAlgorithm2 a) IncrementalSignature.Update(ref state, Utilities.RandomBytes.Slice(100, 100)); IncrementalSignature.Update(ref state, Utilities.RandomBytes.Slice(200, 100)); - var actual = IncrementalSignature.Finalize(ref state, k); + var actual = IncrementalSignature.Finalize(ref state); Assert.Null(state.Algorithm); @@ -87,27 +85,7 @@ public static void FinalizeWithSpanInvalid(SignatureAlgorithm2 a) using var k = new Key(a); var state = default(IncrementalSignature); - Assert.Throws(() => IncrementalSignature.Finalize(ref state, k, new byte[a.SignatureSize])); - } - - [Theory] - [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalizeWithSpanAndNullKey(SignatureAlgorithm2 a) - { - IncrementalSignature.Initialize(a, out var state); - - Assert.Throws("key", () => IncrementalSignature.Finalize(ref state, null!, new byte[a.SignatureSize])); - } - - [Theory] - [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalizeWithSpanAndWrongKey(SignatureAlgorithm2 a) - { - using var k = new Key(SignatureAlgorithm.Ed25519); - - IncrementalSignature.Initialize(a, out var state); - - Assert.Throws("key", () => IncrementalSignature.Finalize(ref state, k, new byte[a.SignatureSize])); + Assert.Throws(() => IncrementalSignature.Finalize(ref state, new byte[a.SignatureSize])); } [Theory] @@ -116,9 +94,9 @@ public static void FinalizeWithSpanTooSmall(SignatureAlgorithm2 a) { using var k = new Key(a); - IncrementalSignature.Initialize(a, out var state); + IncrementalSignature.Initialize(a, k, out var state); - Assert.Throws("signature", () => IncrementalSignature.Finalize(ref state, k, new byte[a.SignatureSize - 1])); + Assert.Throws("signature", () => IncrementalSignature.Finalize(ref state, new byte[a.SignatureSize - 1])); } [Theory] @@ -127,9 +105,9 @@ public static void FinalizeWithSpanTooLarge(SignatureAlgorithm2 a) { using var k = new Key(a); - IncrementalSignature.Initialize(a, out var state); + IncrementalSignature.Initialize(a, k, out var state); - Assert.Throws("signature", () => IncrementalSignature.Finalize(ref state, k, new byte[a.SignatureSize + 1])); + Assert.Throws("signature", () => IncrementalSignature.Finalize(ref state, new byte[a.SignatureSize + 1])); } [Theory] @@ -141,7 +119,7 @@ public static void FinalizeWithSpanSuccess(SignatureAlgorithm2 a) Assert.Null(state.Algorithm); - IncrementalSignature.Initialize(a, out state); + IncrementalSignature.Initialize(a, k, out state); Assert.Same(a, state.Algorithm); @@ -151,7 +129,7 @@ public static void FinalizeWithSpanSuccess(SignatureAlgorithm2 a) var actual = new byte[a.SignatureSize]; - IncrementalSignature.Finalize(ref state, k, actual); + IncrementalSignature.Finalize(ref state, actual); Assert.Null(state.Algorithm); @@ -169,13 +147,13 @@ public static void FinalizeWithSpanSuccessNoUpdate(SignatureAlgorithm2 a) Assert.Null(state.Algorithm); - IncrementalSignature.Initialize(a, out state); + IncrementalSignature.Initialize(a, k, out state); Assert.Same(a, state.Algorithm); var actual = new byte[a.SignatureSize]; - IncrementalSignature.Finalize(ref state, k, actual); + IncrementalSignature.Finalize(ref state, actual); Assert.Null(state.Algorithm); @@ -185,65 +163,5 @@ public static void FinalizeWithSpanSuccessNoUpdate(SignatureAlgorithm2 a) } #endregion - - #region FinalizeAndVerify - - [Theory] - [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalizeAndVerifyInvalid(SignatureAlgorithm2 a) - { - using var k = new Key(a); - var state = default(IncrementalSignature); - - Assert.Throws(() => IncrementalSignature.FinalizeAndVerify(ref state, k.PublicKey, new byte[a.SignatureSize])); - } - - [Theory] - [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalizeAndVerifyWithNullKey(SignatureAlgorithm2 a) - { - IncrementalSignature.Initialize(a, out var state); - - Assert.Throws("publicKey", () => IncrementalSignature.FinalizeAndVerify(ref state, null!, new byte[a.SignatureSize])); - } - - [Theory] - [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalizeAndVerifyWithWrongKey(SignatureAlgorithm2 a) - { - using var k = new Key(SignatureAlgorithm.Ed25519); - - IncrementalSignature.Initialize(a, out var state); - - Assert.Throws("publicKey", () => IncrementalSignature.FinalizeAndVerify(ref state, k.PublicKey, new byte[a.SignatureSize])); - } - - [Theory] - [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalizeAndVerifyFail(SignatureAlgorithm2 a) - { - using var k = new Key(a); - - IncrementalSignature.Initialize(a, out var state); - - Assert.False(IncrementalSignature.FinalizeAndVerify(ref state, k.PublicKey, new byte[a.SignatureSize])); - - Assert.Null(state.Algorithm); - } - - [Theory] - [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void FinalizeAndVerifySuccess(SignatureAlgorithm2 a) - { - using var k = new Key(a); - - IncrementalSignature.Initialize(a, out var state); - - Assert.True(IncrementalSignature.FinalizeAndVerify(ref state, k.PublicKey, a.Sign(k, ReadOnlySpan.Empty))); - - Assert.Null(state.Algorithm); - } - - #endregion } } diff --git a/tests/Base/IncrementalSignatureVerificationTests.cs b/tests/Base/IncrementalSignatureVerificationTests.cs new file mode 100644 index 00000000..9e7c2bb4 --- /dev/null +++ b/tests/Base/IncrementalSignatureVerificationTests.cs @@ -0,0 +1,79 @@ +using System; +using NSec.Cryptography; +using Xunit; + +namespace NSec.Tests.Base +{ + public static class IncrementalSignatureVerificationTests + { + public static readonly TheoryData IncrementalSignatureAlgorithms = Registry.IncrementalSignatureAlgorithms; + + #region Initialize + + [Fact] + public static void InitializeWithNullAlgorithm() + { + using var k = new Key(SignatureAlgorithm.Ed25519ph); + + Assert.Throws("algorithm", () => IncrementalSignatureVerification.Initialize(null!, k.PublicKey, out _)); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void InitializeWithNullKey(SignatureAlgorithm2 a) + { + Assert.Throws("publicKey", () => IncrementalSignatureVerification.Initialize(a, null!, out _)); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void InitializeWithWrongKey(SignatureAlgorithm2 a) + { + using var k = new Key(SignatureAlgorithm.Ed25519); + + Assert.Throws("publicKey", () => IncrementalSignatureVerification.Initialize(a, k.PublicKey, out var state)); + } + + #endregion + + #region FinalizeAndVerify + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalizeAndVerifyInvalid(SignatureAlgorithm2 a) + { + using var k = new Key(a); + var state = default(IncrementalSignatureVerification); + + Assert.Throws(() => IncrementalSignatureVerification.FinalizeAndVerify(ref state, new byte[a.SignatureSize])); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalizeAndVerifyFail(SignatureAlgorithm2 a) + { + using var k = new Key(a); + + IncrementalSignatureVerification.Initialize(a, k.PublicKey, out var state); + + Assert.False(IncrementalSignatureVerification.FinalizeAndVerify(ref state, new byte[a.SignatureSize])); + + Assert.Null(state.Algorithm); + } + + [Theory] + [MemberData(nameof(IncrementalSignatureAlgorithms))] + public static void FinalizeAndVerifySuccess(SignatureAlgorithm2 a) + { + using var k = new Key(a); + + IncrementalSignatureVerification.Initialize(a, k.PublicKey, out var state); + + Assert.True(IncrementalSignatureVerification.FinalizeAndVerify(ref state, a.Sign(k, ReadOnlySpan.Empty))); + + Assert.Null(state.Algorithm); + } + + #endregion + } +} diff --git a/tests/Examples/Incremental.cs b/tests/Examples/Incremental.cs index dc6f786a..75052866 100644 --- a/tests/Examples/Incremental.cs +++ b/tests/Examples/Incremental.cs @@ -90,7 +90,7 @@ public static void Signature() using var key = Key.Create(algorithm); // initialize the state - IncrementalSignature.Initialize(algorithm, out var state); + IncrementalSignature.Initialize(algorithm, key, out var state); // incrementally update the state with some data var lines = new[] @@ -109,7 +109,7 @@ public static void Signature() } // finalize the computation using the private key and get the result - var signature = IncrementalSignature.Finalize(ref state, key); + var signature = IncrementalSignature.Finalize(ref state); #endregion @@ -143,7 +143,7 @@ public static void Verify() // sign { // initialize the state - IncrementalSignature.Initialize(algorithm, out var state); + IncrementalSignature.Initialize(algorithm, key, out var state); // incrementally update the state with the data foreach (var line in lines) @@ -152,22 +152,22 @@ public static void Verify() } // finalize the computation using the private key and get the result - signature = IncrementalSignature.Finalize(ref state, key); + signature = IncrementalSignature.Finalize(ref state); } // verify { // initialize the state - IncrementalSignature.Initialize(algorithm, out var state); + IncrementalSignatureVerification.Initialize(algorithm, key.PublicKey, out var state); // incrementally update the state with the data foreach (var line in lines) { - IncrementalSignature.Update(ref state, Encoding.UTF8.GetBytes(line)); + IncrementalSignatureVerification.Update(ref state, Encoding.UTF8.GetBytes(line)); } // verify the data using the signature and the public key - if (IncrementalSignature.FinalizeAndVerify(ref state, key.PublicKey, signature)) + if (IncrementalSignatureVerification.FinalizeAndVerify(ref state, signature)) { // verified! /*{*//*}*/ From 0bf015c61765bfbf2d591df62a74e32bc86f0cd3 Mon Sep 17 00:00:00 2001 From: joaomatossilva Date: Mon, 12 Jun 2023 22:58:44 +0100 Subject: [PATCH 04/26] Update Documentation --- NSec.sln | 3 + .../nsec.cryptography.incrementalsignature.md | 160 ++++++++++++++++++ ...graphy.incrementalsignatureverification.md | 137 +++++++++++++++ .../nsec.cryptography.signaturealgorithm2.md | 34 ++++ 4 files changed, 334 insertions(+) create mode 100644 docs/api/nsec.cryptography.incrementalsignature.md create mode 100644 docs/api/nsec.cryptography.incrementalsignatureverification.md create mode 100644 docs/api/nsec.cryptography.signaturealgorithm2.md diff --git a/NSec.sln b/NSec.sln index 937219c0..90d2b87c 100644 --- a/NSec.sln +++ b/NSec.sln @@ -62,6 +62,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "api", "api", "{F058E702-CA9 docs\api\nsec.cryptography.hashalgorithm.md = docs\api\nsec.cryptography.hashalgorithm.md docs\api\nsec.cryptography.incrementalhash.md = docs\api\nsec.cryptography.incrementalhash.md docs\api\nsec.cryptography.incrementalmac.md = docs\api\nsec.cryptography.incrementalmac.md + docs\api\nsec.cryptography.incrementalsignature.md = docs\api\nsec.cryptography.incrementalsignature.md + docs\api\nsec.cryptography.incrementalsignatureverification.md = docs\api\nsec.cryptography.incrementalsignatureverification.md docs\api\nsec.cryptography.key.md = docs\api\nsec.cryptography.key.md docs\api\nsec.cryptography.keyagreementalgorithm.md = docs\api\nsec.cryptography.keyagreementalgorithm.md docs\api\nsec.cryptography.keyblobformat.md = docs\api\nsec.cryptography.keyblobformat.md @@ -78,6 +80,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "api", "api", "{F058E702-CA9 docs\api\nsec.cryptography.sharedsecretblobformat.md = docs\api\nsec.cryptography.sharedsecretblobformat.md docs\api\nsec.cryptography.sharedsecretcreationparameters.md = docs\api\nsec.cryptography.sharedsecretcreationparameters.md docs\api\nsec.cryptography.signaturealgorithm.md = docs\api\nsec.cryptography.signaturealgorithm.md + docs\api\nsec.cryptography.signaturealgorithm2.md = docs\api\nsec.cryptography.signaturealgorithm2.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{030978CE-A451-4F42-887C-1B200DF7211C}" diff --git a/docs/api/nsec.cryptography.incrementalsignature.md b/docs/api/nsec.cryptography.incrementalsignature.md new file mode 100644 index 00000000..41692da8 --- /dev/null +++ b/docs/api/nsec.cryptography.incrementalsignature.md @@ -0,0 +1,160 @@ +# IncrementalSignature Struct + +Represents the state of a signature algorithm that can be incrementally updated with +segments of data. + + public readonly struct IncrementalSignature + +The type provides an "init, update, final" interface for generating the signature: First, a +state needs to be initialized with the signature algorithm to be used and the key. The state can +then be updated zero or more times with segments of data. Finalizing the state +yields a result that is identical to the signature of the concatenated segments. + +[[IncrementalSignature|IncrementalSignature Struct]] instances have value-type semantics: +Passing an instance to a method or assigning it to a variable creates a copy of +the state. + + +## Example + +The following C# example shows how to compute a signature from multiple segments of +data: + + {{Incremental Signature}} + + +## [TOC] Summary + + +## Properties + + +### Algorithm + +Gets the algorithm that was used to initialize the state. + + public SignatureAlgorithm2? Algorithm { get; } + +#### Property Value + +An instance of the [[SignatureAlgorithm2|SignatureAlgorithm2 Class]] class, or `null` if the +current instance has not been initialized yet or if it has been finalized. + + +## Static Methods + + +### Initialize(SignatureAlgorithm2, out IncrementalSignature) + +Initializes the [[IncrementalSignature|IncrementalSignature Struct]] state with the +specified signature algorithm. + + public static void Initialize( + SignatureAlgorithm2 algorithm, + Key privateKey, + out IncrementalSignature state) + +#### Parameters + +algorithm +: The signature algorithm to use for computing the signature. + +privateKey +: The key used to sign the data. + +state +: When this method returns, contains the initialized state. + +#### Exceptions + +ArgumentNullException +: `algorithm` is `null` or `privateKey` is `null`. + +ArgumentException +: `privateKey` algorithm does not match `algorithm`. + +### Update(ref IncrementalSignature, ReadOnlySpan) + +Updates the [[IncrementalSignature|IncrementalSignature Struct]] state with the specified +span of bytes. + + public static void Update( + ref IncrementalSignature state, + ReadOnlySpan data) + +#### Parameters + +state +: The state to be updated with `data`. + +data +: A segment of the data to sign. + +#### Exceptions + +InvalidOperationException +: `state` has not been initialized yet or has already been finalized. + + +### Finalize(ref IncrementalSignature) + +Completes the signature computation and returns the result as an array of bytes. + + public static byte[] Finalize( + ref IncrementalSignature state) + +#### Parameters + +state +: The state to be finalized. + +#### Return Value + +The computed signature. + +#### Exceptions + +InvalidOperationException +: `state` has not been initialized yet or has already been finalized. + + +### Finalize(ref IncrementalSignature, Span) + +Completes the signature computation and fills the specified span of bytes with the +result. + + public static void Finalize( + ref IncrementalSignature state, + Span signature) + +#### Parameters + +state +: The state to be finalized. + +signature +: The span to fill with the computed signature. + +#### Exceptions + +InvalidOperationException +: `state` has not been initialized yet or has already been finalized. + +ArgumentException +: `signature.Length` is not equal to [[SignatureSize|SignatureAlgorithm Class#SignatureSize]]. + + +## Thread Safety + +Any public static members of this type are thread safe. Any instance members are +not guaranteed to be thread safe. As with any other type, reading and writing to +a shared variable that contains an instance of this type must be protected by a +lock to guarantee thread safety. + + +## See Also + +* API Reference + * [[SignatureAlgorithm Class]] + * [[SignatureAlgorithm2 Class]] + * [[IncrementalSignatureVerification Struct]] diff --git a/docs/api/nsec.cryptography.incrementalsignatureverification.md b/docs/api/nsec.cryptography.incrementalsignatureverification.md new file mode 100644 index 00000000..a05bb105 --- /dev/null +++ b/docs/api/nsec.cryptography.incrementalsignatureverification.md @@ -0,0 +1,137 @@ +# IncrementalSignatureVerification Struct + +Represents the state of a signature verification algorithm that can be incrementally updated with +segments of data. + + public readonly struct IncrementalSignatureVerification + +The type provides an "init, update, final" interface for verifying a signature: First, a +state needs to be initialized with the signature algorithm to be used and the key. The state can +then be updated zero or more times with segments of data. Finalizing the state +yields a result that is identical to the signature verification of the concatenated segments. + +[[IncrementalSignatureVerification|IncrementalSignatureVerification Struct]] instances have value-type semantics: +Passing an instance to a method or assigning it to a variable creates a copy of +the state. + + +## Example + +The following C# example shows how to verify a computed signature from multiple segments of +data: + + {{Incremental Verify}} + + +## [TOC] Summary + + +## Properties + + +### Algorithm + +Gets the algorithm that was used to initialize the state. + + public SignatureAlgorithm2? Algorithm { get; } + +#### Property Value + +An instance of the [[SignatureAlgorithm2|SignatureAlgorithm2 Class]] class, or `null` if the +current instance has not been initialized yet or if it has been finalized. + + +## Static Methods + + +### Initialize(SignatureAlgorithm2, out IncrementalSignatureVerification) + +Initializes the [[IncrementalSignatureVerification|IncrementalSignatureVerification Struct]] state with the +specified signature algorithm. + + public static void Initialize( + SignatureAlgorithm2 algorithm, + PublicKey publicKey, + out IncrementalSignatureVerification state) + +#### Parameters + +algorithm +: The signature algorithm to use for computing the signature. + +publicKey +: The key used to verify the signature of the data. + +state +: When this method returns, contains the initialized state. + +#### Exceptions + +ArgumentNullException +: `algorithm` is `null` or `publicKey` is `null`. + +ArgumentException +: `publicKey` algorithm does not match `algorithm`. + +### Update(ref IncrementalSignatureVerification, ReadOnlySpan) + +Updates the [[IncrementalSignatureVerification|IncrementalSignatureVerification Struct]] state with the specified +span of bytes. + + public static void Update( + ref IncrementalSignatureVerification state, + ReadOnlySpan data) + +#### Parameters + +state +: The state to be updated with `data`. + +data +: A segment of the data used to verify the signature. + +#### Exceptions + +InvalidOperationException +: `state` has not been initialized yet or has already been finalized. + + +### Finalize(ref IncrementalSignatureVerification, ReadOnlySpan) + +Completes the signature computation and returns the result as an array of bytes. + + public static bool FinalizeAndVerify( + ref IncrementalSignatureVerification state, + ReadOnlySpan signature) + +#### Parameters + +state +: The state to be finalized. + +signature +: The signature to be validated + +#### Return Value + +A boolean result if the signature is valid. + +#### Exceptions + +InvalidOperationException +: `state` has not been initialized yet or has already been finalized. + +## Thread Safety + +Any public static members of this type are thread safe. Any instance members are +not guaranteed to be thread safe. As with any other type, reading and writing to +a shared variable that contains an instance of this type must be protected by a +lock to guarantee thread safety. + + +## See Also + +* API Reference + * [[SignatureAlgorithm Class]] + * [[SignatureAlgorithm2 Class]] + * [[IncrementalSignature Struct]] diff --git a/docs/api/nsec.cryptography.signaturealgorithm2.md b/docs/api/nsec.cryptography.signaturealgorithm2.md new file mode 100644 index 00000000..2818614d --- /dev/null +++ b/docs/api/nsec.cryptography.signaturealgorithm2.md @@ -0,0 +1,34 @@ +# SignatureAlgorithm2 Class + +Represents a digital signature algorithm supporting the "init, update, final" interface. + + public abstract class SignatureAlgorithm2 : SignatureAlgorithm + + +## Inheritance Hierarchy + +* [[Algorithm|Algorithm Class]] + * [[SignatureAlgorithm|SignatureAlgorithm Class]] + * **SignatureAlgorithm2** + * Ed25519ph + + +## [TOC] Summary + +## Thread Safety + +All members of this type are thread safe. + + +## Purity + +All methods yield the same result for the same arguments. + + +## See Also + +* API Reference + * [[Algorithm Class]] + * [[SignatureAlgorithm Class]] + * [[Key Class]] + * [[PublicKey Class]] From bfb0d98d677a6d5c458af109d3e78e107bdae7f6 Mon Sep 17 00:00:00 2001 From: ektrah Date: Sat, 17 Jun 2023 10:38:17 +0200 Subject: [PATCH 05/26] Improve Ed25519ph --- .../nsec.cryptography.incrementalsignature.md | 39 ++-- ...graphy.incrementalsignatureverification.md | 57 +++--- docs/api/nsec.cryptography.md | 4 + .../nsec.cryptography.signaturealgorithm.md | 16 ++ .../nsec.cryptography.signaturealgorithm2.md | 176 +++++++++++++++++- index.md | 1 + src/Cryptography/Ed25519ph.cs | 3 +- src/Cryptography/Error.cs | 6 + src/Cryptography/Error.resx | 3 + src/Cryptography/IncrementalSignature.cs | 52 +++--- .../IncrementalSignatureVerification.cs | 45 +++-- src/Interop/Interop.Sign.Ed25519.cs | 3 + src/Interop/Interop.yaml | 9 +- ...Tests2.cs => IncrementalSignatureTests.cs} | 27 +-- .../IncrementalSignatureVerificationTests.cs | 20 +- tests/Examples/Incremental.cs | 88 ++++----- 16 files changed, 374 insertions(+), 175 deletions(-) rename tests/Base/{IncrementalSignatureTests2.cs => IncrementalSignatureTests.cs} (83%) diff --git a/docs/api/nsec.cryptography.incrementalsignature.md b/docs/api/nsec.cryptography.incrementalsignature.md index 41692da8..1ab1be68 100644 --- a/docs/api/nsec.cryptography.incrementalsignature.md +++ b/docs/api/nsec.cryptography.incrementalsignature.md @@ -5,14 +5,16 @@ segments of data. public readonly struct IncrementalSignature -The type provides an "init, update, final" interface for generating the signature: First, a -state needs to be initialized with the signature algorithm to be used and the key. The state can +The type provides an "init, update, final" interface for computing a signature: +First, a state needs to be initialized with the signing key to be used. The state can then be updated zero or more times with segments of data. Finalizing the state yields a result that is identical to the signature of the concatenated segments. -[[IncrementalSignature|IncrementalSignature Struct]] instances have value-type semantics: -Passing an instance to a method or assigning it to a variable creates a copy of -the state. +!!! Note + [[IncrementalSignature|IncrementalSignature Struct]] instances have + value-type semantics: Passing an instance to a method or assigning it to a + variable creates a copy of the state. It is therefore recommended to always + pass instances using `ref`, `in`, or `out`. ## Example @@ -20,7 +22,7 @@ the state. The following C# example shows how to compute a signature from multiple segments of data: - {{Incremental Signature}} + {{Incremental Signing}} ## [TOC] Summary @@ -44,23 +46,19 @@ current instance has not been initialized yet or if it has been finalized. ## Static Methods -### Initialize(SignatureAlgorithm2, out IncrementalSignature) +### Initialize(Key, out IncrementalSignature) Initializes the [[IncrementalSignature|IncrementalSignature Struct]] state with the -specified signature algorithm. +specified private key. public static void Initialize( - SignatureAlgorithm2 algorithm, Key privateKey, out IncrementalSignature state) #### Parameters -algorithm -: The signature algorithm to use for computing the signature. - privateKey -: The key used to sign the data. +: The private key to use for computing the signature. state : When this method returns, contains the initialized state. @@ -68,10 +66,12 @@ state #### Exceptions ArgumentNullException -: `algorithm` is `null` or `privateKey` is `null`. +: `privateKey` is `null`. ArgumentException -: `privateKey` algorithm does not match `algorithm`. +: `privateKey.Algorithm` is not an instance of the + [[SignatureAlgorithm2|SignatureAlgorithm2 Class]] class. + ### Update(ref IncrementalSignature, ReadOnlySpan) @@ -141,7 +141,10 @@ InvalidOperationException : `state` has not been initialized yet or has already been finalized. ArgumentException -: `signature.Length` is not equal to [[SignatureSize|SignatureAlgorithm Class#SignatureSize]]. +: `signature.Length` is not equal to [[SignatureSize|SignatureAlgorithm2 Class#SignatureSize]]. + +ObjectDisposedException +: The private key used to initialize the state has been disposed. ## Thread Safety @@ -155,6 +158,6 @@ lock to guarantee thread safety. ## See Also * API Reference - * [[SignatureAlgorithm Class]] - * [[SignatureAlgorithm2 Class]] * [[IncrementalSignatureVerification Struct]] + * [[Key Class]] + * [[SignatureAlgorithm2 Class]] diff --git a/docs/api/nsec.cryptography.incrementalsignatureverification.md b/docs/api/nsec.cryptography.incrementalsignatureverification.md index a05bb105..b24db4f3 100644 --- a/docs/api/nsec.cryptography.incrementalsignatureverification.md +++ b/docs/api/nsec.cryptography.incrementalsignatureverification.md @@ -5,22 +5,25 @@ segments of data. public readonly struct IncrementalSignatureVerification -The type provides an "init, update, final" interface for verifying a signature: First, a -state needs to be initialized with the signature algorithm to be used and the key. The state can -then be updated zero or more times with segments of data. Finalizing the state -yields a result that is identical to the signature verification of the concatenated segments. +The type provides an "init, update, final" interface for verifying data given a +public key and a signature. First, a state needs to be initialized with the +public key. The state can then be updated zero or more times with segments of +data. Finalizing the state gives a result as to whether verification of the +concatenated segments was successful. -[[IncrementalSignatureVerification|IncrementalSignatureVerification Struct]] instances have value-type semantics: -Passing an instance to a method or assigning it to a variable creates a copy of -the state. +!!! Note + [[IncrementalSignatureVerification|IncrementalSignatureVerification Struct]] + instances have value-type semantics: Passing an instance to a method or + assigning it to a variable creates a copy of the state. It is therefore + recommended to always pass instances using `ref`, `in`, or `out`. ## Example -The following C# example shows how to verify a computed signature from multiple segments of -data: +The following C# example shows how to verify multiple segments of data given a +public key and a signature: - {{Incremental Verify}} + {{Incremental Signature Verification}} ## [TOC] Summary @@ -44,23 +47,20 @@ current instance has not been initialized yet or if it has been finalized. ## Static Methods -### Initialize(SignatureAlgorithm2, out IncrementalSignatureVerification) +### Initialize(PublicKey, out IncrementalSignatureVerification) -Initializes the [[IncrementalSignatureVerification|IncrementalSignatureVerification Struct]] state with the -specified signature algorithm. +Initializes the +[[IncrementalSignatureVerification|IncrementalSignatureVerification Struct]] +state with the specified public key. public static void Initialize( - SignatureAlgorithm2 algorithm, PublicKey publicKey, out IncrementalSignatureVerification state) #### Parameters -algorithm -: The signature algorithm to use for computing the signature. - publicKey -: The key used to verify the signature of the data. +: The public key to use for verifying the data. state : When this method returns, contains the initialized state. @@ -68,10 +68,12 @@ state #### Exceptions ArgumentNullException -: `algorithm` is `null` or `publicKey` is `null`. +: `publicKey` is `null`. ArgumentException -: `publicKey` algorithm does not match `algorithm`. +: `publicKey.Algorithm` is not an instance of the + [[SignatureAlgorithm2|SignatureAlgorithm2 Class]] class. + ### Update(ref IncrementalSignatureVerification, ReadOnlySpan) @@ -88,7 +90,7 @@ state : The state to be updated with `data`. data -: A segment of the data used to verify the signature. +: A segment of the data to verify. #### Exceptions @@ -96,9 +98,9 @@ InvalidOperationException : `state` has not been initialized yet or has already been finalized. -### Finalize(ref IncrementalSignatureVerification, ReadOnlySpan) +### FinalizeAndVerify(ref IncrementalSignatureVerification, ReadOnlySpan) -Completes the signature computation and returns the result as an array of bytes. +Completes the verification. public static bool FinalizeAndVerify( ref IncrementalSignatureVerification state, @@ -110,17 +112,18 @@ state : The state to be finalized. signature -: The signature to be validated +: The signature of the data to verify. #### Return Value -A boolean result if the signature is valid. +`true` if verification succeeds; otherwise, `false`. #### Exceptions InvalidOperationException : `state` has not been initialized yet or has already been finalized. + ## Thread Safety Any public static members of this type are thread safe. Any instance members are @@ -132,6 +135,6 @@ lock to guarantee thread safety. ## See Also * API Reference - * [[SignatureAlgorithm Class]] - * [[SignatureAlgorithm2 Class]] * [[IncrementalSignature Struct]] + * [[PublicKey Class]] + * [[SignatureAlgorithm2 Class]] diff --git a/docs/api/nsec.cryptography.md b/docs/api/nsec.cryptography.md index 3279a4ee..56451f2a 100644 --- a/docs/api/nsec.cryptography.md +++ b/docs/api/nsec.cryptography.md @@ -26,8 +26,12 @@ * [[ScryptParameters Struct]] * [[SignatureAlgorithm Class]] * Ed25519 Class + * [[SignatureAlgorithm2 Class]] + * Ed25519ph Class * [[IncrementalHash Struct]] * [[IncrementalMac Struct]] +* [[IncrementalSignature Struct]] +* [[IncrementalSignatureVerification Struct]] * [[Key Class]] * [[KeyCreationParameters Struct]] * [[KeyExportPolicies Enum]] diff --git a/docs/api/nsec.cryptography.signaturealgorithm.md b/docs/api/nsec.cryptography.signaturealgorithm.md index f0463174..12dbfe8a 100644 --- a/docs/api/nsec.cryptography.signaturealgorithm.md +++ b/docs/api/nsec.cryptography.signaturealgorithm.md @@ -10,6 +10,8 @@ Represents a digital signature algorithm. * [[Algorithm|Algorithm Class]] * **SignatureAlgorithm** * Ed25519 + * [[SignatureAlgorithm2|SignatureAlgorithm2 Class]] + * Ed25519ph ## [TOC] Summary @@ -25,6 +27,19 @@ Gets the Ed25519 signature algorithm. public static Ed25519 Ed25519 { get; } +### Ed25519ph + +Gets the Ed25519ph signature algorithm. + + public static Ed25519ph Ed25519ph { get; } + +!!! Note + Ed25519ph incorporates a pre-hashing step to support [[incremental + signing|IncrementalSignature Struct]]. As a result, the outputs of Ed25519 + and Ed25519ph are not the same. For most cryptographic applications, it is + recommended to use Ed25519 due to its broader adoption. + + ## Properties @@ -189,3 +204,4 @@ All methods yield the same result for the same arguments. * [[Algorithm Class]] * [[Key Class]] * [[PublicKey Class]] + * [[SignatureAlgorithm2 Class]] diff --git a/docs/api/nsec.cryptography.signaturealgorithm2.md b/docs/api/nsec.cryptography.signaturealgorithm2.md index 2818614d..fc210163 100644 --- a/docs/api/nsec.cryptography.signaturealgorithm2.md +++ b/docs/api/nsec.cryptography.signaturealgorithm2.md @@ -1,6 +1,8 @@ # SignatureAlgorithm2 Class -Represents a digital signature algorithm supporting the "init, update, final" interface. +Represents a digital signature algorithm that supports [[incremental +signing|IncrementalSignature Struct]] and [[incremental signature +verification|IncrementalSignatureVerification Struct]]. public abstract class SignatureAlgorithm2 : SignatureAlgorithm @@ -9,12 +11,178 @@ Represents a digital signature algorithm supporting the "init, update, final" in * [[Algorithm|Algorithm Class]] * [[SignatureAlgorithm|SignatureAlgorithm Class]] - * **SignatureAlgorithm2** - * Ed25519ph + * Ed25519 + * **SignatureAlgorithm2** + * Ed25519ph ## [TOC] Summary + +## Static Properties + + +### Ed25519ph + +Gets the Ed25519ph signature algorithm. + + public static Ed25519ph Ed25519ph { get; } + +!!! Note + Ed25519ph incorporates a pre-hashing step to support [[incremental + signing|IncrementalSignature Struct]]. As a result, the outputs of Ed25519 + and Ed25519ph are not the same. For most cryptographic applications, it is + recommended to use Ed25519 due to its broader adoption. + + +## Properties + + +### PrivateKeySize + +Gets the size of private keys. + + public int PrivateKeySize { get; } + +#### Property Value + +The private key size, in bytes. + + +### PublicKeySize + +Gets the size of public keys. + + public int PublicKeySize { get; } + +#### Property Value + +The public key size, in bytes. + + +### SignatureSize + +Gets the size of a signature. + + public int SignatureSize { get; } + +#### Property Value + +The signature size, in bytes. + + +## Methods + + +### Sign(Key, ReadOnlySpan) + +Signs the specified input data using the specified key and returns the signature +as an array of bytes. + + public byte[] Sign( + Key key, + ReadOnlySpan data) + +#### Parameters + +key +: The [[Key|Key Class]] to use for signing. + +data +: The data to sign. + +#### Return Value + +The signature for the data. + +#### Exceptions + +ArgumentNullException +: `key` is `null`. + +ArgumentException +: `key.Algorithm` is not the same object as the current + [[SignatureAlgorithm|SignatureAlgorithm Class]] object. + +ObjectDisposedException +: `key` has been disposed. + + +### Sign(Key, ReadOnlySpan, Span) + +Signs the specified input data using the specified key and fills the specified +span of bytes with the signature. + + public void Sign( + Key key, + ReadOnlySpan data, + Span signature) + +#### Parameters + +key +: The [[Key|Key Class]] to use for signing. + +data +: The data to sign. + +signature +: The span to fill with the signature for the data. + +#### Exceptions + +ArgumentNullException +: `key` is `null`. + +ArgumentException +: `key.Algorithm` is not the same object as the current + [[SignatureAlgorithm|SignatureAlgorithm Class]] object. + +ArgumentException +: `signature.Length` is not equal to + [[SignatureSize|SignatureAlgorithm Class#SignatureSize]]. + +ObjectDisposedException +: `key` has been disposed. + + +### Verify(PublicKey, ReadOnlySpan, ReadOnlySpan) + +Verifies specified input data using the specified public key and signature. + + public bool Verify( + PublicKey publicKey, + ReadOnlySpan data, + ReadOnlySpan signature) + +#### Parameters + +publicKey +: The [[PublicKey|PublicKey Class]] to use for verification. + Verification fails if this is not the public key for the private key used + when signing the data. + +data +: The data to verify. + Verification fails if the integrity of the data was compromised. + +signature +: The signature of the data to verify. + +#### Return Value + +`true` if verification succeeds; otherwise, `false`. + +#### Exceptions + +ArgumentNullException +: `publicKey` is `null`. + +ArgumentException +: `publicKey.Algorithm` is not the same object as the current + [[SignatureAlgorithm|SignatureAlgorithm Class]] object. + + ## Thread Safety All members of this type are thread safe. @@ -29,6 +197,6 @@ All methods yield the same result for the same arguments. * API Reference * [[Algorithm Class]] - * [[SignatureAlgorithm Class]] * [[Key Class]] * [[PublicKey Class]] + * [[SignatureAlgorithm Class]] diff --git a/index.md b/index.md index 0e7cef50..60116da0 100644 --- a/index.md +++ b/index.md @@ -68,6 +68,7 @@ See [[Installation]] for more details. | [[PasswordBasedKeyDerivationAlgorithm Class]] | Argon2id | | | scrypt | | [[SignatureAlgorithm Class]] | Ed25519 | +| [[SignatureAlgorithm2 Class]] | Ed25519ph | See [[API Reference|NSec.Cryptography Namespace]] for detailed information. diff --git a/src/Cryptography/Ed25519ph.cs b/src/Cryptography/Ed25519ph.cs index 181035f1..2f3c4aa1 100644 --- a/src/Cryptography/Ed25519ph.cs +++ b/src/Cryptography/Ed25519ph.cs @@ -286,7 +286,8 @@ private static void SelfTest() if ((crypto_sign_ed25519_bytes() != crypto_sign_ed25519_BYTES) || (crypto_sign_ed25519_publickeybytes() != crypto_sign_ed25519_PUBLICKEYBYTES) || (crypto_sign_ed25519_secretkeybytes() != crypto_sign_ed25519_SECRETKEYBYTES) || - (crypto_sign_ed25519_seedbytes() != crypto_sign_ed25519_SEEDBYTES)) + (crypto_sign_ed25519_seedbytes() != crypto_sign_ed25519_SEEDBYTES) || + (crypto_sign_ed25519ph_statebytes() != (nuint)Unsafe.SizeOf())) { throw Error.InvalidOperation_InitializationFailed(); } diff --git a/src/Cryptography/Error.cs b/src/Cryptography/Error.cs index d988ac92..f12f3418 100644 --- a/src/Cryptography/Error.cs +++ b/src/Cryptography/Error.cs @@ -221,6 +221,12 @@ internal static ArgumentException Argument_SharedSecretLength( return new ArgumentException(string.Format(ResourceManager.GetString(nameof(Argument_SharedSecretLength))!, arg0), paramName); } + internal static ArgumentException Argument_SignatureKeyRequired( + string paramName) + { + return new ArgumentException(ResourceManager.GetString(nameof(Argument_SignatureKeyRequired)), paramName); + } + internal static ArgumentException Argument_SignatureLength( string paramName, object? arg0) diff --git a/src/Cryptography/Error.resx b/src/Cryptography/Error.resx index ad728a24..2b04a665 100644 --- a/src/Cryptography/Error.resx +++ b/src/Cryptography/Error.resx @@ -258,6 +258,9 @@ The length of the shared secret must be less than or equal to {0}. + + Cannot use a key for an incremental signature that is not a signature key. + The signature must have a length of {0} bytes. diff --git a/src/Cryptography/IncrementalSignature.cs b/src/Cryptography/IncrementalSignature.cs index a2614f7b..a4523aa7 100644 --- a/src/Cryptography/IncrementalSignature.cs +++ b/src/Cryptography/IncrementalSignature.cs @@ -10,7 +10,6 @@ public readonly struct IncrementalSignature { private readonly IncrementalSignatureState _state; private readonly SignatureAlgorithm2? _algorithm; - private readonly Key? _privateKey; public SignatureAlgorithm2? Algorithm => _algorithm; @@ -24,22 +23,16 @@ public readonly struct IncrementalSignature } public static void Initialize( - SignatureAlgorithm2 algorithm, Key privateKey, out IncrementalSignature state) { - if (algorithm == null) - { - throw Error.ArgumentNull_Algorithm(nameof(algorithm)); - } - if (privateKey == null) { throw Error.ArgumentNull_Key(nameof(privateKey)); } - if (privateKey.Algorithm != algorithm) + if (privateKey.Algorithm is not SignatureAlgorithm2 algorithm) { - throw Error.Argument_KeyAlgorithmMismatch(nameof(privateKey), nameof(algorithm)); + throw Error.Argument_SignatureKeyRequired(nameof(privateKey)); } state = default; @@ -63,11 +56,7 @@ public static void Update( public static byte[] Finalize( ref IncrementalSignature state) { - if (state._algorithm == null) - { - throw Error.InvalidOperation_UninitializedState(); - } - if (state._privateKey == null) + if (state._algorithm == null || state._privateKey == null) { throw Error.InvalidOperation_UninitializedState(); } @@ -89,16 +78,10 @@ public static void Finalize( ref IncrementalSignature state, Span signature) { - if (state._algorithm == null) - { - throw Error.InvalidOperation_UninitializedState(); - } - - if (state._privateKey == null) + if (state._algorithm == null || state._privateKey == null) { throw Error.InvalidOperation_UninitializedState(); } - if (signature.Length != state._algorithm.SignatureSize) { throw Error.Argument_SignatureLength(nameof(signature), state._algorithm.SignatureSize); @@ -113,5 +96,32 @@ public static void Finalize( Unsafe.AsRef(in state._algorithm) = null; } } + + [EditorBrowsable(EditorBrowsableState.Never)] + public static new bool ReferenceEquals( + object? objA, + object? objB) + { + return object.ReferenceEquals(objA, objB); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals( + object? obj) + { + throw Error.NotSupported_Operation(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + { + throw Error.NotSupported_Operation(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override string? ToString() + { + return typeof(IncrementalSignature).ToString(); + } } } diff --git a/src/Cryptography/IncrementalSignatureVerification.cs b/src/Cryptography/IncrementalSignatureVerification.cs index d1378434..fec4597f 100644 --- a/src/Cryptography/IncrementalSignatureVerification.cs +++ b/src/Cryptography/IncrementalSignatureVerification.cs @@ -10,7 +10,6 @@ public readonly struct IncrementalSignatureVerification { private readonly IncrementalSignatureState _state; private readonly SignatureAlgorithm2? _algorithm; - private readonly PublicKey? _publicKey; public SignatureAlgorithm2? Algorithm => _algorithm; @@ -24,22 +23,16 @@ public readonly struct IncrementalSignatureVerification } public static void Initialize( - SignatureAlgorithm2 algorithm, PublicKey publicKey, out IncrementalSignatureVerification state) { - if (algorithm == null) - { - throw Error.ArgumentNull_Algorithm(nameof(algorithm)); - } - if (publicKey == null) { throw Error.ArgumentNull_Key(nameof(publicKey)); } - if (publicKey.Algorithm != algorithm) + if (publicKey.Algorithm is not SignatureAlgorithm2 algorithm) { - throw Error.Argument_PublicKeyAlgorithmMismatch(nameof(publicKey), nameof(publicKey)); + throw Error.Argument_SignatureKeyRequired(nameof(publicKey)); } state = default; @@ -64,12 +57,7 @@ public static bool FinalizeAndVerify( ref IncrementalSignatureVerification state, ReadOnlySpan signature) { - if (state._algorithm == null) - { - throw Error.InvalidOperation_UninitializedState(); - } - - if (state._publicKey == null) + if (state._algorithm == null || state._publicKey == null) { throw Error.InvalidOperation_UninitializedState(); } @@ -85,5 +73,32 @@ public static bool FinalizeAndVerify( Unsafe.AsRef(in state._publicKey) = null; } } + + [EditorBrowsable(EditorBrowsableState.Never)] + public static new bool ReferenceEquals( + object? objA, + object? objB) + { + return object.ReferenceEquals(objA, objB); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals( + object? obj) + { + throw Error.NotSupported_Operation(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + { + throw Error.NotSupported_Operation(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override string? ToString() + { + return typeof(IncrementalSignatureVerification).ToString(); + } } } diff --git a/src/Interop/Interop.Sign.Ed25519.cs b/src/Interop/Interop.Sign.Ed25519.cs index bd527a01..e576e250 100644 --- a/src/Interop/Interop.Sign.Ed25519.cs +++ b/src/Interop/Interop.Sign.Ed25519.cs @@ -75,6 +75,9 @@ internal static unsafe extern int crypto_sign_ed25519ph_final_verify( internal static unsafe extern int crypto_sign_ed25519ph_init( crypto_sign_ed25519ph_state* state); + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static extern nuint crypto_sign_ed25519ph_statebytes(); + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] internal static unsafe extern int crypto_sign_ed25519ph_update( crypto_sign_ed25519ph_state* state, diff --git a/src/Interop/Interop.yaml b/src/Interop/Interop.yaml index 73fdf09e..3c15e981 100644 --- a/src/Interop/Interop.yaml +++ b/src/Interop/Interop.yaml @@ -288,15 +288,16 @@ Interop.Sign.Ed25519.cs: - crypto_sign_ed25519_pk_to_curve25519 (PublicKeyBytes* curve25519_pk, PublicKeyBytes* ed25519_pk) - crypto_sign_ed25519_publickeybytes - crypto_sign_ed25519_secretkeybytes - - crypto_sign_ed25519_sk_to_curve25519 (SecureMemoryHandle ed25519_sk) - - crypto_sign_ed25519_sk_to_seed (SecureMemoryHandle sk) - crypto_sign_ed25519_seed_keypair (PublicKeyBytes* pk, SecureMemoryHandle sk) - crypto_sign_ed25519_seedbytes + - crypto_sign_ed25519_sk_to_curve25519 (SecureMemoryHandle ed25519_sk) + - crypto_sign_ed25519_sk_to_seed (SecureMemoryHandle sk) - crypto_sign_ed25519_verify_detached (PublicKeyBytes* pk) - - crypto_sign_ed25519ph_init - - crypto_sign_ed25519ph_update - crypto_sign_ed25519ph_final_create (out ulong siglen_p, SecureMemoryHandle sk) - crypto_sign_ed25519ph_final_verify (PublicKeyBytes* pk) + - crypto_sign_ed25519ph_init + - crypto_sign_ed25519ph_statebytes + - crypto_sign_ed25519ph_update structs: - crypto_sign_ed25519ph_state diff --git a/tests/Base/IncrementalSignatureTests2.cs b/tests/Base/IncrementalSignatureTests.cs similarity index 83% rename from tests/Base/IncrementalSignatureTests2.cs rename to tests/Base/IncrementalSignatureTests.cs index 342afd87..bf33da09 100644 --- a/tests/Base/IncrementalSignatureTests2.cs +++ b/tests/Base/IncrementalSignatureTests.cs @@ -11,18 +11,9 @@ public static class IncrementalSignatureTests #region Initialize [Fact] - public static void InitializeWithNullAlgorithm() + public static void InitializeWithNullKey() { - using var k = new Key(SignatureAlgorithm.Ed25519ph); - - Assert.Throws("algorithm", () => IncrementalSignature.Initialize(null!, k, out _)); - } - - [Theory] - [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void InitializeWithNullKey(SignatureAlgorithm2 a) - { - Assert.Throws("privateKey", () => IncrementalSignature.Initialize(a, null!, out _)); + Assert.Throws("privateKey", () => IncrementalSignature.Initialize(null!, out _)); } [Theory] @@ -31,7 +22,7 @@ public static void InitializeWithWrongKey(SignatureAlgorithm2 a) { using var k = new Key(SignatureAlgorithm.Ed25519); - Assert.Throws("privateKey", () => IncrementalSignature.Initialize(a, k, out var state)); + Assert.Throws("privateKey", () => IncrementalSignature.Initialize(k, out var state)); } #endregion @@ -42,7 +33,6 @@ public static void InitializeWithWrongKey(SignatureAlgorithm2 a) [MemberData(nameof(IncrementalSignatureAlgorithms))] public static void FinalizeInvalid(SignatureAlgorithm2 a) { - using var k = new Key(a); var state = default(IncrementalSignature); Assert.Throws(() => IncrementalSignature.Finalize(ref state)); @@ -57,7 +47,7 @@ public static void FinalizeSuccess(SignatureAlgorithm2 a) Assert.Null(state.Algorithm); - IncrementalSignature.Initialize(a, k, out state); + IncrementalSignature.Initialize(k, out state); Assert.Same(a, state.Algorithm); @@ -82,7 +72,6 @@ public static void FinalizeSuccess(SignatureAlgorithm2 a) [MemberData(nameof(IncrementalSignatureAlgorithms))] public static void FinalizeWithSpanInvalid(SignatureAlgorithm2 a) { - using var k = new Key(a); var state = default(IncrementalSignature); Assert.Throws(() => IncrementalSignature.Finalize(ref state, new byte[a.SignatureSize])); @@ -94,7 +83,7 @@ public static void FinalizeWithSpanTooSmall(SignatureAlgorithm2 a) { using var k = new Key(a); - IncrementalSignature.Initialize(a, k, out var state); + IncrementalSignature.Initialize(k, out var state); Assert.Throws("signature", () => IncrementalSignature.Finalize(ref state, new byte[a.SignatureSize - 1])); } @@ -105,7 +94,7 @@ public static void FinalizeWithSpanTooLarge(SignatureAlgorithm2 a) { using var k = new Key(a); - IncrementalSignature.Initialize(a, k, out var state); + IncrementalSignature.Initialize(k, out var state); Assert.Throws("signature", () => IncrementalSignature.Finalize(ref state, new byte[a.SignatureSize + 1])); } @@ -119,7 +108,7 @@ public static void FinalizeWithSpanSuccess(SignatureAlgorithm2 a) Assert.Null(state.Algorithm); - IncrementalSignature.Initialize(a, k, out state); + IncrementalSignature.Initialize(k, out state); Assert.Same(a, state.Algorithm); @@ -147,7 +136,7 @@ public static void FinalizeWithSpanSuccessNoUpdate(SignatureAlgorithm2 a) Assert.Null(state.Algorithm); - IncrementalSignature.Initialize(a, k, out state); + IncrementalSignature.Initialize(k, out state); Assert.Same(a, state.Algorithm); diff --git a/tests/Base/IncrementalSignatureVerificationTests.cs b/tests/Base/IncrementalSignatureVerificationTests.cs index 9e7c2bb4..f1bf7792 100644 --- a/tests/Base/IncrementalSignatureVerificationTests.cs +++ b/tests/Base/IncrementalSignatureVerificationTests.cs @@ -11,18 +11,9 @@ public static class IncrementalSignatureVerificationTests #region Initialize [Fact] - public static void InitializeWithNullAlgorithm() + public static void InitializeWithNullKey() { - using var k = new Key(SignatureAlgorithm.Ed25519ph); - - Assert.Throws("algorithm", () => IncrementalSignatureVerification.Initialize(null!, k.PublicKey, out _)); - } - - [Theory] - [MemberData(nameof(IncrementalSignatureAlgorithms))] - public static void InitializeWithNullKey(SignatureAlgorithm2 a) - { - Assert.Throws("publicKey", () => IncrementalSignatureVerification.Initialize(a, null!, out _)); + Assert.Throws("publicKey", () => IncrementalSignatureVerification.Initialize(null!, out _)); } [Theory] @@ -31,7 +22,7 @@ public static void InitializeWithWrongKey(SignatureAlgorithm2 a) { using var k = new Key(SignatureAlgorithm.Ed25519); - Assert.Throws("publicKey", () => IncrementalSignatureVerification.Initialize(a, k.PublicKey, out var state)); + Assert.Throws("publicKey", () => IncrementalSignatureVerification.Initialize(k.PublicKey, out var state)); } #endregion @@ -42,7 +33,6 @@ public static void InitializeWithWrongKey(SignatureAlgorithm2 a) [MemberData(nameof(IncrementalSignatureAlgorithms))] public static void FinalizeAndVerifyInvalid(SignatureAlgorithm2 a) { - using var k = new Key(a); var state = default(IncrementalSignatureVerification); Assert.Throws(() => IncrementalSignatureVerification.FinalizeAndVerify(ref state, new byte[a.SignatureSize])); @@ -54,7 +44,7 @@ public static void FinalizeAndVerifyFail(SignatureAlgorithm2 a) { using var k = new Key(a); - IncrementalSignatureVerification.Initialize(a, k.PublicKey, out var state); + IncrementalSignatureVerification.Initialize(k.PublicKey, out var state); Assert.False(IncrementalSignatureVerification.FinalizeAndVerify(ref state, new byte[a.SignatureSize])); @@ -67,7 +57,7 @@ public static void FinalizeAndVerifySuccess(SignatureAlgorithm2 a) { using var k = new Key(a); - IncrementalSignatureVerification.Initialize(a, k.PublicKey, out var state); + IncrementalSignatureVerification.Initialize(k.PublicKey, out var state); Assert.True(IncrementalSignatureVerification.FinalizeAndVerify(ref state, a.Sign(k, ReadOnlySpan.Empty))); diff --git a/tests/Examples/Incremental.cs b/tests/Examples/Incremental.cs index 75052866..549fa201 100644 --- a/tests/Examples/Incremental.cs +++ b/tests/Examples/Incremental.cs @@ -81,7 +81,18 @@ public static void Mac() [Fact] public static void Signature() { - #region Incremental Signature + #region Incremental Signing + + // define some data to be signed + var lines = new[] + { + "Luke Skywalker has returned to\n", + "his home planet of Tatooine in\n", + "an attempt to rescue his\n", + "friend Han Solo from the\n", + "clutches of the vile gangster\n", + "Jabba the Hutt.\n", + }; // select the Ed25519ph algorithm var algorithm = SignatureAlgorithm.Ed25519ph; @@ -89,26 +100,16 @@ public static void Signature() // create a new key pair using var key = Key.Create(algorithm); - // initialize the state - IncrementalSignature.Initialize(algorithm, key, out var state); + // initialize the state with the private key + IncrementalSignature.Initialize(key, out var state); - // incrementally update the state with some data - var lines = new[] - { - "It is a dark time for the\n", - "Rebellion. Although the Death\n", - "Star has been destroyed,\n", - "Imperial troops have driven the\n", - "Rebel forces from their hidden\n", - "base and pursued them across\n", - "the galaxy.\n" - }; + // incrementally update the state with the data foreach (var line in lines) { IncrementalSignature.Update(ref state, Encoding.UTF8.GetBytes(line)); } - // finalize the computation using the private key and get the result + // finalize the computation and get the result var signature = IncrementalSignature.Finalize(ref state); #endregion @@ -117,17 +118,13 @@ public static void Signature() } [Fact] - public static void Verify() + public static void SignatureVerification() { - #region Incremental Signature Verification + using var key = Key.Create(SignatureAlgorithm.Ed25519ph); - // select the Ed25519ph algorithm - var algorithm = SignatureAlgorithm.Ed25519ph; - - // create a new key pair - using var key = Key.Create(algorithm); + #region Incremental Signature Verification - // create some data to be signed + // define some data to be verified var lines = new[] { "Luke Skywalker has returned to\n", @@ -138,40 +135,29 @@ public static void Verify() "Jabba the Hutt.\n", }; - byte[] signature; + // select the Ed25519ph algorithm + var algorithm = SignatureAlgorithm.Ed25519ph; + + // obtain the public key + var publicKey = /*{*/key.PublicKey;/*}*/ - // sign - { - // initialize the state - IncrementalSignature.Initialize(algorithm, key, out var state); + // obtain the signature + var signature = /*{*/algorithm.Sign(key, Encoding.UTF8.GetBytes(string.Concat(lines)));/*}*/ - // incrementally update the state with the data - foreach (var line in lines) - { - IncrementalSignature.Update(ref state, Encoding.UTF8.GetBytes(line)); - } + // initialize the state with the public key + IncrementalSignatureVerification.Initialize(publicKey, out var state); - // finalize the computation using the private key and get the result - signature = IncrementalSignature.Finalize(ref state); + // incrementally update the state with the data + foreach (var line in lines) + { + IncrementalSignatureVerification.Update(ref state, Encoding.UTF8.GetBytes(line)); } - // verify + // verify the data using the signature + if (IncrementalSignatureVerification.FinalizeAndVerify(ref state, signature)) { - // initialize the state - IncrementalSignatureVerification.Initialize(algorithm, key.PublicKey, out var state); - - // incrementally update the state with the data - foreach (var line in lines) - { - IncrementalSignatureVerification.Update(ref state, Encoding.UTF8.GetBytes(line)); - } - - // verify the data using the signature and the public key - if (IncrementalSignatureVerification.FinalizeAndVerify(ref state, signature)) - { - // verified! - /*{*//*}*/ - } + // verified! + /*{*//*}*/ } #endregion From 9f5bc4e4ae530c537fa5345940ec1cbbe6e32be6 Mon Sep 17 00:00:00 2001 From: ektrah Date: Sat, 17 Jun 2023 11:17:15 +0200 Subject: [PATCH 06/26] Update supported platforms --- .github/workflows/test.yml | 2 ++ docs/install.md | 32 +++++++++++++++++--------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e4dbf255..19967553 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,6 +73,7 @@ jobs: - os: centos:7 - os: debian:10 - os: debian:11 + - os: debian:12 - os: fedora:37 - os: fedora:38 - os: ubuntu:16.04 @@ -121,6 +122,7 @@ jobs: matrix: include: - os: alpine3.17 + - os: alpine3.18 runs-on: ubuntu-latest container: image: mcr.microsoft.com/dotnet/sdk:7.0-${{ matrix.os }} diff --git a/docs/install.md b/docs/install.md index e3d9d90f..b9225b25 100644 --- a/docs/install.md +++ b/docs/install.md @@ -40,23 +40,25 @@ has been tested to run on the following platforms and .NET versions: | OS | Version | Architectures | .NET | |:-------------------- |:-------- |:------------- |:--------------- | -| Windows 11 | 22H2 | x64 | 7.0.5 / 6.0.16 | -| Windows Server | 2022 | x64 | 7.0.5 / 6.0.16 | +| Windows 11 | 22H2 | x64 | 7.0.7 / 6.0.18 | +| Windows Server | 2022 | x64 | 7.0.7 / 6.0.18 | | | | | | -| macOS | 11.7 | x64 | 7.0.5 / 6.0.16 | -| | 12.6 | x64 | 7.0.5 / 6.0.16 | -| | 13.3 | x64 | 7.0.5 / 6.0.16 | +| macOS | 11.7 | x64 | 7.0.7 / 6.0.18 | +| | 12.6 | x64 | 7.0.7 / 6.0.18 | +| | 13.4 | x64 | 7.0.7 / 6.0.18 | | | | | | -| Alpine | 3.17 | x64 | 7.0.4 | -| CentOS | 7.9.2009 | x64 | 7.0.5 / 6.0.16 | -| Debian | 10.13 | x64 | 7.0.5 / 6.0.16 | -| | 11.7 | x64 | 7.0.5 / 6.0.16 | -| Fedora | 37 | x64 | 7.0.5 / 6.0.16 | -| | 38 | x64 | 7.0.5 / 6.0.16 | -| Ubuntu | 16.04 | x64 | 7.0.5 / 6.0.16 | -| | 18.04 | x64 | 7.0.5 / 6.0.16 | -| | 20.04 | x64 | 7.0.5 / 6.0.16 | -| | 22.04 | x64 | 7.0.5 / 6.0.16 | +| Alpine | 3.17 | x64 | 7.0.7 | +| | 3.18 | x64 | 7.0.7 | +| CentOS | 7 | x64 | 7.0.7 / 6.0.18 | +| Debian | 10 | x64 | 7.0.7 / 6.0.18 | +| | 11 | x64 | 7.0.7 / 6.0.18 | +| | 12 | x64 | 7.0.7 / 6.0.18 | +| Fedora | 37 | x64 | 7.0.7 / 6.0.18 | +| | 38 | x64 | 7.0.7 / 6.0.18 | +| Ubuntu | 16.04 | x64 | 7.0.7 / 6.0.18 | +| | 18.04 | x64 | 7.0.7 / 6.0.18 | +| | 20.04 | x64 | 7.0.7 / 6.0.18 | +| | 22.04 | x64 | 7.0.7 / 6.0.18 | Other, similar platforms supported by .NET should work as well but have not been tested. From fdec983a2cf7aabab7b63e6474a6e35dd259d6d6 Mon Sep 17 00:00:00 2001 From: ektrah Date: Sat, 17 Jun 2023 11:20:17 +0200 Subject: [PATCH 07/26] Update package references --- tests/Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Tests.csproj b/tests/Tests.csproj index 0f36c842..f7df2ff7 100644 --- a/tests/Tests.csproj +++ b/tests/Tests.csproj @@ -15,7 +15,7 @@ - + all From 6c47d0cf47b6a7a5e6b68bc35a8c08afa86a3ae7 Mon Sep 17 00:00:00 2001 From: ektrah Date: Sat, 17 Jun 2023 11:27:29 +0200 Subject: [PATCH 08/26] 23.6.0-preview.2 --- README.md | 2 +- build/Package.props | 2 +- docs/install.md | 10 +++++----- index.md | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 53fb1eac..cdc185a8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Maintenance](https://img.shields.io/maintenance/yes/2023)](https://github.com/ektrah/nsec) [![License](https://img.shields.io/github/license/ektrah/nsec)](https://nsec.rocks/license) -[![NuGet](https://img.shields.io/nuget/vpre/NSec.Cryptography)](https://www.nuget.org/packages/NSec.Cryptography/23.5.0-preview.1) +[![NuGet](https://img.shields.io/nuget/vpre/NSec.Cryptography)](https://www.nuget.org/packages/NSec.Cryptography/23.6.0-preview.2) [NSec](https://nsec.rocks/) is a cryptographic library for [.NET 6+](https://dotnet.microsoft.com/) based on diff --git a/build/Package.props b/build/Package.props index ca6179cc..e77861b1 100644 --- a/build/Package.props +++ b/build/Package.props @@ -6,7 +6,7 @@ - + 23.6.0-preview.2 $([System.DateTime]::UtcNow.ToString(y.M.0))-preview.2-$(BuildNumberMajor)-$(BuildNumberMinor) diff --git a/docs/install.md b/docs/install.md index b9225b25..48f1016b 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,22 +1,22 @@ # Installation NSec is available as -[a NuGet package from nuget.org](https://www.nuget.org/packages/NSec.Cryptography/23.5.0-preview.1). +[a NuGet package from nuget.org](https://www.nuget.org/packages/NSec.Cryptography/23.6.0-preview.2). It can be added to a project in a number of ways, depending on the project type and tools used: #### dotnet CLI - $ dotnet add package NSec.Cryptography --version 23.5.0-preview.1 + $ dotnet add package NSec.Cryptography --version 23.6.0-preview.2 #### Visual Studio - PM> Install-Package NSec.Cryptography -Version 23.5.0-preview.1 + PM> Install-Package NSec.Cryptography -Version 23.6.0-preview.2 #### .csproj - + ## Supported Platforms @@ -35,7 +35,7 @@ on the following platforms: | **`android-`** | | | | | Specifically, -[NSec 23.5.0-preview.1](https://www.nuget.org/packages/NSec.Cryptography/23.5.0-preview.1) +[NSec 23.6.0-preview.2](https://www.nuget.org/packages/NSec.Cryptography/23.6.0-preview.2) has been tested to run on the following platforms and .NET versions: | OS | Version | Architectures | .NET | diff --git a/index.md b/index.md index 60116da0..f2908847 100644 --- a/index.md +++ b/index.md @@ -42,7 +42,7 @@ and verify the signature: ## Installation - $ dotnet add package NSec.Cryptography --version 23.5.0-preview.1 + $ dotnet add package NSec.Cryptography --version 23.6.0-preview.2 NSec works with .NET 6 and later on Windows, Linux and macOS. From a14a6c159234a03e404af1c56b1757257ef05eeb Mon Sep 17 00:00:00 2001 From: ektrah Date: Sat, 17 Jun 2023 11:47:07 +0200 Subject: [PATCH 09/26] Bump version [ci skip] --- build/Package.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/Package.props b/build/Package.props index e77861b1..c7b10e03 100644 --- a/build/Package.props +++ b/build/Package.props @@ -6,8 +6,8 @@ - 23.6.0-preview.2 - $([System.DateTime]::UtcNow.ToString(y.M.0))-preview.2-$(BuildNumberMajor)-$(BuildNumberMinor) + + $([System.DateTime]::UtcNow.ToString(y.M.0))-preview.3-$(BuildNumberMajor)-$(BuildNumberMinor) From 0eb608dd6908f4ed5ba6446a3f6b7ecb04e799a2 Mon Sep 17 00:00:00 2001 From: ektrah Date: Tue, 19 Sep 2023 19:44:40 +0200 Subject: [PATCH 10/26] Improve installation instructions --- docs/install.md | 57 +++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/docs/install.md b/docs/install.md index 48f1016b..b65beecf 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,27 +1,15 @@ # Installation -NSec is available as -[a NuGet package from nuget.org](https://www.nuget.org/packages/NSec.Cryptography/23.6.0-preview.2). -It can be added to a project in a number of ways, depending on the project type -and tools used: - - -#### dotnet CLI +Use the following command to install the +[NSec.Cryptography NuGet package](https://www.nuget.org/packages/NSec.Cryptography/23.6.0-preview.2): $ dotnet add package NSec.Cryptography --version 23.6.0-preview.2 -#### Visual Studio - - PM> Install-Package NSec.Cryptography -Version 23.6.0-preview.2 - -#### .csproj - - - ## Supported Platforms -NSec is intended to run on +[NSec 23.6.0-preview.2](https://www.nuget.org/packages/NSec.Cryptography/23.6.0-preview.2) +is intended to run on all [supported versions of .NET](https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core) on the following platforms: @@ -34,7 +22,21 @@ on the following platforms: | **`ios-`** | | | | | | **`android-`** | | | | | -Specifically, + +Please note: + +1. For Windows, the + [Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017, 2019, and 2022](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads) + is required. This is part of the .NET SDK but might not be present on a + clean Windows installation. + +2. The AES-GCM implementation in NSec is hardware accelerated and may not be + available on all architectures. Support can be determined at runtime using + the static `IsSupported` property of the `NSec.Cryptography.Aes256Gcm` class. + + +## Tested Platforms + [NSec 23.6.0-preview.2](https://www.nuget.org/packages/NSec.Cryptography/23.6.0-preview.2) has been tested to run on the following platforms and .NET versions: @@ -60,14 +62,17 @@ has been tested to run on the following platforms and .NET versions: | | 20.04 | x64 | 7.0.7 / 6.0.18 | | | 22.04 | x64 | 7.0.7 / 6.0.18 | -Other, similar platforms supported by .NET should work as well but have not been tested. +The other supported platforms should work as well, but haven't been tested. + + +## Frequently Asked Questions -Using NSec on Windows requires the -[Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017, 2019, and 2022](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads). -This dependency is included in the .NET SDK but might -not be present, for example, when deploying a self-contained application. +Below are some frequently asked questions: -The implementation of AES-GCM in NSec is hardware-accelerated and requires an -x64 processor with the AES-NI extension. The availability of this extension can -be determined at runtime using the static `IsSupported` property of the -`NSec.Cryptography.Aes256Gcm` class. +**Q**: What could cause a *System.DllNotFoundException: Unable to load shared +library 'libsodium' or one of its dependencies.* when using the +NSec.Cryptography NuGet package? +**A**: This exception can occur if the operating system or architecture is not +supported, or if the Visual C++ Redistributable has not been installed on a +Windows system. Please refer to the [Supported Platforms](#supported-platforms) +section above. From 487bc4fd8aad4253aca6dad65d2c33aac3070d1f Mon Sep 17 00:00:00 2001 From: ektrah Date: Tue, 19 Sep 2023 19:44:52 +0200 Subject: [PATCH 11/26] Update libsodium to 1.0.19 --- src/Cryptography/NSec.Cryptography.csproj | 2 +- src/Interop/Interop.Version.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Cryptography/NSec.Cryptography.csproj b/src/Cryptography/NSec.Cryptography.csproj index d2df9070..bcb16f9e 100644 --- a/src/Cryptography/NSec.Cryptography.csproj +++ b/src/Cryptography/NSec.Cryptography.csproj @@ -18,7 +18,7 @@ NSec.Cryptography.X25519 - + diff --git a/src/Interop/Interop.Version.cs b/src/Interop/Interop.Version.cs index 143f420e..2bc5c7d3 100644 --- a/src/Interop/Interop.Version.cs +++ b/src/Interop/Interop.Version.cs @@ -5,9 +5,9 @@ internal static partial class Interop { internal static partial class Libsodium { - internal const int SODIUM_LIBRARY_VERSION_MAJOR = 10; - internal const int SODIUM_LIBRARY_VERSION_MINOR = 3; - internal const string SODIUM_VERSION_STRING = "1.0.18"; + internal const int SODIUM_LIBRARY_VERSION_MAJOR = 26; + internal const int SODIUM_LIBRARY_VERSION_MINOR = 1; + internal const string SODIUM_VERSION_STRING = "1.0.19"; [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] internal static extern int sodium_library_version_major(); From d5757b545c6ab74a5d286af04fc70b4bb5ecf923 Mon Sep 17 00:00:00 2001 From: ektrah Date: Tue, 19 Sep 2023 20:18:57 +0200 Subject: [PATCH 12/26] AEGIS-128L and AEGIS-256 :sparkles: --- src/Cryptography/AeadAlgorithm.cs | 32 +++- src/Cryptography/Aegis128L.cs | 179 ++++++++++++++++++ src/Cryptography/Aegis256.cs | 179 ++++++++++++++++++ .../PasswordBased/PasswordBasedKeyExporter.cs | 20 ++ src/Interop/Interop.Aead.Aegis128L.cs | 49 +++++ src/Interop/Interop.Aead.Aegis256.cs | 49 +++++ src/Interop/Interop.projitems | 2 + src/Interop/Interop.yaml | 30 +++ tests/Algorithms/Aegis128LTests.cs | 50 +++++ tests/Algorithms/Aegis256Tests.cs | 50 +++++ tests/Base/AeadAlgorithmTests.cs | 2 +- tests/Formatting/NSecTests.cs | 2 + tests/Registry.cs | 8 + 13 files changed, 650 insertions(+), 2 deletions(-) create mode 100644 src/Cryptography/Aegis128L.cs create mode 100644 src/Cryptography/Aegis256.cs create mode 100644 src/Interop/Interop.Aead.Aegis128L.cs create mode 100644 src/Interop/Interop.Aead.Aegis256.cs create mode 100644 tests/Algorithms/Aegis128LTests.cs create mode 100644 tests/Algorithms/Aegis256Tests.cs diff --git a/src/Cryptography/AeadAlgorithm.cs b/src/Cryptography/AeadAlgorithm.cs index 03fcde95..b4eccde3 100644 --- a/src/Cryptography/AeadAlgorithm.cs +++ b/src/Cryptography/AeadAlgorithm.cs @@ -23,6 +23,8 @@ namespace NSec.Cryptography // public abstract class AeadAlgorithm : Algorithm { + private static Aegis128L? s_Aegis128L; + private static Aegis256? s_Aegis256; private static Aes256Gcm? s_Aes256Gcm; private static ChaCha20Poly1305? s_ChaCha20Poly1305; private static XChaCha20Poly1305? s_XChaCha20Poly1305; @@ -37,7 +39,7 @@ private protected AeadAlgorithm( int tagSize) { Debug.Assert(keySize > 0); - Debug.Assert(nonceSize >= 0 && nonceSize <= 24); + Debug.Assert(nonceSize >= 0 && nonceSize <= 32); Debug.Assert(tagSize >= 0 && tagSize <= 255); _keySize = keySize; @@ -45,6 +47,34 @@ private protected AeadAlgorithm( _tagSize = tagSize; } + public static Aegis128L Aegis128L + { + get + { + Aegis128L? instance = s_Aegis128L; + if (instance == null) + { + Interlocked.CompareExchange(ref s_Aegis128L, new Aegis128L(), null); + instance = s_Aegis128L; + } + return instance; + } + } + + public static Aegis256 Aegis256 + { + get + { + Aegis256? instance = s_Aegis256; + if (instance == null) + { + Interlocked.CompareExchange(ref s_Aegis256, new Aegis256(), null); + instance = s_Aegis256; + } + return instance; + } + } + public static Aes256Gcm Aes256Gcm { get diff --git a/src/Cryptography/Aegis128L.cs b/src/Cryptography/Aegis128L.cs new file mode 100644 index 00000000..fbeeed96 --- /dev/null +++ b/src/Cryptography/Aegis128L.cs @@ -0,0 +1,179 @@ +using System; +using System.Diagnostics; +using System.Threading; +using NSec.Cryptography.Formatting; +using static Interop.Libsodium; + +namespace NSec.Cryptography +{ + // + // AEGIS-128L + // + // The AEGIS-128L authenticated encryption with associated data (AEAD) + // algorithm + // + // References: + // + // draft-irtf-cfrg-aegis-aead-04 - The AEGIS Family of Authenticated + // Encryption Algorithms + // + // RFC 5116 - An Interface and Algorithms for Authenticated Encryption + // + // Parameters: + // + // Key Size - 16 bytes. + // + // Nonce Size - 16 bytes. + // + // Tag Size - 32 bytes. + // + // Plaintext Size - Between 0 and 2^61-1 bytes. (A Span can hold + // only up to 2^31-1 bytes.) + // + // Associated Data Size - Between 0 and 2^64-1 bytes. + // + // Ciphertext Size - The ciphertext always has the size of the + // plaintext plus the tag size. + // + public sealed class Aegis128L : AeadAlgorithm + { + private const uint NSecBlobHeader = 0xDE614ADE; + + private static int s_selfTest; + + public Aegis128L() : base( + keySize: crypto_aead_aegis128l_KEYBYTES, + nonceSize: crypto_aead_aegis128l_NPUBBYTES, + tagSize: crypto_aead_aegis128l_ABYTES) + { + if (s_selfTest == 0) + { + SelfTest(); + Interlocked.Exchange(ref s_selfTest, 1); + } + } + + internal override void CreateKey( + ReadOnlySpan seed, + out SecureMemoryHandle keyHandle, + out PublicKey? publicKey) + { + Debug.Assert(seed.Length == crypto_aead_aegis128l_KEYBYTES); + + publicKey = null; + keyHandle = SecureMemoryHandle.CreateFrom(seed); + } + + private protected unsafe override void EncryptCore( + SecureMemoryHandle keyHandle, + ReadOnlySpan nonce, + ReadOnlySpan associatedData, + ReadOnlySpan plaintext, + Span ciphertext) + { + Debug.Assert(keyHandle.Size == crypto_aead_aegis128l_KEYBYTES); + Debug.Assert(nonce.Length == crypto_aead_aegis128l_NPUBBYTES); + Debug.Assert(ciphertext.Length == plaintext.Length + crypto_aead_aegis128l_ABYTES); + + fixed (byte* c = ciphertext) + fixed (byte* m = plaintext) + fixed (byte* ad = associatedData) + fixed (byte* n = nonce) + { + int error = crypto_aead_aegis128l_encrypt( + c, + out ulong clen_p, + m, + (ulong)plaintext.Length, + ad, + (ulong)associatedData.Length, + null, + n, + keyHandle); + + Debug.Assert(error == 0); + Debug.Assert((ulong)ciphertext.Length == clen_p); + } + } + + internal override int GetSeedSize() + { + return crypto_aead_aegis128l_KEYBYTES; + } + + private protected unsafe override bool DecryptCore( + SecureMemoryHandle keyHandle, + ReadOnlySpan nonce, + ReadOnlySpan associatedData, + ReadOnlySpan ciphertext, + Span plaintext) + { + Debug.Assert(keyHandle.Size == crypto_aead_aegis128l_KEYBYTES); + Debug.Assert(nonce.Length == crypto_aead_aegis128l_NPUBBYTES); + Debug.Assert(plaintext.Length == ciphertext.Length - crypto_aead_aegis128l_ABYTES); + + fixed (byte* m = plaintext) + fixed (byte* c = ciphertext) + fixed (byte* ad = associatedData) + fixed (byte* n = nonce) + { + int error = crypto_aead_aegis128l_decrypt( + m, + out ulong mlen_p, + null, + c, + (ulong)ciphertext.Length, + ad, + (ulong)associatedData.Length, + n, + keyHandle); + + // libsodium clears plaintext if decryption fails + + Debug.Assert(error != 0 || (ulong)plaintext.Length == mlen_p); + return error == 0; + } + } + + internal override bool TryExportKey( + SecureMemoryHandle keyHandle, + KeyBlobFormat format, + Span blob, + out int blobSize) + { + return format switch + { + KeyBlobFormat.RawSymmetricKey => RawKeyFormatter.TryExport(keyHandle, blob, out blobSize), + KeyBlobFormat.NSecSymmetricKey => NSecKeyFormatter.TryExport(NSecBlobHeader, crypto_aead_aegis128l_KEYBYTES, crypto_aead_aegis128l_ABYTES, keyHandle, blob, out blobSize), + _ => throw Error.Argument_FormatNotSupported(nameof(format), format.ToString()), + }; + } + + internal override bool TryImportKey( + ReadOnlySpan blob, + KeyBlobFormat format, + out SecureMemoryHandle? keyHandle, + out PublicKey? publicKey) + { + publicKey = null; + + return format switch + { + KeyBlobFormat.RawSymmetricKey => RawKeyFormatter.TryImport(crypto_aead_aegis128l_KEYBYTES, blob, out keyHandle), + KeyBlobFormat.NSecSymmetricKey => NSecKeyFormatter.TryImport(NSecBlobHeader, crypto_aead_aegis128l_KEYBYTES, crypto_aead_aegis128l_ABYTES, blob, out keyHandle), + _ => throw Error.Argument_FormatNotSupported(nameof(format), format.ToString()), + }; + } + + private static void SelfTest() + { + if ((crypto_aead_aegis128l_abytes() != crypto_aead_aegis128l_ABYTES) || + (crypto_aead_aegis128l_keybytes() != crypto_aead_aegis128l_KEYBYTES) || + (crypto_aead_aegis128l_npubbytes() != crypto_aead_aegis128l_NPUBBYTES) || + (crypto_aead_aegis128l_nsecbytes() != crypto_aead_aegis128l_NSECBYTES)) + { + throw Error.InvalidOperation_InitializationFailed(); + } + } + } +} diff --git a/src/Cryptography/Aegis256.cs b/src/Cryptography/Aegis256.cs new file mode 100644 index 00000000..1a8acfa8 --- /dev/null +++ b/src/Cryptography/Aegis256.cs @@ -0,0 +1,179 @@ +using System; +using System.Diagnostics; +using System.Threading; +using NSec.Cryptography.Formatting; +using static Interop.Libsodium; + +namespace NSec.Cryptography +{ + // + // AEGIS-256 + // + // The AEGIS-256 authenticated encryption with associated data (AEAD) + // algorithm + // + // References: + // + // draft-irtf-cfrg-aegis-aead-04 - The AEGIS Family of Authenticated + // Encryption Algorithms + // + // RFC 5116 - An Interface and Algorithms for Authenticated Encryption + // + // Parameters: + // + // Key Size - 32 bytes. + // + // Nonce Size - 32 bytes. + // + // Tag Size - 32 bytes. + // + // Plaintext Size - Between 0 and 2^61-1 bytes. (A Span can hold + // only up to 2^31-1 bytes.) + // + // Associated Data Size - Between 0 and 2^64-1 bytes. + // + // Ciphertext Size - The ciphertext always has the size of the + // plaintext plus the tag size. + // + public sealed class Aegis256 : AeadAlgorithm + { + private const uint NSecBlobHeader = 0xDE614BDE; + + private static int s_selfTest; + + public Aegis256() : base( + keySize: crypto_aead_aegis256_KEYBYTES, + nonceSize: crypto_aead_aegis256_NPUBBYTES, + tagSize: crypto_aead_aegis256_ABYTES) + { + if (s_selfTest == 0) + { + SelfTest(); + Interlocked.Exchange(ref s_selfTest, 1); + } + } + + internal override void CreateKey( + ReadOnlySpan seed, + out SecureMemoryHandle keyHandle, + out PublicKey? publicKey) + { + Debug.Assert(seed.Length == crypto_aead_aegis256_KEYBYTES); + + publicKey = null; + keyHandle = SecureMemoryHandle.CreateFrom(seed); + } + + private protected unsafe override void EncryptCore( + SecureMemoryHandle keyHandle, + ReadOnlySpan nonce, + ReadOnlySpan associatedData, + ReadOnlySpan plaintext, + Span ciphertext) + { + Debug.Assert(keyHandle.Size == crypto_aead_aegis256_KEYBYTES); + Debug.Assert(nonce.Length == crypto_aead_aegis256_NPUBBYTES); + Debug.Assert(ciphertext.Length == plaintext.Length + crypto_aead_aegis256_ABYTES); + + fixed (byte* c = ciphertext) + fixed (byte* m = plaintext) + fixed (byte* ad = associatedData) + fixed (byte* n = nonce) + { + int error = crypto_aead_aegis256_encrypt( + c, + out ulong clen_p, + m, + (ulong)plaintext.Length, + ad, + (ulong)associatedData.Length, + null, + n, + keyHandle); + + Debug.Assert(error == 0); + Debug.Assert((ulong)ciphertext.Length == clen_p); + } + } + + internal override int GetSeedSize() + { + return crypto_aead_aegis256_KEYBYTES; + } + + private protected unsafe override bool DecryptCore( + SecureMemoryHandle keyHandle, + ReadOnlySpan nonce, + ReadOnlySpan associatedData, + ReadOnlySpan ciphertext, + Span plaintext) + { + Debug.Assert(keyHandle.Size == crypto_aead_aegis256_KEYBYTES); + Debug.Assert(nonce.Length == crypto_aead_aegis256_NPUBBYTES); + Debug.Assert(plaintext.Length == ciphertext.Length - crypto_aead_aegis256_ABYTES); + + fixed (byte* m = plaintext) + fixed (byte* c = ciphertext) + fixed (byte* ad = associatedData) + fixed (byte* n = nonce) + { + int error = crypto_aead_aegis256_decrypt( + m, + out ulong mlen_p, + null, + c, + (ulong)ciphertext.Length, + ad, + (ulong)associatedData.Length, + n, + keyHandle); + + // libsodium clears plaintext if decryption fails + + Debug.Assert(error != 0 || (ulong)plaintext.Length == mlen_p); + return error == 0; + } + } + + internal override bool TryExportKey( + SecureMemoryHandle keyHandle, + KeyBlobFormat format, + Span blob, + out int blobSize) + { + return format switch + { + KeyBlobFormat.RawSymmetricKey => RawKeyFormatter.TryExport(keyHandle, blob, out blobSize), + KeyBlobFormat.NSecSymmetricKey => NSecKeyFormatter.TryExport(NSecBlobHeader, crypto_aead_aegis256_KEYBYTES, crypto_aead_aegis256_ABYTES, keyHandle, blob, out blobSize), + _ => throw Error.Argument_FormatNotSupported(nameof(format), format.ToString()), + }; + } + + internal override bool TryImportKey( + ReadOnlySpan blob, + KeyBlobFormat format, + out SecureMemoryHandle? keyHandle, + out PublicKey? publicKey) + { + publicKey = null; + + return format switch + { + KeyBlobFormat.RawSymmetricKey => RawKeyFormatter.TryImport(crypto_aead_aegis256_KEYBYTES, blob, out keyHandle), + KeyBlobFormat.NSecSymmetricKey => NSecKeyFormatter.TryImport(NSecBlobHeader, crypto_aead_aegis256_KEYBYTES, crypto_aead_aegis256_ABYTES, blob, out keyHandle), + _ => throw Error.Argument_FormatNotSupported(nameof(format), format.ToString()), + }; + } + + private static void SelfTest() + { + if ((crypto_aead_aegis256_abytes() != crypto_aead_aegis256_ABYTES) || + (crypto_aead_aegis256_keybytes() != crypto_aead_aegis256_KEYBYTES) || + (crypto_aead_aegis256_npubbytes() != crypto_aead_aegis256_NPUBBYTES) || + (crypto_aead_aegis256_nsecbytes() != crypto_aead_aegis256_NSECBYTES)) + { + throw Error.InvalidOperation_InitializationFailed(); + } + } + } +} diff --git a/src/Experimental/PasswordBased/PasswordBasedKeyExporter.cs b/src/Experimental/PasswordBased/PasswordBasedKeyExporter.cs index 40f964e2..520a9d49 100644 --- a/src/Experimental/PasswordBased/PasswordBasedKeyExporter.cs +++ b/src/Experimental/PasswordBased/PasswordBasedKeyExporter.cs @@ -189,6 +189,16 @@ private static void ReadEncryptionParameters( Read(ref reader, out nonce); break; + case Aegis128L _: + Read(ref reader, 0x2004); + Read(ref reader, out nonce); + break; + + case Aegis256 _: + Read(ref reader, 0x2005); + Read(ref reader, out nonce); + break; + default: throw new NotSupportedException(); } @@ -275,6 +285,16 @@ private static void WriteEncryptionParameters( Write(ref writer, nonce); break; + case Aegis128L _: + Write(ref writer, 0x2004); + Write(ref writer, nonce); + break; + + case Aegis256 _: + Write(ref writer, 0x2005); + Write(ref writer, nonce); + break; + default: throw new NotSupportedException(); } diff --git a/src/Interop/Interop.Aead.Aegis128L.cs b/src/Interop/Interop.Aead.Aegis128L.cs new file mode 100644 index 00000000..f19e9b33 --- /dev/null +++ b/src/Interop/Interop.Aead.Aegis128L.cs @@ -0,0 +1,49 @@ +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Libsodium + { + internal const int crypto_aead_aegis128l_ABYTES = 32; + internal const int crypto_aead_aegis128l_KEYBYTES = 16; + internal const int crypto_aead_aegis128l_NPUBBYTES = 16; + internal const int crypto_aead_aegis128l_NSECBYTES = 0; + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static extern nuint crypto_aead_aegis128l_abytes(); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static unsafe extern int crypto_aead_aegis128l_decrypt( + byte* m, + out ulong mlen_p, + byte* nsec, + byte* c, + ulong clen, + byte* ad, + ulong adlen, + byte* npub, + SecureMemoryHandle k); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static unsafe extern int crypto_aead_aegis128l_encrypt( + byte* c, + out ulong clen_p, + byte* m, + ulong mlen, + byte* ad, + ulong adlen, + byte* nsec, + byte* npub, + SecureMemoryHandle k); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static extern nuint crypto_aead_aegis128l_keybytes(); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static extern nuint crypto_aead_aegis128l_npubbytes(); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static extern nuint crypto_aead_aegis128l_nsecbytes(); + } +} diff --git a/src/Interop/Interop.Aead.Aegis256.cs b/src/Interop/Interop.Aead.Aegis256.cs new file mode 100644 index 00000000..4b22d0bb --- /dev/null +++ b/src/Interop/Interop.Aead.Aegis256.cs @@ -0,0 +1,49 @@ +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Libsodium + { + internal const int crypto_aead_aegis256_ABYTES = 32; + internal const int crypto_aead_aegis256_KEYBYTES = 32; + internal const int crypto_aead_aegis256_NPUBBYTES = 32; + internal const int crypto_aead_aegis256_NSECBYTES = 0; + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static extern nuint crypto_aead_aegis256_abytes(); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static unsafe extern int crypto_aead_aegis256_decrypt( + byte* m, + out ulong mlen_p, + byte* nsec, + byte* c, + ulong clen, + byte* ad, + ulong adlen, + byte* npub, + SecureMemoryHandle k); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static unsafe extern int crypto_aead_aegis256_encrypt( + byte* c, + out ulong clen_p, + byte* m, + ulong mlen, + byte* ad, + ulong adlen, + byte* nsec, + byte* npub, + SecureMemoryHandle k); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static extern nuint crypto_aead_aegis256_keybytes(); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static extern nuint crypto_aead_aegis256_npubbytes(); + + [DllImport(Libraries.Libsodium, CallingConvention = CallingConvention.Cdecl)] + internal static extern nuint crypto_aead_aegis256_nsecbytes(); + } +} diff --git a/src/Interop/Interop.projitems b/src/Interop/Interop.projitems index 20eb0f2f..dad831ee 100644 --- a/src/Interop/Interop.projitems +++ b/src/Interop/Interop.projitems @@ -5,6 +5,8 @@ true + + diff --git a/src/Interop/Interop.yaml b/src/Interop/Interop.yaml index 3c15e981..fd4f6148 100644 --- a/src/Interop/Interop.yaml +++ b/src/Interop/Interop.yaml @@ -6,6 +6,36 @@ Interop.Core.cs: - sodium_init - sodium_set_misuse_handler +Interop.Aead.Aegis128L.cs: + include: include/sodium/crypto_aead_aegis128l.h + constants: + - crypto_aead_aegis128l_ABYTES + - crypto_aead_aegis128l_KEYBYTES + - crypto_aead_aegis128l_NPUBBYTES + - crypto_aead_aegis128l_NSECBYTES + functions: + - crypto_aead_aegis128l_abytes + - crypto_aead_aegis128l_decrypt (out ulong mlen_p, SecureMemoryHandle k) + - crypto_aead_aegis128l_encrypt (out ulong clen_p, SecureMemoryHandle k) + - crypto_aead_aegis128l_keybytes + - crypto_aead_aegis128l_npubbytes + - crypto_aead_aegis128l_nsecbytes + +Interop.Aead.Aegis256.cs: + include: include/sodium/crypto_aead_aegis256.h + constants: + - crypto_aead_aegis256_ABYTES + - crypto_aead_aegis256_KEYBYTES + - crypto_aead_aegis256_NPUBBYTES + - crypto_aead_aegis256_NSECBYTES + functions: + - crypto_aead_aegis256_abytes + - crypto_aead_aegis256_decrypt (out ulong mlen_p, SecureMemoryHandle k) + - crypto_aead_aegis256_encrypt (out ulong clen_p, SecureMemoryHandle k) + - crypto_aead_aegis256_keybytes + - crypto_aead_aegis256_npubbytes + - crypto_aead_aegis256_nsecbytes + Interop.Aead.Aes256Gcm.cs: include: include/sodium/crypto_aead_aes256gcm.h constants: diff --git a/tests/Algorithms/Aegis128LTests.cs b/tests/Algorithms/Aegis128LTests.cs new file mode 100644 index 00000000..f199c06e --- /dev/null +++ b/tests/Algorithms/Aegis128LTests.cs @@ -0,0 +1,50 @@ +using System; +using NSec.Cryptography; +using Xunit; + +namespace NSec.Tests.Algorithms +{ + public static class Aegis128LTests + { + public static readonly TheoryData PlaintextLengths = Utilities.Primes; + + #region Properties + + [Fact] + public static void Properties() + { + var a = AeadAlgorithm.Aegis128L; + + Assert.Equal(16, a.KeySize); + Assert.Equal(16, a.NonceSize); + Assert.Equal(32, a.TagSize); + } + + #endregion + + #region Encrypt/Decrypt + + [Theory] + [MemberData(nameof(PlaintextLengths))] + public static void EncryptDecrypt(int length) + { + var a = AeadAlgorithm.Aegis128L; + + using var k = new Key(a); + var n = Utilities.RandomBytes.Slice(0, a.NonceSize); + var ad = Utilities.RandomBytes.Slice(0, 100); + + var expected = Utilities.RandomBytes.Slice(0, length).ToArray(); + + var ciphertext = a.Encrypt(k, n, ad, expected); + Assert.NotNull(ciphertext); + Assert.Equal(length + a.TagSize, ciphertext.Length); + + var actual = a.Decrypt(k, n, ad, ciphertext); + Assert.NotNull(actual); + Assert.Equal(expected, actual); + } + + #endregion + } +} diff --git a/tests/Algorithms/Aegis256Tests.cs b/tests/Algorithms/Aegis256Tests.cs new file mode 100644 index 00000000..1659fab8 --- /dev/null +++ b/tests/Algorithms/Aegis256Tests.cs @@ -0,0 +1,50 @@ +using System; +using NSec.Cryptography; +using Xunit; + +namespace NSec.Tests.Algorithms +{ + public static class Aegis256Tests + { + public static readonly TheoryData PlaintextLengths = Utilities.Primes; + + #region Properties + + [Fact] + public static void Properties() + { + var a = AeadAlgorithm.Aegis256; + + Assert.Equal(32, a.KeySize); + Assert.Equal(32, a.NonceSize); + Assert.Equal(32, a.TagSize); + } + + #endregion + + #region Encrypt/Decrypt + + [Theory] + [MemberData(nameof(PlaintextLengths))] + public static void EncryptDecrypt(int length) + { + var a = AeadAlgorithm.Aegis256; + + using var k = new Key(a); + var n = Utilities.RandomBytes.Slice(0, a.NonceSize); + var ad = Utilities.RandomBytes.Slice(0, 100); + + var expected = Utilities.RandomBytes.Slice(0, length).ToArray(); + + var ciphertext = a.Encrypt(k, n, ad, expected); + Assert.NotNull(ciphertext); + Assert.Equal(length + a.TagSize, ciphertext.Length); + + var actual = a.Decrypt(k, n, ad, ciphertext); + Assert.NotNull(actual); + Assert.Equal(expected, actual); + } + + #endregion + } +} diff --git a/tests/Base/AeadAlgorithmTests.cs b/tests/Base/AeadAlgorithmTests.cs index 7a724dda..7911cc17 100644 --- a/tests/Base/AeadAlgorithmTests.cs +++ b/tests/Base/AeadAlgorithmTests.cs @@ -17,7 +17,7 @@ public static class AeadAlgorithmTests public static void Properties(AeadAlgorithm a) { Assert.True(a.KeySize > 0); - Assert.InRange(a.NonceSize, 0, 24); + Assert.InRange(a.NonceSize, 0, 32); Assert.InRange(a.TagSize, 0, 255); } diff --git a/tests/Formatting/NSecTests.cs b/tests/Formatting/NSecTests.cs index 93b0c5bb..6d67a8f9 100644 --- a/tests/Formatting/NSecTests.cs +++ b/tests/Formatting/NSecTests.cs @@ -7,6 +7,8 @@ namespace NSec.Tests.Formatting public static class NSecTests { [Theory] + [InlineData(typeof(Aegis128L), new byte[] { 0xDE, 0x61, 0x4A, 0xDE })] + [InlineData(typeof(Aegis256), new byte[] { 0xDE, 0x61, 0x4B, 0xDE })] [InlineData(typeof(Aes256Gcm), new byte[] { 0xDE, 0x61, 0x44, 0xDE })] [InlineData(typeof(ChaCha20Poly1305), new byte[] { 0xDE, 0x61, 0x43, 0xDE })] public static void Aead(Type algorithmType, byte[] blobHeader) diff --git a/tests/Registry.cs b/tests/Registry.cs index f8ccd24a..8fe26fd5 100644 --- a/tests/Registry.cs +++ b/tests/Registry.cs @@ -12,6 +12,8 @@ internal static class Registry public static readonly TheoryData AeadAlgorithms = new() { + AeadAlgorithm.Aegis128L, + AeadAlgorithm.Aegis256, AeadAlgorithm.Aes256Gcm, AeadAlgorithm.ChaCha20Poly1305, AeadAlgorithm.XChaCha20Poly1305, @@ -101,6 +103,8 @@ internal static class Registry public static readonly TheoryData SymmetricAlgorithms = new() { + AeadAlgorithm.Aegis128L, + AeadAlgorithm.Aegis256, AeadAlgorithm.Aes256Gcm, AeadAlgorithm.ChaCha20Poly1305, AeadAlgorithm.XChaCha20Poly1305, @@ -167,6 +171,10 @@ internal static class Registry public static readonly TheoryData SymmetricKeyBlobFormats = new() { + { AeadAlgorithm.Aegis128L, KeyBlobFormat.RawSymmetricKey }, + { AeadAlgorithm.Aegis128L, KeyBlobFormat.NSecSymmetricKey }, + { AeadAlgorithm.Aegis256, KeyBlobFormat.RawSymmetricKey }, + { AeadAlgorithm.Aegis256, KeyBlobFormat.NSecSymmetricKey }, { AeadAlgorithm.Aes256Gcm, KeyBlobFormat.RawSymmetricKey }, { AeadAlgorithm.Aes256Gcm, KeyBlobFormat.NSecSymmetricKey }, { MacAlgorithm.Blake2b_128, KeyBlobFormat.RawSymmetricKey }, From 273342c152ee0cc159500a9d65ee0de8b32726ed Mon Sep 17 00:00:00 2001 From: ektrah Date: Sun, 24 Sep 2023 22:40:07 +0200 Subject: [PATCH 13/26] Update documentation --- docs/api/nsec.cryptography.aeadalgorithm.md | 23 ++++++++++--- docs/api/nsec.cryptography.algorithm.md | 3 ++ docs/api/nsec.cryptography.md | 2 ++ docs/install.md | 36 ++++++++++----------- index.md | 8 +++-- 5 files changed, 47 insertions(+), 25 deletions(-) diff --git a/docs/api/nsec.cryptography.aeadalgorithm.md b/docs/api/nsec.cryptography.aeadalgorithm.md index 479e31ab..0151d5d5 100644 --- a/docs/api/nsec.cryptography.aeadalgorithm.md +++ b/docs/api/nsec.cryptography.aeadalgorithm.md @@ -9,6 +9,8 @@ Represents an authenticated encryption with associated data (AEAD) algorithm. * [[Algorithm|Algorithm Class]] * **AeadAlgorithm** + * Aegis128L + * Aegis256 * Aes256Gcm * ChaCha20Poly1305 * XChaCha20Poly1305 @@ -20,6 +22,20 @@ Represents an authenticated encryption with associated data (AEAD) algorithm. ## Static Properties +### Aegis128L + +Gets the AEGIS-128L AEAD algorithm. + + public static Aegis128L Aegis128L { get; } + + +### Aegis256 + +Gets the AEGIS-256 AEAD algorithm. + + public static Aegis256 Aegis256 { get; } + + ### Aes256Gcm Gets the AES256-GCM AEAD algorithm. @@ -33,10 +49,9 @@ PlatformNotSupportedException #### Remarks -The implementation of AES-GCM in NSec is hardware-accelerated and requires an -x64 processor with the AES-NI extension. The availability of this extension can -be determined at runtime using the static `IsSupported` property of the -`NSec.Cryptography.Aes256Gcm` class. +The AES-GCM implementation in NSec is hardware-accelerated and may not be +available on all architectures. Support can be determined at runtime using +the static `IsSupported` property of the `NSec.Cryptography.Aes256Gcm` class. ### ChaCha20Poly1305 diff --git a/docs/api/nsec.cryptography.algorithm.md b/docs/api/nsec.cryptography.algorithm.md index e7e7eff4..96a076bc 100644 --- a/docs/api/nsec.cryptography.algorithm.md +++ b/docs/api/nsec.cryptography.algorithm.md @@ -11,8 +11,11 @@ This class has no public members. * **Algorithm** * [[AeadAlgorithm|AeadAlgorithm Class]] + * Aegis128L + * Aegis256 * Aes256Gcm * ChaCha20Poly1305 + * XChaCha20Poly1305 * [[HashAlgorithm|HashAlgorithm Class]] * Blake2b * Sha256 diff --git a/docs/api/nsec.cryptography.md b/docs/api/nsec.cryptography.md index 56451f2a..ef62c1e7 100644 --- a/docs/api/nsec.cryptography.md +++ b/docs/api/nsec.cryptography.md @@ -2,6 +2,8 @@ * [[Algorithm Class]] * [[AeadAlgorithm Class]] + * Aegis128L + * Aegis256 * Aes256Gcm Class * ChaCha20Poly1305 Class * XChaCha20Poly1305 Class diff --git a/docs/install.md b/docs/install.md index b65beecf..44efc8af 100644 --- a/docs/install.md +++ b/docs/install.md @@ -30,7 +30,7 @@ Please note: is required. This is part of the .NET SDK but might not be present on a clean Windows installation. -2. The AES-GCM implementation in NSec is hardware accelerated and may not be +2. The AES-GCM implementation in NSec is hardware-accelerated and may not be available on all architectures. Support can be determined at runtime using the static `IsSupported` property of the `NSec.Cryptography.Aes256Gcm` class. @@ -42,25 +42,25 @@ has been tested to run on the following platforms and .NET versions: | OS | Version | Architectures | .NET | |:-------------------- |:-------- |:------------- |:--------------- | -| Windows 11 | 22H2 | x64 | 7.0.7 / 6.0.18 | -| Windows Server | 2022 | x64 | 7.0.7 / 6.0.18 | +| Windows 11 | 22H2 | x64 | 7.0.11 / 6.0.22 | +| Windows Server | 2022 | x64 | 7.0.11 / 6.0.22 | | | | | | -| macOS | 11.7 | x64 | 7.0.7 / 6.0.18 | -| | 12.6 | x64 | 7.0.7 / 6.0.18 | -| | 13.4 | x64 | 7.0.7 / 6.0.18 | +| macOS | 11.7 | x64 | 7.0.11 / 6.0.22 | +| | 12.6 | x64 | 7.0.11 / 6.0.22 | +| | 13.4 | x64 | 7.0.11 / 6.0.22 | | | | | | -| Alpine | 3.17 | x64 | 7.0.7 | -| | 3.18 | x64 | 7.0.7 | -| CentOS | 7 | x64 | 7.0.7 / 6.0.18 | -| Debian | 10 | x64 | 7.0.7 / 6.0.18 | -| | 11 | x64 | 7.0.7 / 6.0.18 | -| | 12 | x64 | 7.0.7 / 6.0.18 | -| Fedora | 37 | x64 | 7.0.7 / 6.0.18 | -| | 38 | x64 | 7.0.7 / 6.0.18 | -| Ubuntu | 16.04 | x64 | 7.0.7 / 6.0.18 | -| | 18.04 | x64 | 7.0.7 / 6.0.18 | -| | 20.04 | x64 | 7.0.7 / 6.0.18 | -| | 22.04 | x64 | 7.0.7 / 6.0.18 | +| Alpine Linux | 3.17 | x64 | 7.0.10 | +| | 3.18 | x64 | 7.0.11 | +| CentOS Linux | 7 | x64 | 7.0.11 / 6.0.22 | +| Debian | 10 | x64 | 7.0.11 / 6.0.22 | +| | 11 | x64 | 7.0.11 / 6.0.22 | +| | 12 | x64 | 7.0.11 / 6.0.22 | +| Fedora | 37 | x64 | 7.0.11 / 6.0.22 | +| | 38 | x64 | 7.0.11 / 6.0.22 | +| Ubuntu | 16.04 | x64 | 7.0.11 / 6.0.22 | +| | 18.04 | x64 | 7.0.11 / 6.0.22 | +| | 20.04 | x64 | 7.0.11 / 6.0.22 | +| | 22.04 | x64 | 7.0.11 / 6.0.22 | The other supported platforms should work as well, but haven't been tested. diff --git a/index.md b/index.md index f2908847..e19d0876 100644 --- a/index.md +++ b/index.md @@ -53,16 +53,18 @@ See [[Installation]] for more details. | Class | Algorithms | |:----------------------------------------------- |:------------------------- | -| [[AeadAlgorithm Class]] | AES256-GCM | +| [[AeadAlgorithm Class]] | AEGIS-128L | +| | AEGIS-256 | +| | AES256-GCM | | | ChaCha20-Poly1305 | | | XChaCha20-Poly1305 | -| [[HashAlgorithm Class]] | BLAKE2b *-- unkeyed* | +| [[HashAlgorithm Class]] | BLAKE2b *(unkeyed)* | | | SHA-256 | | | SHA-512 | | [[KeyAgreementAlgorithm Class]] | X25519 | | [[KeyDerivationAlgorithm2 Class]] | HKDF-SHA-256 | | | HKDF-SHA-512 | -| [[MacAlgorithm Class]] | BLAKE2b *-- keyed* | +| [[MacAlgorithm Class]] | BLAKE2b *(keyed)* | | | HMAC-SHA-256 | | | HMAC-SHA-512 | | [[PasswordBasedKeyDerivationAlgorithm Class]] | Argon2id | From 15ed6c27976feac4802914b4e405ef05cb3df1ce Mon Sep 17 00:00:00 2001 From: ektrah Date: Sun, 24 Sep 2023 22:40:32 +0200 Subject: [PATCH 14/26] Update package references --- tests/Tests.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Tests.csproj b/tests/Tests.csproj index f7df2ff7..9bb980d0 100644 --- a/tests/Tests.csproj +++ b/tests/Tests.csproj @@ -15,9 +15,9 @@ - - - + + + all runtime; build; native; contentfiles; analyzers From 67a8da7d5cbff2622e57be99331dbb9244de27b4 Mon Sep 17 00:00:00 2001 From: ektrah Date: Sun, 24 Sep 2023 22:43:00 +0200 Subject: [PATCH 15/26] 23.9.0-preview.3 --- README.md | 2 +- build/Package.props | 2 +- docs/install.md | 8 ++++---- index.md | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index cdc185a8..f63a4455 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Maintenance](https://img.shields.io/maintenance/yes/2023)](https://github.com/ektrah/nsec) [![License](https://img.shields.io/github/license/ektrah/nsec)](https://nsec.rocks/license) -[![NuGet](https://img.shields.io/nuget/vpre/NSec.Cryptography)](https://www.nuget.org/packages/NSec.Cryptography/23.6.0-preview.2) +[![NuGet](https://img.shields.io/nuget/vpre/NSec.Cryptography)](https://www.nuget.org/packages/NSec.Cryptography/23.9.0-preview.3) [NSec](https://nsec.rocks/) is a cryptographic library for [.NET 6+](https://dotnet.microsoft.com/) based on diff --git a/build/Package.props b/build/Package.props index c7b10e03..452bc9ae 100644 --- a/build/Package.props +++ b/build/Package.props @@ -6,7 +6,7 @@ - + 23.9.0-preview.3 $([System.DateTime]::UtcNow.ToString(y.M.0))-preview.3-$(BuildNumberMajor)-$(BuildNumberMinor) diff --git a/docs/install.md b/docs/install.md index 44efc8af..1de9baed 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,14 +1,14 @@ # Installation Use the following command to install the -[NSec.Cryptography NuGet package](https://www.nuget.org/packages/NSec.Cryptography/23.6.0-preview.2): +[NSec.Cryptography NuGet package](https://www.nuget.org/packages/NSec.Cryptography/23.9.0-preview.3): - $ dotnet add package NSec.Cryptography --version 23.6.0-preview.2 + $ dotnet add package NSec.Cryptography --version 23.9.0-preview.3 ## Supported Platforms -[NSec 23.6.0-preview.2](https://www.nuget.org/packages/NSec.Cryptography/23.6.0-preview.2) +[NSec 23.9.0-preview.3](https://www.nuget.org/packages/NSec.Cryptography/23.9.0-preview.3) is intended to run on all [supported versions of .NET](https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core) on the following platforms: @@ -37,7 +37,7 @@ Please note: ## Tested Platforms -[NSec 23.6.0-preview.2](https://www.nuget.org/packages/NSec.Cryptography/23.6.0-preview.2) +[NSec 23.9.0-preview.3](https://www.nuget.org/packages/NSec.Cryptography/23.9.0-preview.3) has been tested to run on the following platforms and .NET versions: | OS | Version | Architectures | .NET | diff --git a/index.md b/index.md index e19d0876..68ffa47a 100644 --- a/index.md +++ b/index.md @@ -42,7 +42,7 @@ and verify the signature: ## Installation - $ dotnet add package NSec.Cryptography --version 23.6.0-preview.2 + $ dotnet add package NSec.Cryptography --version 23.9.0-preview.3 NSec works with .NET 6 and later on Windows, Linux and macOS. From 145f985d8495fa1d69ba3a35d8e1de3115ab671d Mon Sep 17 00:00:00 2001 From: ektrah Date: Sun, 24 Sep 2023 22:59:33 +0200 Subject: [PATCH 16/26] Bump version [ci skip] --- build/Package.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/Package.props b/build/Package.props index 452bc9ae..e1e50075 100644 --- a/build/Package.props +++ b/build/Package.props @@ -6,8 +6,8 @@ - 23.9.0-preview.3 - $([System.DateTime]::UtcNow.ToString(y.M.0))-preview.3-$(BuildNumberMajor)-$(BuildNumberMinor) + + $([System.DateTime]::UtcNow.ToString(y.M.0))-preview.4-$(BuildNumberMajor)-$(BuildNumberMinor) From 804c40368a456c1645a70288ba77403ca5912d01 Mon Sep 17 00:00:00 2001 From: ektrah Date: Sun, 24 Sep 2023 23:25:00 +0200 Subject: [PATCH 17/26] Update documentation [ci skip] --- docs/api/nsec.cryptography.aeadalgorithm.md | 10 ++++++---- docs/api/nsec.cryptography.md | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/api/nsec.cryptography.aeadalgorithm.md b/docs/api/nsec.cryptography.aeadalgorithm.md index 0151d5d5..40b79076 100644 --- a/docs/api/nsec.cryptography.aeadalgorithm.md +++ b/docs/api/nsec.cryptography.aeadalgorithm.md @@ -136,8 +136,9 @@ nonce catastrophic loss of security. : To prevent nonce reuse when encrypting multiple plaintexts with the same key, - it is recommended to increment the previous nonce; a randomly generated - nonce is not suitable. + it is recommended to increment the previous nonce. A randomly generated + nonce is unsafe unless the [[nonce size|AeadAlgorithm Class#NonceSize]] + is at least 24 bytes. associatedData : Optional additional data to be authenticated during decryption. @@ -199,8 +200,9 @@ nonce catastrophic loss of security. : To prevent nonce reuse when encrypting multiple plaintexts with the same key, - it is recommended to increment the previous nonce; a randomly generated - nonce is not suitable. + it is recommended to increment the previous nonce. A randomly generated + nonce is unsafe unless the [[nonce size|AeadAlgorithm Class#NonceSize]] + is at least 24 bytes. associatedData : Optional additional data to be authenticated during decryption. diff --git a/docs/api/nsec.cryptography.md b/docs/api/nsec.cryptography.md index ef62c1e7..7d30471f 100644 --- a/docs/api/nsec.cryptography.md +++ b/docs/api/nsec.cryptography.md @@ -2,8 +2,8 @@ * [[Algorithm Class]] * [[AeadAlgorithm Class]] - * Aegis128L - * Aegis256 + * Aegis128L Class + * Aegis256 Class * Aes256Gcm Class * ChaCha20Poly1305 Class * XChaCha20Poly1305 Class From ec95061568147d79084ceb1d5baf3249ecbe95d4 Mon Sep 17 00:00:00 2001 From: iRubens Date: Fri, 12 Apr 2024 10:43:14 +0200 Subject: [PATCH 18/26] added NET8 support --- Directory.Build.props | 2 +- src/Cryptography/NSec.Cryptography.csproj | 2 +- src/Experimental/NSec.Experimental.csproj | 2 +- tests/Tests.csproj | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 1639a01a..be877e77 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@  - 9.0 + 12.0 strict enable diff --git a/src/Cryptography/NSec.Cryptography.csproj b/src/Cryptography/NSec.Cryptography.csproj index bcb16f9e..7d7cb1ab 100644 --- a/src/Cryptography/NSec.Cryptography.csproj +++ b/src/Cryptography/NSec.Cryptography.csproj @@ -1,7 +1,7 @@  - net6.0 + net6.0;net8.0 diff --git a/src/Experimental/NSec.Experimental.csproj b/src/Experimental/NSec.Experimental.csproj index bdcfcea1..9a6aeacc 100644 --- a/src/Experimental/NSec.Experimental.csproj +++ b/src/Experimental/NSec.Experimental.csproj @@ -1,7 +1,7 @@  - net6.0 + net6.0;net8.0 diff --git a/tests/Tests.csproj b/tests/Tests.csproj index 9bb980d0..edc39290 100644 --- a/tests/Tests.csproj +++ b/tests/Tests.csproj @@ -1,7 +1,7 @@  - net7.0;net6.0 + net6.0;net7.0;net8.0 @@ -15,9 +15,9 @@ - - - + + + all runtime; build; native; contentfiles; analyzers From 1e903de7b3cdc16d21f21e8dc07531b77b68555b Mon Sep 17 00:00:00 2001 From: iRubens Date: Mon, 15 Apr 2024 17:02:30 +0200 Subject: [PATCH 19/26] updated GH actions for NET8 --- .github/workflows/test.yml | 40 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 19967553..14b2755f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,8 +16,13 @@ jobs: dotnet-version: | 6.0.x 7.0.x + 8.0.x - run: dotnet --info - uses: actions/checkout@v3 + - name: Test (.NET 8.0/Debug) + run: dotnet test tests -f net8.0 -c Debug + - name: Test (.NET 8.0/Release) + run: dotnet test tests -f net8.0 -c Release - name: Test (.NET 7.0/Debug) run: dotnet test tests -f net7.0 -c Debug - name: Test (.NET 7.0/Release) @@ -53,8 +58,13 @@ jobs: dotnet-version: | 6.0.x 7.0.x + 8.0.x - run: dotnet --info - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 + - name: Test (.NET 8.0/Debug) + run: dotnet test tests -f net8.0 -c Debug + - name: Test (.NET 8.0/Release) + run: dotnet test tests -f net8.0 -c Release - name: Test (.NET 7.0/Debug) run: dotnet test tests -f net7.0 -c Debug - name: Test (.NET 7.0/Release) @@ -105,8 +115,13 @@ jobs: dotnet-version: | 6.0.x 7.0.x + 8.0.x - run: dotnet --info - uses: actions/checkout@v3 + - name: Test (.NET 8.0/Debug) + run: dotnet test tests -f net8.0 -c Debug + - name: Test (.NET 8.0/Release) + run: dotnet test tests -f net8.0 -c Release - name: Test (.NET 7.0/Debug) run: dotnet test tests -f net7.0 -c Debug - name: Test (.NET 7.0/Release) @@ -116,7 +131,7 @@ jobs: - name: Test (.NET 6.0/Release) run: dotnet test tests -f net6.0 -c Release - test-linux-musl: + test-linux-musl7: strategy: fail-fast: false matrix: @@ -136,3 +151,24 @@ jobs: run: dotnet test tests -f net7.0 -c Debug - name: Test (.NET 7.0/Release) run: dotnet test tests -f net7.0 -c Release + + test-linux-musl8: + strategy: + fail-fast: false + matrix: + include: + - os: alpine3.18 + - os: alpine3.19 + runs-on: ubuntu-latest + container: + image: mcr.microsoft.com/dotnet/sdk:8.0-${{ matrix.os }} + env: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 + steps: + - run: dotnet --info + - uses: actions/checkout@v3 + - name: Test (.NET 8.0/Debug) + run: dotnet test tests -f net8.0 -c Debug + - name: Test (.NET 8.0/Release) + run: dotnet test tests -f net8.0 -c Release From 36ffd9ead0048c08f2f7d8cce84e9844d4a91e3d Mon Sep 17 00:00:00 2001 From: iRubens Date: Mon, 15 Apr 2024 17:08:03 +0200 Subject: [PATCH 20/26] NET6 projects configuration revert --- src/Cryptography/NSec.Cryptography.csproj | 2 +- src/Experimental/NSec.Experimental.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cryptography/NSec.Cryptography.csproj b/src/Cryptography/NSec.Cryptography.csproj index 7d7cb1ab..5e864d83 100644 --- a/src/Cryptography/NSec.Cryptography.csproj +++ b/src/Cryptography/NSec.Cryptography.csproj @@ -1,7 +1,7 @@  - net6.0;net8.0 + net6.0 diff --git a/src/Experimental/NSec.Experimental.csproj b/src/Experimental/NSec.Experimental.csproj index 9a6aeacc..4487b179 100644 --- a/src/Experimental/NSec.Experimental.csproj +++ b/src/Experimental/NSec.Experimental.csproj @@ -1,7 +1,7 @@  - net6.0;net8.0 + net6.0 From 471baf4786f3e93e6bfedb2c3609b2dc4f55dfc2 Mon Sep 17 00:00:00 2001 From: ektrah Date: Sat, 27 Apr 2024 10:22:58 +0200 Subject: [PATCH 21/26] Update supported platforms --- .github/workflows/test.yml | 47 +++-------------------- Directory.Build.props | 2 +- docs/install.md | 47 ++++++++++++----------- src/Cryptography/NSec.Cryptography.csproj | 2 +- src/Experimental/NSec.Experimental.csproj | 2 +- tests/Tests.csproj | 2 +- 6 files changed, 34 insertions(+), 68 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 14b2755f..fa4f4b39 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,6 @@ jobs: with: dotnet-version: | 6.0.x - 7.0.x 8.0.x - run: dotnet --info - uses: actions/checkout@v3 @@ -23,10 +22,6 @@ jobs: run: dotnet test tests -f net8.0 -c Debug - name: Test (.NET 8.0/Release) run: dotnet test tests -f net8.0 -c Release - - name: Test (.NET 7.0/Debug) - run: dotnet test tests -f net7.0 -c Debug - - name: Test (.NET 7.0/Release) - run: dotnet test tests -f net7.0 -c Release - name: Test (.NET 6.0/Debug) run: dotnet test tests -f net6.0 -c Debug - name: Test (.NET 6.0/Release) @@ -44,9 +39,9 @@ jobs: fail-fast: false matrix: include: - - os: macos-11 - os: macos-12 - os: macos-13 + - os: macos-14 runs-on: ${{ matrix.os }} env: DOTNET_CLI_TELEMETRY_OPTOUT: 1 @@ -57,18 +52,13 @@ jobs: with: dotnet-version: | 6.0.x - 7.0.x 8.0.x - run: dotnet --info - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - name: Test (.NET 8.0/Debug) run: dotnet test tests -f net8.0 -c Debug - name: Test (.NET 8.0/Release) run: dotnet test tests -f net8.0 -c Release - - name: Test (.NET 7.0/Debug) - run: dotnet test tests -f net7.0 -c Debug - - name: Test (.NET 7.0/Release) - run: dotnet test tests -f net7.0 -c Release - name: Test (.NET 6.0/Debug) run: dotnet test tests -f net6.0 -c Debug - name: Test (.NET 6.0/Release) @@ -80,16 +70,17 @@ jobs: fail-fast: false matrix: include: - - os: centos:7 - os: debian:10 - os: debian:11 - os: debian:12 - - os: fedora:37 - os: fedora:38 + - os: fedora:39 + - os: fedora:40 - os: ubuntu:16.04 - os: ubuntu:18.04 - os: ubuntu:20.04 - os: ubuntu:22.04 + - os: ubuntu:24.04 runs-on: ubuntu-latest container: image: ${{ matrix.os }} @@ -114,7 +105,6 @@ jobs: with: dotnet-version: | 6.0.x - 7.0.x 8.0.x - run: dotnet --info - uses: actions/checkout@v3 @@ -122,37 +112,12 @@ jobs: run: dotnet test tests -f net8.0 -c Debug - name: Test (.NET 8.0/Release) run: dotnet test tests -f net8.0 -c Release - - name: Test (.NET 7.0/Debug) - run: dotnet test tests -f net7.0 -c Debug - - name: Test (.NET 7.0/Release) - run: dotnet test tests -f net7.0 -c Release - name: Test (.NET 6.0/Debug) run: dotnet test tests -f net6.0 -c Debug - name: Test (.NET 6.0/Release) run: dotnet test tests -f net6.0 -c Release - test-linux-musl7: - strategy: - fail-fast: false - matrix: - include: - - os: alpine3.17 - - os: alpine3.18 - runs-on: ubuntu-latest - container: - image: mcr.microsoft.com/dotnet/sdk:7.0-${{ matrix.os }} - env: - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 - steps: - - run: dotnet --info - - uses: actions/checkout@v3 - - name: Test (.NET 7.0/Debug) - run: dotnet test tests -f net7.0 -c Debug - - name: Test (.NET 7.0/Release) - run: dotnet test tests -f net7.0 -c Release - - test-linux-musl8: + test-linux-musl: strategy: fail-fast: false matrix: diff --git a/Directory.Build.props b/Directory.Build.props index be877e77..1639a01a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@  - 12.0 + 9.0 strict enable diff --git a/docs/install.md b/docs/install.md index 1de9baed..9cefcd04 100644 --- a/docs/install.md +++ b/docs/install.md @@ -26,7 +26,7 @@ on the following platforms: Please note: 1. For Windows, the - [Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017, 2019, and 2022](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads) + [Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017, 2019, and 2022](https://learn.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist) is required. This is part of the .NET SDK but might not be present on a clean Windows installation. @@ -40,27 +40,28 @@ Please note: [NSec 23.9.0-preview.3](https://www.nuget.org/packages/NSec.Cryptography/23.9.0-preview.3) has been tested to run on the following platforms and .NET versions: -| OS | Version | Architectures | .NET | -|:-------------------- |:-------- |:------------- |:--------------- | -| Windows 11 | 22H2 | x64 | 7.0.11 / 6.0.22 | -| Windows Server | 2022 | x64 | 7.0.11 / 6.0.22 | -| | | | | -| macOS | 11.7 | x64 | 7.0.11 / 6.0.22 | -| | 12.6 | x64 | 7.0.11 / 6.0.22 | -| | 13.4 | x64 | 7.0.11 / 6.0.22 | -| | | | | -| Alpine Linux | 3.17 | x64 | 7.0.10 | -| | 3.18 | x64 | 7.0.11 | -| CentOS Linux | 7 | x64 | 7.0.11 / 6.0.22 | -| Debian | 10 | x64 | 7.0.11 / 6.0.22 | -| | 11 | x64 | 7.0.11 / 6.0.22 | -| | 12 | x64 | 7.0.11 / 6.0.22 | -| Fedora | 37 | x64 | 7.0.11 / 6.0.22 | -| | 38 | x64 | 7.0.11 / 6.0.22 | -| Ubuntu | 16.04 | x64 | 7.0.11 / 6.0.22 | -| | 18.04 | x64 | 7.0.11 / 6.0.22 | -| | 20.04 | x64 | 7.0.11 / 6.0.22 | -| | 22.04 | x64 | 7.0.11 / 6.0.22 | +| OS | Version | Architecture | .NET | +|:-------------------- |:-------- |:------------- |:-------------- | +| Windows 11 | 23H2 | x64 | 8.0.4 / 6.0.29 | +| Windows Server | 2022 | x64 | 8.0.4 / 6.0.29 | +| | | | | +| macOS | 12.7 | x64 | 8.0.4 / 6.0.29 | +| | 13.6 | x64 | 8.0.4 / 6.0.29 | +| | 14.4 | arm64 | 8.0.4 / 6.0.29 | +| | | | | +| Alpine Linux | 3.18 | x64 | 8.0.4 | +| | 3.19 | x64 | 8.0.4 | +| Debian | 10 | x64 | 8.0.4 / 6.0.29 | +| | 11 | x64 | 8.0.4 / 6.0.29 | +| | 12 | x64 | 8.0.4 / 6.0.29 | +| Fedora | 38 | x64 | 8.0.4 / 6.0.29 | +| | 39 | x64 | 8.0.4 / 6.0.29 | +| | 40 | x64 | 8.0.4 / 6.0.29 | +| Ubuntu | 16.04 | x64 | 8.0.4 / 6.0.29 | +| | 18.04 | x64 | 8.0.4 / 6.0.29 | +| | 20.04 | x64 | 8.0.4 / 6.0.29 | +| | 22.04 | x64 | 8.0.4 / 6.0.29 | +| | 24.04 | x64 | 8.0.4 / 6.0.29 | The other supported platforms should work as well, but haven't been tested. @@ -69,7 +70,7 @@ The other supported platforms should work as well, but haven't been tested. Below are some frequently asked questions: -**Q**: What could cause a *System.DllNotFoundException: Unable to load shared +**Q**: What causes a *System.DllNotFoundException: Unable to load shared library 'libsodium' or one of its dependencies.* when using the NSec.Cryptography NuGet package? **A**: This exception can occur if the operating system or architecture is not diff --git a/src/Cryptography/NSec.Cryptography.csproj b/src/Cryptography/NSec.Cryptography.csproj index 5e864d83..bcb16f9e 100644 --- a/src/Cryptography/NSec.Cryptography.csproj +++ b/src/Cryptography/NSec.Cryptography.csproj @@ -1,7 +1,7 @@  - net6.0 + net6.0 diff --git a/src/Experimental/NSec.Experimental.csproj b/src/Experimental/NSec.Experimental.csproj index 4487b179..bdcfcea1 100644 --- a/src/Experimental/NSec.Experimental.csproj +++ b/src/Experimental/NSec.Experimental.csproj @@ -1,7 +1,7 @@  - net6.0 + net6.0 diff --git a/tests/Tests.csproj b/tests/Tests.csproj index edc39290..bfaaf4ce 100644 --- a/tests/Tests.csproj +++ b/tests/Tests.csproj @@ -1,7 +1,7 @@  - net6.0;net7.0;net8.0 + net8.0;net7.0;net6.0 From d00e9f5e2bb1b79cf16a1b7808037afd86c76199 Mon Sep 17 00:00:00 2001 From: ektrah Date: Sat, 27 Apr 2024 10:51:30 +0200 Subject: [PATCH 22/26] Update actions to use Node.js 20 --- .github/workflows/test.yml | 16 +++++++--------- docs/install.md | 4 +--- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fa4f4b39..e45ed439 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,13 +11,13 @@ jobs: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 steps: - name: Set up .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | 6.0.x 8.0.x - run: dotnet --info - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Test (.NET 8.0/Debug) run: dotnet test tests -f net8.0 -c Debug - name: Test (.NET 8.0/Release) @@ -48,13 +48,13 @@ jobs: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 steps: - name: Set up .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | 6.0.x 8.0.x - run: dotnet --info - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Test (.NET 8.0/Debug) run: dotnet test tests -f net8.0 -c Debug - name: Test (.NET 8.0/Release) @@ -76,8 +76,6 @@ jobs: - os: fedora:38 - os: fedora:39 - os: fedora:40 - - os: ubuntu:16.04 - - os: ubuntu:18.04 - os: ubuntu:20.04 - os: ubuntu:22.04 - os: ubuntu:24.04 @@ -101,13 +99,13 @@ jobs: run: apt-get -qq update && apt-get -qq install --yes --no-install-recommends curl ca-certificates gettext if: ${{ startsWith(matrix.os, 'ubuntu') }} - name: Set up .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | 6.0.x 8.0.x - run: dotnet --info - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Test (.NET 8.0/Debug) run: dotnet test tests -f net8.0 -c Debug - name: Test (.NET 8.0/Release) @@ -132,7 +130,7 @@ jobs: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 steps: - run: dotnet --info - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Test (.NET 8.0/Debug) run: dotnet test tests -f net8.0 -c Debug - name: Test (.NET 8.0/Release) diff --git a/docs/install.md b/docs/install.md index 9cefcd04..1d1724a9 100644 --- a/docs/install.md +++ b/docs/install.md @@ -57,9 +57,7 @@ has been tested to run on the following platforms and .NET versions: | Fedora | 38 | x64 | 8.0.4 / 6.0.29 | | | 39 | x64 | 8.0.4 / 6.0.29 | | | 40 | x64 | 8.0.4 / 6.0.29 | -| Ubuntu | 16.04 | x64 | 8.0.4 / 6.0.29 | -| | 18.04 | x64 | 8.0.4 / 6.0.29 | -| | 20.04 | x64 | 8.0.4 / 6.0.29 | +| Ubuntu | 20.04 | x64 | 8.0.4 / 6.0.29 | | | 22.04 | x64 | 8.0.4 / 6.0.29 | | | 24.04 | x64 | 8.0.4 / 6.0.29 | From f8440e532796178ef4ca5debafefd40bbae1d84f Mon Sep 17 00:00:00 2001 From: ektrah Date: Sat, 27 Apr 2024 11:09:47 +0200 Subject: [PATCH 23/26] 2024 --- LICENSE | 2 +- NOTICE | 2 +- README.md | 2 +- notice.md | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/LICENSE b/LICENSE index f53d1507..33f80545 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Klaus Hartke +Copyright (c) 2024 Klaus Hartke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/NOTICE b/NOTICE index d5ed078d..054451aa 100644 --- a/NOTICE +++ b/NOTICE @@ -4,7 +4,7 @@ License Notice for libsodium This software is based on and contains code derived from libsodium, which is available under the ISC license. -Copyright (c) 2013-2023 Frank Denis +Copyright (c) 2013-2024 Frank Denis Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/README.md b/README.md index f63a4455..db76da78 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # NSec -[![Maintenance](https://img.shields.io/maintenance/yes/2023)](https://github.com/ektrah/nsec) +[![Maintenance](https://img.shields.io/maintenance/yes/2024)](https://github.com/ektrah/nsec) [![License](https://img.shields.io/github/license/ektrah/nsec)](https://nsec.rocks/license) [![NuGet](https://img.shields.io/nuget/vpre/NSec.Cryptography)](https://www.nuget.org/packages/NSec.Cryptography/23.9.0-preview.3) diff --git a/notice.md b/notice.md index f86f4892..c6e16a22 100644 --- a/notice.md +++ b/notice.md @@ -12,7 +12,7 @@ permission except when reproducing the unmodified NSec documentation. [NSec](https://github.com/ektrah/nsec) is licensed under the MIT license. -Copyright © 2023 Klaus Hartke +Copyright © 2024 Klaus Hartke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -39,7 +39,7 @@ NSec is based on and contains code derived from [libsodium](https://github.com/jedisct1/libsodium), which is available under the ISC license. -Copyright © 2013-2023 Frank Denis +Copyright © 2013-2024 Frank Denis Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above From 0c798cc85d16e894c77df0d0374f736a6bd51368 Mon Sep 17 00:00:00 2001 From: ektrah Date: Sat, 27 Apr 2024 11:16:25 +0200 Subject: [PATCH 24/26] 24.4.0 --- README.md | 2 +- build/Package.props | 2 +- docs/install.md | 8 ++++---- index.md | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index db76da78..8bc90aea 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Maintenance](https://img.shields.io/maintenance/yes/2024)](https://github.com/ektrah/nsec) [![License](https://img.shields.io/github/license/ektrah/nsec)](https://nsec.rocks/license) -[![NuGet](https://img.shields.io/nuget/vpre/NSec.Cryptography)](https://www.nuget.org/packages/NSec.Cryptography/23.9.0-preview.3) +[![NuGet](https://img.shields.io/nuget/vpre/NSec.Cryptography)](https://www.nuget.org/packages/NSec.Cryptography/24.4.0) [NSec](https://nsec.rocks/) is a cryptographic library for [.NET 6+](https://dotnet.microsoft.com/) based on diff --git a/build/Package.props b/build/Package.props index e1e50075..85bc4388 100644 --- a/build/Package.props +++ b/build/Package.props @@ -6,7 +6,7 @@ - + 24.4.0 $([System.DateTime]::UtcNow.ToString(y.M.0))-preview.4-$(BuildNumberMajor)-$(BuildNumberMinor) diff --git a/docs/install.md b/docs/install.md index 1d1724a9..a4267f83 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,14 +1,14 @@ # Installation Use the following command to install the -[NSec.Cryptography NuGet package](https://www.nuget.org/packages/NSec.Cryptography/23.9.0-preview.3): +[NSec.Cryptography NuGet package](https://www.nuget.org/packages/NSec.Cryptography/24.4.0): - $ dotnet add package NSec.Cryptography --version 23.9.0-preview.3 + $ dotnet add package NSec.Cryptography --version 24.4.0 ## Supported Platforms -[NSec 23.9.0-preview.3](https://www.nuget.org/packages/NSec.Cryptography/23.9.0-preview.3) +[NSec 24.4.0](https://www.nuget.org/packages/NSec.Cryptography/24.4.0) is intended to run on all [supported versions of .NET](https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core) on the following platforms: @@ -37,7 +37,7 @@ Please note: ## Tested Platforms -[NSec 23.9.0-preview.3](https://www.nuget.org/packages/NSec.Cryptography/23.9.0-preview.3) +[NSec 24.4.0](https://www.nuget.org/packages/NSec.Cryptography/24.4.0) has been tested to run on the following platforms and .NET versions: | OS | Version | Architecture | .NET | diff --git a/index.md b/index.md index 68ffa47a..004a8a34 100644 --- a/index.md +++ b/index.md @@ -42,7 +42,7 @@ and verify the signature: ## Installation - $ dotnet add package NSec.Cryptography --version 23.9.0-preview.3 + $ dotnet add package NSec.Cryptography --version 24.4.0 NSec works with .NET 6 and later on Windows, Linux and macOS. From c95d134488da66069c745fb78f618a6bc03e1fc7 Mon Sep 17 00:00:00 2001 From: ektrah Date: Sat, 27 Apr 2024 11:33:33 +0200 Subject: [PATCH 25/26] Bump version [ci skip] --- build/Package.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/Package.props b/build/Package.props index 85bc4388..e6dcde00 100644 --- a/build/Package.props +++ b/build/Package.props @@ -6,8 +6,8 @@ - 24.4.0 - $([System.DateTime]::UtcNow.ToString(y.M.0))-preview.4-$(BuildNumberMajor)-$(BuildNumberMinor) + + $([System.DateTime]::UtcNow.ToString(y.M.0))-preview.1-$(BuildNumberMajor)-$(BuildNumberMinor) From f21196a734a28a4906e11f21e14ff26436d284df Mon Sep 17 00:00:00 2001 From: ektrah Date: Sat, 27 Apr 2024 11:35:35 +0200 Subject: [PATCH 26/26] Update actions to use Node.js 20 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e45ed439..8020c7ab 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: - name: Pack run: dotnet pack -c Release - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: path: '**/*.nupkg'