Skip to content

Commit

Permalink
Merge branch 'main' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
MrMatthewLayton committed Oct 18, 2024
2 parents b42d50b + 632271e commit bbd3a8f
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 32 deletions.
6 changes: 3 additions & 3 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>9.5.0</Version>
<PackageVersion>9.5.0</PackageVersion>
<AssemblyVersion>9.5.0</AssemblyVersion>
<Version>9.5.1</Version>
<PackageVersion>9.5.1</PackageVersion>
<AssemblyVersion>9.5.1</AssemblyVersion>
</PropertyGroup>
</Project>
145 changes: 145 additions & 0 deletions OnixLabs.Security.Cryptography.UnitTests/Sha3Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

using System.Security.Cryptography;
using OnixLabs.Core;
using Xunit;

namespace OnixLabs.Security.Cryptography.UnitTests;
Expand Down Expand Up @@ -598,4 +599,148 @@ public void Sha3Shake256ShouldProduceExpectedResult(char character, int length,
// Then
Assert.Equal(expected, actual);
}

[Theory(DisplayName = "Sha3Hash224 multiple block transforms should produce the expected result")]
[InlineData("abcdef", "", "ceb3f4cd85af081120bf69ecf76bf61232bd5d810866f0eca3c8907d")]
[InlineData("abcde", "f", "ceb3f4cd85af081120bf69ecf76bf61232bd5d810866f0eca3c8907d")]
[InlineData("abcd", "ef", "ceb3f4cd85af081120bf69ecf76bf61232bd5d810866f0eca3c8907d")]
[InlineData("abc", "def", "ceb3f4cd85af081120bf69ecf76bf61232bd5d810866f0eca3c8907d")]
[InlineData("ab", "cdef", "ceb3f4cd85af081120bf69ecf76bf61232bd5d810866f0eca3c8907d")]
[InlineData("a", "bcdef", "ceb3f4cd85af081120bf69ecf76bf61232bd5d810866f0eca3c8907d")]
[InlineData("", "abcdef", "ceb3f4cd85af081120bf69ecf76bf61232bd5d810866f0eca3c8907d")]
public void Sha3Hash224MultipleBlockTransformsShouldProduceExpectedResult(string firstBlock, string lastBlock, string expected)
{
// Given
using HashAlgorithm algorithm = Sha3.CreateSha3Hash224();
byte[] firstBlockBytes = firstBlock.ToByteArray();
byte[] lastBlockBytes = lastBlock.ToByteArray();

// When
algorithm.TransformBlock(firstBlockBytes, 0, firstBlockBytes.Length, null, 0);
algorithm.TransformFinalBlock(lastBlockBytes, 0, lastBlockBytes.Length);
string actual = new Hash(algorithm.Hash).ToString();

// Then
Assert.Equal(expected, actual);
}

[Theory(DisplayName = "Sha3Hash256 multiple block transforms should produce the expected result")]
[InlineData("abcdef", "", "59890c1d183aa279505750422e6384ccb1499c793872d6f31bb3bcaa4bc9f5a5")]
[InlineData("abcde", "f", "59890c1d183aa279505750422e6384ccb1499c793872d6f31bb3bcaa4bc9f5a5")]
[InlineData("abcd", "ef", "59890c1d183aa279505750422e6384ccb1499c793872d6f31bb3bcaa4bc9f5a5")]
[InlineData("abc", "def", "59890c1d183aa279505750422e6384ccb1499c793872d6f31bb3bcaa4bc9f5a5")]
[InlineData("ab", "cdef", "59890c1d183aa279505750422e6384ccb1499c793872d6f31bb3bcaa4bc9f5a5")]
[InlineData("a", "bcdef", "59890c1d183aa279505750422e6384ccb1499c793872d6f31bb3bcaa4bc9f5a5")]
[InlineData("", "abcdef", "59890c1d183aa279505750422e6384ccb1499c793872d6f31bb3bcaa4bc9f5a5")]
public void Sha3Hash256MultipleBlockTransformsShouldProduceExpectedResult(string firstBlock, string lastBlock, string expected)
{
// Given
using HashAlgorithm algorithm = Sha3.CreateSha3Hash256();
byte[] firstBlockBytes = firstBlock.ToByteArray();
byte[] lastBlockBytes = lastBlock.ToByteArray();

// When
algorithm.TransformBlock(firstBlockBytes, 0, firstBlockBytes.Length, null, 0);
algorithm.TransformFinalBlock(lastBlockBytes, 0, lastBlockBytes.Length);
string actual = new Hash(algorithm.Hash).ToString();

// Then
Assert.Equal(expected, actual);
}

[Theory(DisplayName = "Sha3Hash384 multiple block transforms should produce the expected result")]
[InlineData("abcdef", "", "d77460b0ce6109168480e279a81af32facb689ab96e22623f0122ff3a10ead263db6607f83876a843d3264dc2a863805")]
[InlineData("abcde", "f", "d77460b0ce6109168480e279a81af32facb689ab96e22623f0122ff3a10ead263db6607f83876a843d3264dc2a863805")]
[InlineData("abcd", "ef", "d77460b0ce6109168480e279a81af32facb689ab96e22623f0122ff3a10ead263db6607f83876a843d3264dc2a863805")]
[InlineData("abc", "def", "d77460b0ce6109168480e279a81af32facb689ab96e22623f0122ff3a10ead263db6607f83876a843d3264dc2a863805")]
[InlineData("ab", "cdef", "d77460b0ce6109168480e279a81af32facb689ab96e22623f0122ff3a10ead263db6607f83876a843d3264dc2a863805")]
[InlineData("a", "bcdef", "d77460b0ce6109168480e279a81af32facb689ab96e22623f0122ff3a10ead263db6607f83876a843d3264dc2a863805")]
[InlineData("", "abcdef", "d77460b0ce6109168480e279a81af32facb689ab96e22623f0122ff3a10ead263db6607f83876a843d3264dc2a863805")]
public void Sha3Hash384MultipleBlockTransformsShouldProduceExpectedResult(string firstBlock, string lastBlock, string expected)
{
// Given
using HashAlgorithm algorithm = Sha3.CreateSha3Hash384();
byte[] firstBlockBytes = firstBlock.ToByteArray();
byte[] lastBlockBytes = lastBlock.ToByteArray();

// When
algorithm.TransformBlock(firstBlockBytes, 0, firstBlockBytes.Length, null, 0);
algorithm.TransformFinalBlock(lastBlockBytes, 0, lastBlockBytes.Length);
string actual = new Hash(algorithm.Hash).ToString();

// Then
Assert.Equal(expected, actual);
}

[Theory(DisplayName = "Sha3Hash512 multiple block transforms should produce the expected result")]
[InlineData("abcdef", "", "01309a45c57cd7faef9ee6bb95fed29e5e2e0312af12a95fffeee340e5e5948b4652d26ae4b75976a53cc1612141af6e24df36517a61f46a1a05f59cf667046a")]
[InlineData("abcde", "f", "01309a45c57cd7faef9ee6bb95fed29e5e2e0312af12a95fffeee340e5e5948b4652d26ae4b75976a53cc1612141af6e24df36517a61f46a1a05f59cf667046a")]
[InlineData("abcd", "ef", "01309a45c57cd7faef9ee6bb95fed29e5e2e0312af12a95fffeee340e5e5948b4652d26ae4b75976a53cc1612141af6e24df36517a61f46a1a05f59cf667046a")]
[InlineData("abc", "def", "01309a45c57cd7faef9ee6bb95fed29e5e2e0312af12a95fffeee340e5e5948b4652d26ae4b75976a53cc1612141af6e24df36517a61f46a1a05f59cf667046a")]
[InlineData("ab", "cdef", "01309a45c57cd7faef9ee6bb95fed29e5e2e0312af12a95fffeee340e5e5948b4652d26ae4b75976a53cc1612141af6e24df36517a61f46a1a05f59cf667046a")]
[InlineData("a", "bcdef", "01309a45c57cd7faef9ee6bb95fed29e5e2e0312af12a95fffeee340e5e5948b4652d26ae4b75976a53cc1612141af6e24df36517a61f46a1a05f59cf667046a")]
[InlineData("", "abcdef", "01309a45c57cd7faef9ee6bb95fed29e5e2e0312af12a95fffeee340e5e5948b4652d26ae4b75976a53cc1612141af6e24df36517a61f46a1a05f59cf667046a")]
public void Sha3Hash512MultipleBlockTransformsShouldProduceExpectedResult(string firstBlock, string lastBlock, string expected)
{
// Given
using HashAlgorithm algorithm = Sha3.CreateSha3Hash512();
byte[] firstBlockBytes = firstBlock.ToByteArray();
byte[] lastBlockBytes = lastBlock.ToByteArray();

// When
algorithm.TransformBlock(firstBlockBytes, 0, firstBlockBytes.Length, null, 0);
algorithm.TransformFinalBlock(lastBlockBytes, 0, lastBlockBytes.Length);
string actual = new Hash(algorithm.Hash).ToString();

// Then
Assert.Equal(expected, actual);
}

[Theory(DisplayName = "Sha3Shake128 multiple block transforms should produce the expected result")]
[InlineData("abcdef", "", "9428dbf9493c942630c0618d8a0983d518e828a7c0f4a39c2a54e013f64ebc125475308324e864c2617062639263a24bd58c26379342b40bad4a81e6f3e2c32e41bcd52927971ad0374c88f3244b6d229652a454fdc4fa422838eab19aa2fac7ddf457d66122ea674424e534b529e65684b2b4e3404914ad814a53")]
[InlineData("abcde", "f", "9428dbf9493c942630c0618d8a0983d518e828a7c0f4a39c2a54e013f64ebc125475308324e864c2617062639263a24bd58c26379342b40bad4a81e6f3e2c32e41bcd52927971ad0374c88f3244b6d229652a454fdc4fa422838eab19aa2fac7ddf457d66122ea674424e534b529e65684b2b4e3404914ad814a53")]
[InlineData("abcd", "ef", "9428dbf9493c942630c0618d8a0983d518e828a7c0f4a39c2a54e013f64ebc125475308324e864c2617062639263a24bd58c26379342b40bad4a81e6f3e2c32e41bcd52927971ad0374c88f3244b6d229652a454fdc4fa422838eab19aa2fac7ddf457d66122ea674424e534b529e65684b2b4e3404914ad814a53")]
[InlineData("abc", "def", "9428dbf9493c942630c0618d8a0983d518e828a7c0f4a39c2a54e013f64ebc125475308324e864c2617062639263a24bd58c26379342b40bad4a81e6f3e2c32e41bcd52927971ad0374c88f3244b6d229652a454fdc4fa422838eab19aa2fac7ddf457d66122ea674424e534b529e65684b2b4e3404914ad814a53")]
[InlineData("ab", "cdef", "9428dbf9493c942630c0618d8a0983d518e828a7c0f4a39c2a54e013f64ebc125475308324e864c2617062639263a24bd58c26379342b40bad4a81e6f3e2c32e41bcd52927971ad0374c88f3244b6d229652a454fdc4fa422838eab19aa2fac7ddf457d66122ea674424e534b529e65684b2b4e3404914ad814a53")]
[InlineData("a", "bcdef", "9428dbf9493c942630c0618d8a0983d518e828a7c0f4a39c2a54e013f64ebc125475308324e864c2617062639263a24bd58c26379342b40bad4a81e6f3e2c32e41bcd52927971ad0374c88f3244b6d229652a454fdc4fa422838eab19aa2fac7ddf457d66122ea674424e534b529e65684b2b4e3404914ad814a53")]
[InlineData("", "abcdef", "9428dbf9493c942630c0618d8a0983d518e828a7c0f4a39c2a54e013f64ebc125475308324e864c2617062639263a24bd58c26379342b40bad4a81e6f3e2c32e41bcd52927971ad0374c88f3244b6d229652a454fdc4fa422838eab19aa2fac7ddf457d66122ea674424e534b529e65684b2b4e3404914ad814a53")]
public void Sha3Shake128MultipleBlockTransformsShouldProduceExpectedResult(string firstBlock, string lastBlock, string expected)
{
// Given
using HashAlgorithm algorithm = Sha3.CreateSha3Shake128(123);
byte[] firstBlockBytes = firstBlock.ToByteArray();
byte[] lastBlockBytes = lastBlock.ToByteArray();

// When
algorithm.TransformBlock(firstBlockBytes, 0, firstBlockBytes.Length, null, 0);
algorithm.TransformFinalBlock(lastBlockBytes, 0, lastBlockBytes.Length);
string actual = new Hash(algorithm.Hash).ToString();

// Then
Assert.Equal(expected, actual);
}

[Theory(DisplayName = "Sha3Shake256 multiple block transforms should produce the expected result")]
[InlineData("abcdef", "", "81d5e45d095acf3c0decf25bcc63f6ee16c689b909b48040ad91c7c67dfe4e9fec910fa73b44e84541600b5a5736b7b0869c89b1e403e35e550158e09bdb7430a6617cff69c0f10fdaf1035ac62ab6277cd267616c06b9ce4d888543ba5771eecf12df031e9add88f314de262dc1bb0c4aae43c9b5316fea1af11d")]
[InlineData("abcde", "f", "81d5e45d095acf3c0decf25bcc63f6ee16c689b909b48040ad91c7c67dfe4e9fec910fa73b44e84541600b5a5736b7b0869c89b1e403e35e550158e09bdb7430a6617cff69c0f10fdaf1035ac62ab6277cd267616c06b9ce4d888543ba5771eecf12df031e9add88f314de262dc1bb0c4aae43c9b5316fea1af11d")]
[InlineData("abcd", "ef", "81d5e45d095acf3c0decf25bcc63f6ee16c689b909b48040ad91c7c67dfe4e9fec910fa73b44e84541600b5a5736b7b0869c89b1e403e35e550158e09bdb7430a6617cff69c0f10fdaf1035ac62ab6277cd267616c06b9ce4d888543ba5771eecf12df031e9add88f314de262dc1bb0c4aae43c9b5316fea1af11d")]
[InlineData("abc", "def", "81d5e45d095acf3c0decf25bcc63f6ee16c689b909b48040ad91c7c67dfe4e9fec910fa73b44e84541600b5a5736b7b0869c89b1e403e35e550158e09bdb7430a6617cff69c0f10fdaf1035ac62ab6277cd267616c06b9ce4d888543ba5771eecf12df031e9add88f314de262dc1bb0c4aae43c9b5316fea1af11d")]
[InlineData("ab", "cdef", "81d5e45d095acf3c0decf25bcc63f6ee16c689b909b48040ad91c7c67dfe4e9fec910fa73b44e84541600b5a5736b7b0869c89b1e403e35e550158e09bdb7430a6617cff69c0f10fdaf1035ac62ab6277cd267616c06b9ce4d888543ba5771eecf12df031e9add88f314de262dc1bb0c4aae43c9b5316fea1af11d")]
[InlineData("a", "bcdef", "81d5e45d095acf3c0decf25bcc63f6ee16c689b909b48040ad91c7c67dfe4e9fec910fa73b44e84541600b5a5736b7b0869c89b1e403e35e550158e09bdb7430a6617cff69c0f10fdaf1035ac62ab6277cd267616c06b9ce4d888543ba5771eecf12df031e9add88f314de262dc1bb0c4aae43c9b5316fea1af11d")]
[InlineData("", "abcdef", "81d5e45d095acf3c0decf25bcc63f6ee16c689b909b48040ad91c7c67dfe4e9fec910fa73b44e84541600b5a5736b7b0869c89b1e403e35e550158e09bdb7430a6617cff69c0f10fdaf1035ac62ab6277cd267616c06b9ce4d888543ba5771eecf12df031e9add88f314de262dc1bb0c4aae43c9b5316fea1af11d")]
public void Sha3Shake256MultipleBlockTransformsShouldProduceExpectedResult(string firstBlock, string lastBlock, string expected)
{
// Given
using HashAlgorithm algorithm = Sha3.CreateSha3Shake256(123);
byte[] firstBlockBytes = firstBlock.ToByteArray();
byte[] lastBlockBytes = lastBlock.ToByteArray();

// When
algorithm.TransformBlock(firstBlockBytes, 0, firstBlockBytes.Length, null, 0);
algorithm.TransformFinalBlock(lastBlockBytes, 0, lastBlockBytes.Length);
string actual = new Hash(algorithm.Hash).ToString();

// Then
Assert.Equal(expected, actual);
}
}
55 changes: 26 additions & 29 deletions OnixLabs.Security.Cryptography/Sha3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,11 @@ public abstract partial class Sha3 : HashAlgorithm
/// </summary>
private readonly int delimiter;

// /// <summary>
// /// The length of the hash in bits.
// /// </summary>
// private readonly int bitLength;

/// <summary>
/// The state block size.
/// </summary>
private int blockSize;

/// <summary>
/// The state input pointer.
/// </summary>
private int inputPointer;

/// <summary>
/// The state output pointer.
/// </summary>
private int outputPointer;

/// <summary>
/// The permutable sponge state.
/// </summary>
Expand All @@ -83,17 +68,17 @@ protected Sha3(int rateBytes, int delimiter, int bitLength)
this.rateBytes = rateBytes;
this.delimiter = delimiter;
HashSizeValue = bitLength;

Initialize();
}

/// <summary>
/// Initializes an implementation of the <see cref="Sha3"/> class.
/// </summary>
public override void Initialize()
public sealed override void Initialize()
{
// ReSharper disable HeapView.ObjectAllocation.Evident
blockSize = default;
inputPointer = default;
outputPointer = default;
state = new ulong[25];
result = new byte[HashSize / 8];
}
Expand All @@ -106,23 +91,30 @@ public override void Initialize()
/// <param name="cbSize">The number of bytes in the byte array to use as data.</param>
protected override void HashCore(byte[] array, int ibStart, int cbSize)
{
Initialize();
int offset = ibStart;

while (cbSize > 0)
{
blockSize = Math.Min(cbSize, rateBytes);
// Calculate the number of bytes we can process in this iteration
int bytesToProcess = Math.Min(cbSize, rateBytes - blockSize);

for (int index = ibStart; index < blockSize; index++)
// Absorb the input into the state
for (int i = 0; i < bytesToProcess; i++)
{
byte value = Convert.ToByte(Buffer.GetByte(state, index) ^ array[index + inputPointer]);
Buffer.SetByte(state, index, value);
int stateIndex = blockSize + i;
byte value = Convert.ToByte(Buffer.GetByte(state, stateIndex) ^ array[offset + i]);
Buffer.SetByte(state, stateIndex, value);
}

inputPointer += blockSize;
cbSize -= blockSize;
// Update the block size and offsets
blockSize += bytesToProcess;
offset += bytesToProcess;
cbSize -= bytesToProcess;

// If the block isn't full, continue...
if (blockSize != rateBytes) continue;

// ...otherwise, permute the state
Permute(state);
blockSize = 0;
}
Expand All @@ -134,23 +126,28 @@ protected override void HashCore(byte[] array, int ibStart, int cbSize)
/// <returns>The computed hash code.</returns>
protected override byte[] HashFinal()
{
// Apply padding to the current block
byte pad = Convert.ToByte(Buffer.GetByte(state, blockSize) ^ delimiter);
Buffer.SetByte(state, blockSize, pad);

// If the delimiter has its highest bit set, and we're at the last byte of the block, permute the state
if ((delimiter & 0x80) != 0 && blockSize == rateBytes - 1) Permute(state);

// Apply final padding and permute the state
pad = Convert.ToByte(Buffer.GetByte(state, rateBytes - 1) ^ 0x80);
Buffer.SetByte(state, rateBytes - 1, pad);
Permute(state);

int outputBytesLeft = HashSize / 8;
int outputOffset = 0; // Local variable to track the offset in the result array

// Extract the hash output from the state
while (outputBytesLeft > 0)
{
blockSize = Math.Min(outputBytesLeft, rateBytes);
Buffer.BlockCopy(state, 0, result, outputPointer, blockSize);
outputPointer += blockSize;
outputBytesLeft -= blockSize;
int bytesToOutput = Math.Min(outputBytesLeft, rateBytes);
Buffer.BlockCopy(state, 0, result, outputOffset, bytesToOutput);
outputOffset += bytesToOutput;
outputBytesLeft -= bytesToOutput;

if (outputBytesLeft > 0) Permute(state);
}
Expand Down

0 comments on commit bbd3a8f

Please sign in to comment.