Initial commit: scadaproj umbrella — sister-project index, auth component normalization (design + GAPS), and the built ZB.MOM.WW.Auth shared library (0.1.0, flattened in).

This commit is contained in:
dohertj2
2026-06-01 03:59:23 -04:00
commit 37e23cf9f2
73 changed files with 6836 additions and 0 deletions
@@ -0,0 +1,99 @@
using ZB.MOM.WW.Auth.ApiKeys;
namespace ZB.MOM.WW.Auth.ApiKeys.Tests;
public class ApiKeySecretHasherTests
{
private const string Secret = "mysecret";
private const string Pepper = "mypepper";
// --- Hash determinism ---
[Fact]
public void Hash_SameInputs_ProducesIdenticalHashes()
{
byte[] first = ApiKeySecretHasher.Hash(Secret, Pepper);
byte[] second = ApiKeySecretHasher.Hash(Secret, Pepper);
Assert.Equal(first, second);
}
[Fact]
public void Hash_DifferentPepper_ProducesDifferentHash()
{
byte[] withPepper1 = ApiKeySecretHasher.Hash(Secret, "pepper1");
byte[] withPepper2 = ApiKeySecretHasher.Hash(Secret, "pepper2");
Assert.NotEqual(withPepper1, withPepper2);
}
[Fact]
public void Hash_DifferentSecret_ProducesDifferentHash()
{
byte[] hash1 = ApiKeySecretHasher.Hash("secret1", Pepper);
byte[] hash2 = ApiKeySecretHasher.Hash("secret2", Pepper);
Assert.NotEqual(hash1, hash2);
}
[Fact]
public void Hash_ReturnsThirtyTwoBytes()
{
// HMAC-SHA256 output is 256 bits = 32 bytes
byte[] hash = ApiKeySecretHasher.Hash(Secret, Pepper);
Assert.Equal(32, hash.Length);
}
// --- Verify happy path ---
[Fact]
public void Verify_CorrectSecretAndPepper_ReturnsTrue()
{
byte[] hash = ApiKeySecretHasher.Hash(Secret, Pepper);
Assert.True(ApiKeySecretHasher.Verify(Secret, Pepper, hash));
}
[Fact]
public void Verify_WrongSecret_ReturnsFalse()
{
byte[] hash = ApiKeySecretHasher.Hash(Secret, Pepper);
Assert.False(ApiKeySecretHasher.Verify("wrongsecret", Pepper, hash));
}
[Fact]
public void Verify_WrongPepper_ReturnsFalse()
{
byte[] hash = ApiKeySecretHasher.Hash(Secret, Pepper);
Assert.False(ApiKeySecretHasher.Verify(Secret, "wrongpepper", hash));
}
// --- Constant-time: length mismatch must not throw ---
[Fact]
public void Verify_HashOfDifferentLength_ReturnsFalseWithoutThrowing()
{
// A hash of a completely different length — FixedTimeEquals must handle it
// without throwing and return false.
byte[] shortHash = [1, 2, 3];
var exception = Record.Exception(() => ApiKeySecretHasher.Verify(Secret, Pepper, shortHash));
Assert.Null(exception);
Assert.False(ApiKeySecretHasher.Verify(Secret, Pepper, shortHash));
}
[Fact]
public void Verify_EmptyHash_ReturnsFalseWithoutThrowing()
{
byte[] emptyHash = [];
var exception = Record.Exception(() => ApiKeySecretHasher.Verify(Secret, Pepper, emptyHash));
Assert.Null(exception);
Assert.False(ApiKeySecretHasher.Verify(Secret, Pepper, emptyHash));
}
}