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)); } }