Skip to content

Commit 7f6eed4

Browse files
authored
Add Ascon Hash Algorithm (#479)
1 parent 833a0fb commit 7f6eed4

16 files changed

+1747
-1
lines changed
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
using System;
2+
using System.Text;
3+
using Algorithms.Crypto.Digests;
4+
using Algorithms.Crypto.Exceptions;
5+
using FluentAssertions;
6+
using NUnit.Framework;
7+
8+
namespace Algorithms.Tests.Crypto.Digests;
9+
10+
[NonParallelizable]
11+
public class AsconDigestTests
12+
{
13+
private readonly AsconDigest asconHash = new AsconDigest(AsconDigest.AsconParameters.AsconHash);
14+
private readonly AsconDigest asconHashA = new AsconDigest(AsconDigest.AsconParameters.AsconHashA);
15+
16+
[TestCase("a", "02a9d471afab12914197af7090f00d16c41b6e30be0a63bbfd00bc13064de548")]
17+
[TestCase("abc", "d37fe9f1d10dbcfad8408a6804dbe91124a8912693322bb23ec1701e19e3fd51")]
18+
[TestCase("Hello", "d80f38d94ad72bd18718879f753a44870e8446925ff64bd7441db5fe020b6c0c")]
19+
[TestCase("message digest", "e8848979c5adfd21bfcf29e54be1dd085ee523d251e8e6876f2654d6368da0ca")]
20+
[TestCase("abcdefghijklmnopqrstuvwxyz", "c62368674e1b2301f19f46c50bb7f87a988a3e41205d68ab9d7882d2a15e917b")]
21+
[TestCase("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "4ff71928d740524735b5ab12bb1598463054f88089f3c5f9760b6bdcd23f897b")]
22+
[TestCase("12345678901234567890123456789012345678901234567890123456789012345678901234567890", "2dae8b553b93841120e88ee77b9ccb8b512a32318db6012025f3f1c482b1def8")]
23+
public void AsconHash_ReturnsCorrectValue(string input, string expected)
24+
{
25+
var inputBytes = Encoding.ASCII.GetBytes(input);
26+
var result = asconHash.Digest(inputBytes);
27+
28+
result.Should().Be(expected);
29+
}
30+
31+
[TestCase("a", "062bb0346671da00da4f460308b4d2c4d9877c3e2827d6229ff5361332d36527")]
32+
[TestCase("abc", "836a5ddba0142b011ce3425ea9789fd6a21628d619195a48c1540f847667a84e")]
33+
[TestCase("Hello", "15f245df8af697dc540e86083822809ab7299575d8ad6c2e17ecc603a7ab79dd")]
34+
[TestCase("message digest", "3f18a1f398a40a77e0e9477aa6cb50e9e1abecff651c1874f9717c02c8a165ba")]
35+
[TestCase("abcdefghijklmnopqrstuvwxyz", "406b809260f361e12dcf0bf924bfe1ffd2f987fc18d90b94fc544ff80dc2946b")]
36+
[TestCase("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "5c6c69ff3ee83361391b7236c8eb6718f52df43de5a61a4f4d2819d40430dc19")]
37+
[TestCase("12345678901234567890123456789012345678901234567890123456789012345678901234567890", "d8e38fc50d682550cd176decda61adb7fd1c793cdafa825f17f3a002d65847be")]
38+
public void AsconHashA_ReturnsCorrectValue(string input, string expected)
39+
{
40+
var inputBytes = Encoding.ASCII.GetBytes(input);
41+
var result = asconHashA.Digest(inputBytes);
42+
43+
result.Should().Be(expected);
44+
}
45+
46+
[Test]
47+
public void BlockUpdate_WithValidOffsetAndLength_ShouldProcessCorrectly()
48+
{
49+
// Arrange
50+
var input = new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99 };
51+
var offset = 2;
52+
var length = 6; // Picking 6 bytes starting from offset 2
53+
54+
// Act
55+
var act = () => asconHash.BlockUpdate(input, offset, length);
56+
57+
// Assert
58+
act.Should().NotThrow(); // Ensure no exceptions are thrown during processing
59+
60+
// Finalize the hash and check the output size
61+
var output = new byte[asconHash.GetDigestSize()];
62+
asconHash.DoFinal(output, 0);
63+
output.Should().HaveCount(32); // Ascon hash size is 32 bytes
64+
}
65+
66+
[Test]
67+
public void BlockUpdate_WithInvalidOffset_ShouldThrowDataLengthException()
68+
{
69+
// Arrange
70+
var input = new byte[] { 0x00, 0x11, 0x22, 0x33 };
71+
var offset = 3; // Offset goes too close to the end
72+
var length = 3; // Length would exceed buffer size
73+
74+
// Act
75+
var act = () => asconHash.BlockUpdate(input, offset, length);
76+
77+
// Assert
78+
act.Should().Throw<DataLengthException>()
79+
.WithMessage("input buffer too short");
80+
}
81+
82+
[Test]
83+
public void BlockUpdate_WithInvalidLength_ShouldThrowDataLengthException()
84+
{
85+
// Arrange
86+
var input = new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
87+
var offset = 1; // Valid offset
88+
var length = 10; // Invalid length (exceeds buffer)
89+
90+
// Act
91+
var act = () => asconHash.BlockUpdate(input, offset, length);
92+
93+
// Assert
94+
act.Should().Throw<DataLengthException>()
95+
.WithMessage("input buffer too short");
96+
}
97+
98+
[Test]
99+
public void BlockUpdate_WithPartialBlock_ShouldProcessCorrectly()
100+
{
101+
// Arrange
102+
var input = new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44 };
103+
var offset = 0;
104+
var length = 5; // Less than 8 bytes, partial block
105+
106+
// Act
107+
asconHash.BlockUpdate(input, offset, length);
108+
109+
// Assert
110+
var output = new byte[asconHash.GetDigestSize()];
111+
asconHash.DoFinal(output, 0);
112+
output.Should().HaveCount(32); // Ensure valid hash output
113+
}
114+
115+
[Test]
116+
public void BlockUpdate_WithFullBlock_ShouldProcessCorrectly()
117+
{
118+
// Arrange
119+
var input = new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
120+
var offset = 0;
121+
var length = 8; // Full block
122+
123+
// Act
124+
asconHash.BlockUpdate(input, offset, length);
125+
126+
// Assert
127+
var output = new byte[asconHash.GetDigestSize()];
128+
asconHash.DoFinal(output, 0);
129+
output.Should().HaveCount(32); // Ensure valid hash output
130+
}
131+
132+
[Test]
133+
public void BlockUpdate_MultipleCalls_ShouldProcessCorrectly()
134+
{
135+
// Arrange
136+
var input1 = new byte[] { 0x00, 0x11, 0x22 };
137+
var input2 = new byte[] { 0x33, 0x44, 0x55, 0x66, 0x77 };
138+
139+
// Act
140+
asconHash.BlockUpdate(input1, 0, input1.Length);
141+
asconHash.BlockUpdate(input2, 0, input2.Length);
142+
143+
// Assert
144+
var output = new byte[asconHash.GetDigestSize()];
145+
asconHash.DoFinal(output, 0);
146+
output.Should().HaveCount(32); // Ensure valid hash output
147+
}
148+
149+
[Test]
150+
public void AsconHash_WhenGetNameIsCalled_ReturnsCorrectValue()
151+
{
152+
asconHash.AlgorithmName.Should().Be("Ascon-Hash");
153+
asconHashA.AlgorithmName.Should().Be("Ascon-HashA");
154+
}
155+
156+
[Test]
157+
public void AsconHash_WhenGetByteLengthIsCalled_ReturnsCorrectValue()
158+
{
159+
asconHash.GetByteLength().Should().Be(8);
160+
}
161+
162+
[Test]
163+
public void Update_ShouldProcessByte_WhenBufferIsFull()
164+
{
165+
// Arrange
166+
byte[] inputBytes = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; // 8 bytes to fill the buffer
167+
168+
// Act
169+
foreach (var input in inputBytes)
170+
{
171+
asconHashA.Update(input);
172+
}
173+
174+
// Assert
175+
// Since the buffer is full after 8 updates, we expect the state to have been processed.
176+
var output = new byte[asconHashA.GetDigestSize()];
177+
asconHashA.DoFinal(output, 0);
178+
output.Should().HaveCount(32); // Ascon hash size is 32 bytes
179+
}
180+
181+
[Test]
182+
public void Update_ShouldNotProcess_WhenBufferIsNotFull()
183+
{
184+
// Arrange
185+
byte[] inputBytes = { 0x00, 0x11, 0x22, 0x33 }; // Only 4 bytes (buffer is not full)
186+
187+
// Act
188+
foreach (var input in inputBytes)
189+
{
190+
asconHashA.Update(input);
191+
}
192+
193+
// Assert
194+
// Even though the buffer has received input, it should not process until it is full (8 bytes).
195+
// We can check that DoFinal still completes, but the buffer has not been processed yet.
196+
var output = new byte[asconHashA.GetDigestSize()];
197+
asconHashA.DoFinal(output, 0);
198+
output.Should().HaveCount(32); // Ensure valid hash output
199+
}
200+
201+
[Test]
202+
public void Update_ShouldProcessMultipleBlocks()
203+
{
204+
// Arrange
205+
var inputBytes = new byte[16]; // Enough to fill two full blocks (16 bytes)
206+
207+
// Act
208+
foreach (var input in inputBytes)
209+
{
210+
asconHashA.Update(input);
211+
}
212+
213+
// Assert
214+
// Ensure that the state is processed twice since 16 bytes were passed (2 blocks of 8 bytes).
215+
var output = new byte[asconHashA.GetDigestSize()];
216+
asconHashA.DoFinal(output, 0);
217+
output.Should().HaveCount(32); // Ensure valid hash output
218+
}
219+
220+
[Test]
221+
public void Update_ShouldHandleSingleByteCorrectly()
222+
{
223+
// Arrange
224+
byte input = 0xFF; // Single byte input
225+
226+
// Act
227+
asconHashA.Update(input);
228+
229+
// Assert
230+
// Even though one byte is provided, it should not process the state (waiting for 8 bytes).
231+
var output = new byte[asconHashA.GetDigestSize()];
232+
asconHashA.DoFinal(output, 0);
233+
output.Should().HaveCount(32); // Ensure valid hash output
234+
}
235+
236+
[Test]
237+
public void Update_ShouldAccumulateStateWithMultipleUpdates()
238+
{
239+
// Arrange
240+
byte[] inputBytes = { 0x00, 0x11, 0x22 }; // Partial input
241+
242+
// Act
243+
foreach (var input in inputBytes)
244+
{
245+
asconHashA.Update(input);
246+
}
247+
248+
// Add more data to fill the buffer.
249+
byte[] additionalBytes = { 0x33, 0x44, 0x55, 0x66, 0x77 };
250+
foreach (var input in additionalBytes)
251+
{
252+
asconHashA.Update(input);
253+
}
254+
255+
// Assert
256+
// Ensure that the state is correctly updated after multiple partial updates.
257+
var output = new byte[asconHashA.GetDigestSize()];
258+
asconHashA.DoFinal(output, 0);
259+
output.Should().HaveCount(32); // Ensure valid hash output
260+
}
261+
262+
private static string ToHexString(byte[] bytes)
263+
{
264+
return BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant();
265+
}
266+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using Algorithms.Crypto.Exceptions;
2+
using NUnit.Framework;
3+
using FluentAssertions;
4+
using System;
5+
6+
7+
namespace Algorithms.Tests.Crypto.Exceptions
8+
{
9+
[TestFixture]
10+
public class CryptoExceptionTests
11+
{
12+
[Test]
13+
public void CryptoException_ShouldBeCreatedWithoutMessageOrInnerException()
14+
{
15+
// Act
16+
var exception = new CryptoException();
17+
18+
// Assert
19+
exception.Should().BeOfType<CryptoException>()
20+
.And.Subject.As<CryptoException>()
21+
.Message.Should().NotBeNullOrEmpty();
22+
exception.InnerException.Should().BeNull();
23+
}
24+
25+
[Test]
26+
public void CryptoException_ShouldSetMessage()
27+
{
28+
// Arrange
29+
var expectedMessage = "This is a custom cryptographic error.";
30+
31+
// Act
32+
var exception = new CryptoException(expectedMessage);
33+
34+
// Assert
35+
exception.Should().BeOfType<CryptoException>()
36+
.And.Subject.As<CryptoException>()
37+
.Message.Should().Be(expectedMessage);
38+
exception.InnerException.Should().BeNull();
39+
}
40+
41+
[Test]
42+
public void CryptoException_ShouldSetMessageAndInnerException()
43+
{
44+
// Arrange
45+
var expectedMessage = "An error occurred during encryption.";
46+
var innerException = new InvalidOperationException("Invalid operation");
47+
48+
// Act
49+
var exception = new CryptoException(expectedMessage, innerException);
50+
51+
// Assert
52+
exception.Should().BeOfType<CryptoException>()
53+
.And.Subject.As<CryptoException>()
54+
.Message.Should().Be(expectedMessage);
55+
exception.InnerException.Should().Be(innerException);
56+
}
57+
58+
[Test]
59+
public void CryptoException_MessageShouldNotBeNullWhenUsingDefaultConstructor()
60+
{
61+
// Act
62+
var exception = new CryptoException();
63+
64+
// Assert
65+
exception.Message.Should().NotBeNullOrEmpty(); // Even the default Exception message is not null or empty.
66+
}
67+
}
68+
}
69+

0 commit comments

Comments
 (0)