diff --git a/src/LibOQS.NET.Tests/KemTests.cs b/src/LibOQS.NET.Tests/KemTests.cs
index 1673c01..679d88d 100644
--- a/src/LibOQS.NET.Tests/KemTests.cs
+++ b/src/LibOQS.NET.Tests/KemTests.cs
@@ -12,12 +12,6 @@ public class KemTests
[InlineData(KemAlgorithm.MlKem1024)]
public void KemEncapsDecaps_ShouldSucceed(KemAlgorithm algorithm)
{
- // Skip test if algorithm is not enabled
- if (!algorithm.IsEnabled())
- {
- return;
- }
-
using var kem = new KemInstance(algorithm);
// Generate keypair
@@ -42,11 +36,6 @@ public void KemEncapsDecaps_ShouldSucceed(KemAlgorithm algorithm)
[Fact]
public void KemEncapsulate_WithWrongKeySize_ShouldThrow()
{
- if (!KemAlgorithm.MlKem512.IsEnabled())
- {
- return;
- }
-
using var kem = new KemInstance(KemAlgorithm.MlKem512);
var wrongSizeKey = new byte[100]; // Wrong size
@@ -56,11 +45,6 @@ public void KemEncapsulate_WithWrongKeySize_ShouldThrow()
[Fact]
public void KemDecapsulate_WithWrongKeySize_ShouldThrow()
{
- if (!KemAlgorithm.MlKem512.IsEnabled())
- {
- return;
- }
-
using var kem = new KemInstance(KemAlgorithm.MlKem512);
var (publicKey, _) = kem.GenerateKeypair();
var (ciphertext, _) = kem.Encapsulate(publicKey);
@@ -73,11 +57,6 @@ public void KemDecapsulate_WithWrongKeySize_ShouldThrow()
[Fact]
public void KemDecapsulate_WithWrongCiphertextSize_ShouldThrow()
{
- if (!KemAlgorithm.MlKem512.IsEnabled())
- {
- return;
- }
-
using var kem = new KemInstance(KemAlgorithm.MlKem512);
var (_, secretKey) = kem.GenerateKeypair();
@@ -89,11 +68,6 @@ public void KemDecapsulate_WithWrongCiphertextSize_ShouldThrow()
[Fact]
public void KemDispose_ShouldAllowMultipleCalls()
{
- if (!KemAlgorithm.MlKem512.IsEnabled())
- {
- return;
- }
-
var kem = new KemInstance(KemAlgorithm.MlKem512);
kem.Dispose();
@@ -103,11 +77,6 @@ public void KemDispose_ShouldAllowMultipleCalls()
[Fact]
public void KemUseAfterDispose_ShouldThrow()
{
- if (!KemAlgorithm.MlKem512.IsEnabled())
- {
- return;
- }
-
var kem = new KemInstance(KemAlgorithm.MlKem512);
kem.Dispose();
diff --git a/src/LibOQS.NET.Tests/LibOqsFixture.cs b/src/LibOQS.NET.Tests/LibOqsFixture.cs
deleted file mode 100644
index 395305a..0000000
--- a/src/LibOQS.NET.Tests/LibOqsFixture.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using LibOQS.NET;
-
-namespace LibOQS.NET.Tests;
-
-///
-/// Test fixture to ensure LibOQS is properly managed for all tests
-///
-public class LibOqsFixture : IDisposable
-{
- public LibOqsFixture()
- {
- // LibOQS initialization is automatic via static constructor
- LibOqs.EnsureInitialized();
- }
-
- public void Dispose()
- {
- // Cleanup when all tests are done
- LibOqs.Cleanup();
- }
-}
diff --git a/src/LibOQS.NET.Tests/SigTests.cs b/src/LibOQS.NET.Tests/SigTests.cs
index 08bd0b3..3a7418d 100644
--- a/src/LibOQS.NET.Tests/SigTests.cs
+++ b/src/LibOQS.NET.Tests/SigTests.cs
@@ -10,33 +10,32 @@ public class SigTests
[InlineData(SigAlgorithm.MlDsa44)]
[InlineData(SigAlgorithm.MlDsa65)]
[InlineData(SigAlgorithm.MlDsa87)]
+ [InlineData(SigAlgorithm.Dilithium2)]
+ [InlineData(SigAlgorithm.Dilithium3)]
+ [InlineData(SigAlgorithm.Dilithium5)]
+ [InlineData(SigAlgorithm.Falcon512)]
+ [InlineData(SigAlgorithm.Falcon1024)]
public void SigSignVerify_ShouldSucceed(SigAlgorithm algorithm)
{
- // Skip test if algorithm is not enabled
- if (!algorithm.IsEnabled())
- {
- return;
- }
-
using var sig = new SigInstance(algorithm);
-
+
// Generate keypair
var (publicKey, secretKey) = sig.GenerateKeypair();
-
+
Assert.Equal(sig.PublicKeyLength, publicKey.Length);
Assert.Equal(sig.SecretKeyLength, secretKey.Length);
-
+
// Sign message
var message = "Hello, post-quantum world!"u8.ToArray();
var signature = sig.Sign(message, secretKey);
-
+
Assert.True(signature.Length > 0);
Assert.True(signature.Length <= sig.MaxSignatureLength);
-
+
// Verify signature
var isValid = sig.Verify(message, signature, publicKey);
Assert.True(isValid);
-
+
// Verify with tampered message should fail
var tamperedMessage = "Hello, post-quantum world?"u8.ToArray();
var isValidTampered = sig.Verify(tamperedMessage, signature, publicKey);
@@ -46,44 +45,29 @@ public void SigSignVerify_ShouldSucceed(SigAlgorithm algorithm)
[Fact]
public void SigSign_WithWrongKeySize_ShouldThrow()
{
- if (!SigAlgorithm.MlDsa44.IsEnabled())
- {
- return;
- }
-
using var sig = new SigInstance(SigAlgorithm.MlDsa44);
var message = "test"u8.ToArray();
var wrongSizeKey = new byte[100]; // Wrong size
-
+
Assert.Throws(() => sig.Sign(message, wrongSizeKey));
}
[Fact]
public void SigVerify_WithWrongKeySize_ShouldThrow()
{
- if (!SigAlgorithm.MlDsa44.IsEnabled())
- {
- return;
- }
-
using var sig = new SigInstance(SigAlgorithm.MlDsa44);
var message = "test"u8.ToArray();
var signature = new byte[100];
var wrongSizeKey = new byte[100]; // Wrong size
-
+
Assert.Throws(() => sig.Verify(message, signature, wrongSizeKey));
}
[Fact]
public void SigDispose_ShouldAllowMultipleCalls()
{
- if (!SigAlgorithm.MlDsa44.IsEnabled())
- {
- return;
- }
-
var sig = new SigInstance(SigAlgorithm.MlDsa44);
-
+
sig.Dispose();
sig.Dispose(); // Should not throw
}
@@ -91,19 +75,171 @@ public void SigDispose_ShouldAllowMultipleCalls()
[Fact]
public void SigUseAfterDispose_ShouldThrow()
{
- if (!SigAlgorithm.MlDsa44.IsEnabled())
- {
- return;
- }
-
var sig = new SigInstance(SigAlgorithm.MlDsa44);
sig.Dispose();
-
+
Assert.Throws(() => sig.GenerateKeypair());
}
[Fact]
public void SigEmptyMessage_ShouldWork()
+ {
+ using var sig = new SigInstance(SigAlgorithm.MlDsa44);
+ var (publicKey, secretKey) = sig.GenerateKeypair();
+
+ var emptyMessage = Array.Empty();
+ var signature = sig.Sign(emptyMessage, secretKey);
+ var isValid = sig.Verify(emptyMessage, signature, publicKey);
+
+ Assert.True(isValid);
+ }
+
+ [Theory]
+ [InlineData(SigAlgorithm.Dilithium2)]
+ [InlineData(SigAlgorithm.Dilithium3)]
+ [InlineData(SigAlgorithm.Dilithium5)]
+ public void DilithiumSignVerify_ShouldSucceed(SigAlgorithm algorithm)
+ {
+ using var sig = new SigInstance(algorithm);
+
+ // Generate keypair
+ var (publicKey, secretKey) = sig.GenerateKeypair();
+
+ Assert.Equal(sig.PublicKeyLength, publicKey.Length);
+ Assert.Equal(sig.SecretKeyLength, secretKey.Length);
+
+ // Test with various message sizes
+ var testMessages = new[]
+ {
+ Array.Empty(),
+ "Small message"u8.ToArray(),
+ new byte[1024], // Medium message
+ new byte[10000] // Large message
+ };
+
+ foreach (var message in testMessages)
+ {
+ var signature = sig.Sign(message, secretKey);
+ Assert.True(signature.Length > 0);
+ Assert.True(signature.Length <= sig.MaxSignatureLength);
+
+ var isValid = sig.Verify(message, signature, publicKey);
+ Assert.True(isValid);
+ }
+ }
+
+ [Theory]
+ [InlineData(SigAlgorithm.Falcon512)]
+ [InlineData(SigAlgorithm.Falcon1024)]
+ public void FalconSignVerify_ShouldSucceed(SigAlgorithm algorithm)
+ {
+ using var sig = new SigInstance(algorithm);
+
+ // Generate keypair
+ var (publicKey, secretKey) = sig.GenerateKeypair();
+
+ Assert.Equal(sig.PublicKeyLength, publicKey.Length);
+ Assert.Equal(sig.SecretKeyLength, secretKey.Length);
+
+ var message = "Falcon test message"u8.ToArray();
+ var signature1 = sig.Sign(message, secretKey);
+ var signature2 = sig.Sign(message, secretKey);
+
+ // Both signatures should be valid
+ Assert.True(sig.Verify(message, signature1, publicKey));
+ Assert.True(sig.Verify(message, signature2, publicKey));
+
+ // Test signature length consistency
+ Assert.True(signature1.Length <= sig.MaxSignatureLength);
+ Assert.True(signature2.Length <= sig.MaxSignatureLength);
+ }
+
+ [Fact]
+ public void SigAlgorithmIsEnabled_ShouldReturnConsistentResults()
+ {
+ var allAlgorithms = Enum.GetValues();
+
+ foreach (var algorithm in allAlgorithms)
+ {
+ if (algorithm.IsEnabled())
+ {
+ // If enabled, should be able to create instance
+ using var sig = new SigInstance(algorithm);
+ Assert.NotNull(sig);
+ }
+ }
+ }
+
+ [Theory]
+ [InlineData(SigAlgorithm.MlDsa44)]
+ [InlineData(SigAlgorithm.Dilithium2)]
+ [InlineData(SigAlgorithm.Falcon512)]
+ public void SigKeyLengths_ShouldBeConsistent(SigAlgorithm algorithm)
+ {
+ using var sig = new SigInstance(algorithm);
+
+ // Key lengths should be positive
+ Assert.True(sig.PublicKeyLength > 0);
+ Assert.True(sig.SecretKeyLength > 0);
+ Assert.True(sig.MaxSignatureLength > 0);
+
+ // Generated keys should match reported lengths
+ var (publicKey, secretKey) = sig.GenerateKeypair();
+ Assert.Equal(sig.PublicKeyLength, publicKey.Length);
+ Assert.Equal(sig.SecretKeyLength, secretKey.Length);
+ }
+
+ [Fact]
+ public void SigDifferentAlgorithms_ShouldNotCrossVerify()
+ {
+ var enabledAlgorithms = Enum.GetValues()
+ .Where(alg => alg.IsEnabled())
+ .Take(2)
+ .ToArray();
+
+ if (enabledAlgorithms.Length < 2)
+ {
+ // Skip if less than 2 algorithms are enabled
+ return;
+ }
+
+ using var sig1 = new SigInstance(enabledAlgorithms[0]);
+ using var sig2 = new SigInstance(enabledAlgorithms[1]);
+
+ var (pk1, sk1) = sig1.GenerateKeypair();
+ var (pk2, sk2) = sig2.GenerateKeypair();
+
+ var message = "Cross-algorithm test"u8.ToArray();
+ var signature1 = sig1.Sign(message, sk1);
+
+ // Should not verify with different algorithm's key
+ if (pk2.Length == pk1.Length) // Only test if key sizes match
+ {
+ var isValid = sig2.Verify(message, signature1, pk2);
+ Assert.False(isValid);
+ }
+ }
+
+ [Theory]
+ [InlineData(SigAlgorithm.MlDsa44)]
+ [InlineData(SigAlgorithm.Dilithium2)]
+ public void SigLargeMessage_ShouldWork(SigAlgorithm algorithm)
+ {
+ using var sig = new SigInstance(algorithm);
+ var (publicKey, secretKey) = sig.GenerateKeypair();
+
+ // Test with very large message (1MB)
+ var largeMessage = new byte[1024 * 1024];
+ new Random(42).NextBytes(largeMessage); // Deterministic random data
+
+ var signature = sig.Sign(largeMessage, secretKey);
+ var isValid = sig.Verify(largeMessage, signature, publicKey);
+
+ Assert.True(isValid);
+ }
+
+ [Fact]
+ public void SigNullMessage_ShouldThrow()
{
if (!SigAlgorithm.MlDsa44.IsEnabled())
{
@@ -112,11 +248,169 @@ public void SigEmptyMessage_ShouldWork()
using var sig = new SigInstance(SigAlgorithm.MlDsa44);
var (publicKey, secretKey) = sig.GenerateKeypair();
-
- var emptyMessage = Array.Empty();
- var signature = sig.Sign(emptyMessage, secretKey);
- var isValid = sig.Verify(emptyMessage, signature, publicKey);
-
- Assert.True(isValid);
+ var signature = new byte[100];
+
+ Assert.Throws(() => sig.Sign(null!, secretKey));
+ Assert.Throws(() => sig.Verify(null!, signature, publicKey));
+ }
+
+ [Fact]
+ public void SigNullKeys_ShouldThrow()
+ {
+ using var sig = new SigInstance(SigAlgorithm.MlDsa44);
+ var message = "test"u8.ToArray();
+ var signature = new byte[100];
+ var publicKey = new byte[sig.PublicKeyLength];
+
+ Assert.Throws(() => sig.Sign(message, null!));
+ Assert.Throws(() => sig.Verify(message, signature, null!));
+ Assert.Throws(() => sig.Verify(message, null!, publicKey));
+ }
+
+ [Fact]
+ public void SigVerify_WithInvalidSignature_ShouldReturnFalse()
+ {
+ using var sig = new SigInstance(SigAlgorithm.MlDsa44);
+ var (publicKey, secretKey) = sig.GenerateKeypair();
+ var message = "test message"u8.ToArray();
+
+ // Create invalid signature (all zeros)
+ var invalidSignature = new byte[100];
+
+ var isValid = sig.Verify(message, invalidSignature, publicKey);
+ Assert.False(isValid);
+ }
+
+ [Fact]
+ public void SigSign_WithTamperedSecretKey_ShouldFail()
+ {
+ using var sig = new SigInstance(SigAlgorithm.MlDsa44);
+ var (publicKey, secretKey) = sig.GenerateKeypair();
+ var message = "test message"u8.ToArray();
+
+ // Tamper with secret key
+ var tamperedSecretKey = (byte[])secretKey.Clone();
+ tamperedSecretKey[0] ^= 0xFF; // Flip bits in first byte
+
+ var signature = sig.Sign(message, tamperedSecretKey);
+ var isValid = sig.Verify(message, signature, publicKey);
+
+ // Should either throw during signing or produce invalid signature
+ Assert.False(isValid);
+ }
+
+ [Theory]
+ [InlineData(SigAlgorithm.MlDsa44)]
+ [InlineData(SigAlgorithm.MlDsa65)]
+ [InlineData(SigAlgorithm.MlDsa87)]
+ public void MlDsaAlgorithms_ShouldHaveCorrectSecurityLevels(SigAlgorithm algorithm)
+ {
+ using var sig = new SigInstance(algorithm);
+
+ // ML-DSA algorithms should have progressively larger keys and signatures
+ switch (algorithm)
+ {
+ case SigAlgorithm.MlDsa44:
+ // ML-DSA-44 should be the smallest
+ Assert.True(sig.PublicKeyLength > 0);
+ Assert.True(sig.SecretKeyLength > 0);
+ break;
+ case SigAlgorithm.MlDsa65:
+ // ML-DSA-65 should be larger than 44
+ Assert.True(sig.PublicKeyLength > 1000);
+ Assert.True(sig.SecretKeyLength > 2000);
+ break;
+ case SigAlgorithm.MlDsa87:
+ // ML-DSA-87 should be the largest
+ Assert.True(sig.PublicKeyLength > 1500);
+ Assert.True(sig.SecretKeyLength > 3000);
+ break;
+ }
+ }
+
+ [Theory]
+ [InlineData(SigAlgorithm.Falcon512)]
+ [InlineData(SigAlgorithm.Falcon1024)]
+ public void FalconAlgorithms_ShouldHaveCompactSignatures(SigAlgorithm algorithm)
+ {
+ using var sig = new SigInstance(algorithm);
+ var (publicKey, secretKey) = sig.GenerateKeypair();
+ var message = "Falcon signature test"u8.ToArray();
+
+ var signature = sig.Sign(message, secretKey);
+
+ // Falcon is known for compact signatures
+ // Signature should be much smaller than SPHINCS+ variants
+ Assert.True(signature.Length < 2000, $"Falcon signature too large: {signature.Length} bytes");
+
+ // Verify the signature works
+ Assert.True(sig.Verify(message, signature, publicKey));
+ }
+
+ [Fact]
+ public void SigMultipleInstances_ShouldWorkIndependently()
+ {
+ using var sig1 = new SigInstance(SigAlgorithm.MlDsa44);
+ using var sig2 = new SigInstance(SigAlgorithm.Dilithium2);
+
+ var (pk1, sk1) = sig1.GenerateKeypair();
+ var (pk2, sk2) = sig2.GenerateKeypair();
+
+ var message1 = "Message for MlDsa44"u8.ToArray();
+ var message2 = "Message for Dilithium2"u8.ToArray();
+
+ var signature1 = sig1.Sign(message1, sk1);
+ var signature2 = sig2.Sign(message2, sk2);
+
+ // Each should verify with its own keys
+ Assert.True(sig1.Verify(message1, signature1, pk1));
+ Assert.True(sig2.Verify(message2, signature2, pk2));
+ }
+
+ [Fact]
+ public async Task SigThreadSafety_MultipleKeypairGeneration()
+ {
+ const int threadCount = 4;
+ const int operationsPerThread = 5;
+ var results = new bool[threadCount];
+ var tasks = new Task[threadCount];
+
+ for (int i = 0; i < threadCount; i++)
+ {
+ int threadIndex = i;
+ tasks[i] = Task.Run(() =>
+ {
+ try
+ {
+ using var sig = new SigInstance(SigAlgorithm.MlDsa44);
+ for (int j = 0; j < operationsPerThread; j++)
+ {
+ var (pk, sk) = sig.GenerateKeypair();
+ var message = System.Text.Encoding.UTF8.GetBytes($"Thread {threadIndex} operation {j}");
+ var signature = sig.Sign(message, sk);
+ var isValid = sig.Verify(message, signature, pk);
+
+ if (!isValid)
+ {
+ results[threadIndex] = false;
+ return;
+ }
+ }
+ results[threadIndex] = true;
+ }
+ catch
+ {
+ results[threadIndex] = false;
+ }
+ });
+ }
+
+ await Task.WhenAll(tasks);
+
+ // All threads should succeed
+ foreach (var result in results)
+ {
+ Assert.True(result);
+ }
}
}