Skip to content

Commit 1d97711

Browse files
authored
SHA3 NIST test vectors (#185)
1 parent 246e9de commit 1d97711

10 files changed

Lines changed: 1812 additions & 119 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ StyleCopReport.xml
8282
*.pgc
8383
*.pgd
8484
*.rsp
85+
!cas-dotnet-sdk-tests/**/**/*.rsp
8586
*.sbr
8687
*.tlb
8788
*.tli

cas-dotnet-sdk-tests/SHA/Data/SHA3_256LongMsg.rsp

Lines changed: 408 additions & 0 deletions
Large diffs are not rendered by default.

cas-dotnet-sdk-tests/SHA/Data/SHA3_256ShortMsg.rsp

Lines changed: 555 additions & 0 deletions
Large diffs are not rendered by default.

cas-dotnet-sdk-tests/SHA/Data/SHA3_512LongMsg.rsp

Lines changed: 408 additions & 0 deletions
Large diffs are not rendered by default.

cas-dotnet-sdk-tests/SHA/Data/SHA3_512ShortMsg.rsp

Lines changed: 299 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
using CasDotnetSdk.Hashers;
2+
using System.Globalization;
3+
using Xunit;
4+
5+
namespace CasDotnetSdkTests.Tests
6+
{
7+
public class SHAWrapperTests
8+
{
9+
private readonly SHAWrapper _wrapper;
10+
private const string DataDirectory = "SHA/Data";
11+
private const string ProjectDataDirectory = "cas-dotnet-sdk-tests/SHA/Data";
12+
13+
public SHAWrapperTests()
14+
{
15+
this._wrapper = new SHAWrapper();
16+
}
17+
18+
[Theory]
19+
[MemberData(nameof(SHA256NistVectors))]
20+
public void SHA256MatchesNistVectors(byte[] message, byte[] expectedDigest)
21+
{
22+
byte[] hashed = this._wrapper.Hash256(message);
23+
Assert.Equal(expectedDigest, hashed);
24+
Assert.True(this._wrapper.Verify256(message, expectedDigest));
25+
}
26+
27+
[Theory]
28+
[MemberData(nameof(SHA512NistVectors))]
29+
public void SHA512MatchesNistVectors(byte[] message, byte[] expectedDigest)
30+
{
31+
byte[] hashed = this._wrapper.Hash512(message);
32+
Assert.Equal(expectedDigest, hashed);
33+
Assert.True(this._wrapper.Verify512(message, expectedDigest));
34+
}
35+
36+
public static IEnumerable<object[]> SHA256NistVectors()
37+
{
38+
return LoadRspVectors("SHA3_256ShortMsg.rsp")
39+
.Concat(LoadRspVectors("SHA3_256LongMsg.rsp"));
40+
}
41+
42+
public static IEnumerable<object[]> SHA512NistVectors()
43+
{
44+
return LoadRspVectors("SHA3_512ShortMsg.rsp")
45+
.Concat(LoadRspVectors("SHA3_512LongMsg.rsp"));
46+
}
47+
48+
private static IEnumerable<object[]> LoadRspVectors(string fileName)
49+
{
50+
string path = ResolveVectorPath(fileName);
51+
52+
string? msgHex = null;
53+
int? bitLength = null;
54+
55+
foreach (string rawLine in File.ReadLines(path))
56+
{
57+
string line = rawLine.Trim();
58+
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#", StringComparison.Ordinal))
59+
{
60+
continue;
61+
}
62+
63+
if (line.StartsWith("Len =", StringComparison.Ordinal))
64+
{
65+
bitLength = int.Parse(line["Len =".Length..].Trim(), CultureInfo.InvariantCulture);
66+
continue;
67+
}
68+
69+
if (line.StartsWith("Msg =", StringComparison.Ordinal))
70+
{
71+
msgHex = line["Msg =".Length..].Trim();
72+
continue;
73+
}
74+
75+
if (!line.StartsWith("MD =", StringComparison.Ordinal) || bitLength is null || msgHex is null)
76+
{
77+
continue;
78+
}
79+
80+
string digestHex = line["MD =".Length..].Trim();
81+
yield return new object[]
82+
{
83+
GetMessageBytes(msgHex, bitLength.Value),
84+
Convert.FromHexString(digestHex)
85+
};
86+
87+
msgHex = null;
88+
bitLength = null;
89+
}
90+
}
91+
92+
private static byte[] GetMessageBytes(string msgHex, int bitLength)
93+
{
94+
if (bitLength == 0)
95+
{
96+
return Array.Empty<byte>();
97+
}
98+
99+
if (bitLength % 8 != 0)
100+
{
101+
throw new InvalidOperationException($"Only byte-aligned NIST test vectors are supported. Found Len = {bitLength}.");
102+
}
103+
104+
byte[] fullMessage = Convert.FromHexString(msgHex);
105+
return fullMessage.Take(bitLength / 8).ToArray();
106+
}
107+
108+
private static string ResolveVectorPath(string fileName)
109+
{
110+
string[] candidatePaths =
111+
{
112+
Path.Combine(AppContext.BaseDirectory, DataDirectory, fileName),
113+
Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", ProjectDataDirectory, fileName)),
114+
Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..", ProjectDataDirectory, fileName))
115+
};
116+
117+
foreach (string candidatePath in candidatePaths)
118+
{
119+
if (File.Exists(candidatePath))
120+
{
121+
return candidatePath;
122+
}
123+
}
124+
125+
throw new FileNotFoundException(
126+
$"Missing NIST SHA vector file: {fileName}. Checked: {string.Join(", ", candidatePaths)}");
127+
}
128+
}
129+
}

cas-dotnet-sdk-tests/SHAWrapperTests.cs

Lines changed: 0 additions & 78 deletions
This file was deleted.

cas-dotnet-sdk-tests/cas-dotnet-sdk-tests.csproj

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,15 @@
2626
<ItemGroup>
2727
<ProjectReference Include="..\cas-dotnet-sdk\cas-dotnet-sdk.csproj" />
2828
</ItemGroup>
29+
30+
<ItemGroup>
31+
<Content Include="SHA\Data\*.rsp">
32+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
33+
<TargetPath>SHA\Data\%(Filename)%(Extension)</TargetPath>
34+
</Content>
35+
</ItemGroup>
36+
37+
<ItemGroup>
38+
<Folder Include="SHA\Data\" />
39+
</ItemGroup>
2940
</Project>

cas-dotnet-sdk/Hashers/SHAWrapper.cs

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,6 @@ public SHAWrapper()
2828

2929
public byte[] Hash512(byte[] dataToHash)
3030
{
31-
if (dataToHash == null)
32-
{
33-
throw new Exception("You must provide a byte array of data to hash");
34-
}
35-
if (dataToHash.Length == 0)
36-
{
37-
throw new Exception("You must provide a byte array with allocated data to hash");
38-
}
39-
4031
SHAHashByteResult hashedPtr = (this._platform == OSPlatform.Linux) ?
4132
SHALinuxWrapper.sha512_bytes(dataToHash, dataToHash.Length) :
4233
SHAWindowsWrapper.sha512_bytes(dataToHash, dataToHash.Length);
@@ -58,23 +49,12 @@ public byte[] Hash512(byte[] dataToHash)
5849

5950
public byte[] Hash256(byte[] dataToHash)
6051
{
61-
if (dataToHash == null)
62-
{
63-
throw new Exception("You must provide a byte array of data to hash");
64-
}
65-
if (dataToHash.Length == 0)
66-
{
67-
throw new Exception("You must provide a byte array with allocated data to hash");
68-
}
69-
7052
SHAHashByteResult hashedPtr = (this._platform == OSPlatform.Linux) ?
7153
SHALinuxWrapper.sha256_bytes(dataToHash, dataToHash.Length) :
7254
SHAWindowsWrapper.sha256_bytes(dataToHash, dataToHash.Length);
7355
byte[] result = new byte[hashedPtr.length];
7456
Marshal.Copy(hashedPtr.result_bytes_ptr, result, 0, hashedPtr.length);
7557
FreeMemoryHelper.FreeBytesMemory(hashedPtr.result_bytes_ptr);
76-
77-
7858
return result;
7959
}
8060

@@ -89,20 +69,9 @@ public byte[] Hash256(byte[] dataToHash)
8969

9070
public bool Verify512(byte[] dataToVerify, byte[] hashedData)
9171
{
92-
if (dataToVerify == null || dataToVerify.Length == 0)
93-
{
94-
throw new Exception("You must provide a byte array with allocated data to verify");
95-
}
96-
if (hashedData == null || hashedData.Length == 0)
97-
{
98-
throw new Exception("You must provide a byte array with allocated data to verify");
99-
}
100-
10172
bool result = (this._platform == OSPlatform.Linux) ?
10273
SHALinuxWrapper.sha512_bytes_verify(dataToVerify, dataToVerify.Length, hashedData, hashedData.Length) :
10374
SHAWindowsWrapper.sha512_bytes_verify(dataToVerify, dataToVerify.Length, hashedData, hashedData.Length);
104-
105-
10675
return result;
10776
}
10877

@@ -117,15 +86,6 @@ public bool Verify512(byte[] dataToVerify, byte[] hashedData)
11786

11887
public bool Verify256(byte[] dataToVerify, byte[] hashedData)
11988
{
120-
if (dataToVerify == null || dataToVerify.Length == 0)
121-
{
122-
throw new Exception("You must provide a byte array with allocated data to verify");
123-
}
124-
if (hashedData == null || hashedData.Length == 0)
125-
{
126-
throw new Exception("You must provide a byte array with allocated data to verify");
127-
}
128-
12989
bool result = (this._platform == OSPlatform.Linux) ?
13090
SHALinuxWrapper.sha256_bytes_verify(dataToVerify, dataToVerify.Length, hashedData, hashedData.Length) :
13191
SHAWindowsWrapper.sha256_bytes_verify(dataToVerify, dataToVerify.Length, hashedData, hashedData.Length);

cas-dotnet-sdk/cas-dotnet-sdk.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
88
<Title>cas-dotnet-sdk</Title>
99
<PackageIcon>icon.jpeg</PackageIcon>
10-
<Version>1.9.4</Version>
10+
<Version>1.9.5</Version>
1111
<Authors>Mike Mulchrone</Authors>
1212
<Description>A Nuget package that provides a implementation of the RustCrypto suite of cryptographic algorithms.</Description>
1313
<RepositoryUrl>https://github.com/Cryptographic-API-Services/cas-dotnet-sdk</RepositoryUrl>

0 commit comments

Comments
 (0)