private static void CalculateHash_ShouldNotBeDeterministic(HashingAlgorithmSpecification algorithm, SaltingMode saltingMode) => CalculateHash_ValidateDeterminism(algorithm, saltingMode, false);
private static void CalculateHash_ValidateDeterminism(HashingAlgorithmSpecification algorithm, SaltingMode saltingMode, Boolean shouldBeDeterministic) { using (var randomnessProvider = RandomNumberGenerator.Create()) { // Arrange. var target = new HashingStringProcessor(randomnessProvider); var plaintextObject = "䆟`ಮ䷆ʘ‣⦸⏹ⰄͶa✰ṁ亡Zᨖ0༂⽔9㗰"; // Act. var firstHashValue = target.CalculateHash(plaintextObject, algorithm, saltingMode); // Assert. firstHashValue.Should().NotBeNullOrEmpty(); firstHashValue.Count(value => value == 0x00).Should().NotBe(firstHashValue.Length); // Act. var secondHashValue = target.CalculateHash(plaintextObject, algorithm, saltingMode); // Assert. secondHashValue.Should().NotBeNullOrEmpty(); secondHashValue.Length.Should().Be(firstHashValue.Length); secondHashValue.Count(value => value == 0x00).Should().NotBe(secondHashValue.Length); if (shouldBeDeterministic) { // Assert. firstHashValue.ComputeThirtyTwoBitHash().Should().Be(secondHashValue.ComputeThirtyTwoBitHash()); } else { // Assert. firstHashValue.ComputeThirtyTwoBitHash().Should().NotBe(secondHashValue.ComputeThirtyTwoBitHash()); } } }
private static void CalculateHash_ValidateDeterminism(HashingAlgorithmSpecification algorithm, SaltingMode saltingMode, Boolean shouldBeDeterministic) { using (var randomnessProvider = RandomNumberGenerator.Create()) { // Arrange. var binarySerializer = new BinaryPassThroughSerializer(); var target = new HashingProcessor <Byte[]>(randomnessProvider, binarySerializer); var plaintextObject = new Byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 }; // Act. var firstHashValue = target.CalculateHash(plaintextObject, algorithm, saltingMode); // Assert. firstHashValue.Should().NotBeNullOrEmpty(); firstHashValue.Count(value => value == 0x00).Should().NotBe(firstHashValue.Length); ValidateDigestLength(firstHashValue, algorithm, saltingMode, target.SaltLengthInBytes); // Act. var secondHashValue = target.CalculateHash(plaintextObject, algorithm, saltingMode); // Assert. secondHashValue.Should().NotBeNullOrEmpty(); secondHashValue.Length.Should().Be(firstHashValue.Length); secondHashValue.Count(value => value == 0x00).Should().NotBe(secondHashValue.Length); if (shouldBeDeterministic) { // Assert. firstHashValue.ComputeThirtyTwoBitHash().Should().Be(secondHashValue.ComputeThirtyTwoBitHash()); } else { // Assert. firstHashValue.ComputeThirtyTwoBitHash().Should().NotBe(secondHashValue.ComputeThirtyTwoBitHash()); } } }
private static void ValidateDigestLength(Byte[] hashValue, HashingAlgorithmSpecification algorithm, SaltingMode saltingMode, Int32 saltLengthInBytes) { switch (algorithm) { case HashingAlgorithmSpecification.Md5: hashValue.Length.Should().Be(16 + (saltingMode == SaltingMode.Salted ? saltLengthInBytes : 0)); break; case HashingAlgorithmSpecification.ShaTwo256: hashValue.Length.Should().Be(32 + (saltingMode == SaltingMode.Salted ? saltLengthInBytes : 0)); break; case HashingAlgorithmSpecification.ShaTwo384: hashValue.Length.Should().Be(48 + (saltingMode == SaltingMode.Salted ? saltLengthInBytes : 0)); break; case HashingAlgorithmSpecification.ShaTwo512: hashValue.Length.Should().Be(64 + (saltingMode == SaltingMode.Salted ? saltLengthInBytes : 0)); break; default: Assert.Fail(); return; } }
private static void EvaluateHash_ShouldProduceDesiredResults(HashingAlgorithmSpecification algorithm, SaltingMode saltingMode) { using (var randomnessProvider = RandomNumberGenerator.Create()) { // Arrange. var binarySerializer = new BinaryPassThroughSerializer(); var target = new HashingProcessor <Byte[]>(randomnessProvider, binarySerializer); var plaintextObject = new Byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 }; var matchingHashValue = target.CalculateHash(plaintextObject, algorithm, saltingMode); var nonMatchingHashValue = matchingHashValue.PerformCircularBitShift(Core.BitShiftDirection.Left, 16); // Act. var resultOne = target.EvaluateHash(matchingHashValue, plaintextObject, algorithm, saltingMode); var resultTwo = target.EvaluateHash(nonMatchingHashValue, plaintextObject, algorithm, saltingMode); // Assert. resultOne.Should().BeTrue(); resultTwo.Should().BeFalse(); ValidateDigestLength(matchingHashValue, algorithm, saltingMode, target.SaltLengthInBytes); } }
/// <summary> /// Calculates a hash value for the specified plaintext object and compares the result with the specified hash value. /// </summary> /// <param name="hash"> /// The hash value to evaluate. /// </param> /// <param name="plaintextObject"> /// The plaintext object to evaluate. /// </param> /// <param name="algorithm"> /// The algorithm specification used to transform the plaintext. /// </param> /// <param name="saltingMode"> /// A value specifying whether or not salt is applied to the plaintext. /// </param> /// <returns> /// <see langword="true" /> if the resulting hash value matches <paramref name="hash" />, otherwise <see langword="false" />. /// </returns> /// <exception cref="SecurityException"> /// An exception was raised during hashing or serialization. /// </exception> public Boolean EvaluateHash(Byte[] hash, T plaintextObject, HashingAlgorithmSpecification algorithm, SaltingMode saltingMode) { try { var digestLength = (algorithm.ToDigestBitLength() / 8); Byte[] processedHash; Byte[] calculatedHash; switch (saltingMode) { case SaltingMode.Salted: var salt = new Byte[SaltLengthInBytes]; processedHash = hash.Take(digestLength).ToArray(); salt = hash.Skip(digestLength).Take(SaltLengthInBytes).ToArray(); calculatedHash = CalculateHash(plaintextObject, algorithm, salt).Take(digestLength).ToArray(); break; default: processedHash = hash; calculatedHash = CalculateHash(plaintextObject, algorithm, null); break; } if (processedHash.Length != digestLength) { return(false); } else if (calculatedHash.Length != digestLength) { return(false); } for (var i = 0; i < digestLength; i++) { if (processedHash[i] == calculatedHash[i]) { continue; } return(false); } return(true); } catch { throw new SecurityException("The hashing operation failed."); } }
/// <summary> /// Calculates a hash value for the specified plaintext object. /// </summary> /// <param name="plaintextObject"> /// The plaintext object to hash. /// </param> /// <param name="algorithm"> /// The algorithm specification used to transform the plaintext. /// </param> /// <param name="saltingMode"> /// A value specifying whether or not salt is applied to the plaintext. /// </param> /// <returns> /// The resulting hash value. /// </returns> /// <exception cref="SecurityException"> /// An exception was raised during hashing or serialization. /// </exception> public Byte[] CalculateHash(T plaintextObject, HashingAlgorithmSpecification algorithm, SaltingMode saltingMode) { try { switch (saltingMode) { case SaltingMode.Salted: var salt = new Byte[SaltLengthInBytes]; RandomnessProvider.GetBytes(salt); return(CalculateHash(plaintextObject, algorithm, salt)); default: return(CalculateHash(plaintextObject, algorithm, null)); } } catch { throw new SecurityException("The hashing operation failed."); } }