Last active
November 30, 2023 18:09
-
-
Save akamsteeg/fe04d4b4909fae23d2b8 to your computer and use it in GitHub Desktop.
Speed of different hashing algorithms
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Security.Cryptography; | |
using System.Diagnostics; | |
using System.Text; | |
// Compile in the VS Developer Command Prompt with: csc /warnaserror+ /optimize+ hasingspeed.cs | |
// This is just a demonstration. :) | |
// Sample output: | |
// Time to calculate one hash: | |
// - MD5: 0.152 sec. | |
// - SHA1: 0.006 sec. | |
// - SHA256: 0.006 sec. | |
// - SHA384: 0.007 sec. | |
// - SHA512: 0.006 sec. | |
// - RipeMD160: 0.024 sec. | |
// - PBKDF2_1000: 0.023 sec. | |
// - PBKDF2_10000: 0.044 sec. | |
// - PBKDF2_20000: 0.089 sec. | |
// - PBKDF2_40000: 0.177 sec. | |
// - PBKDF2_80000: 0.341 sec. | |
// - PBKDF2_120000: 0.549 sec. | |
// | |
// Hashes per second: | |
// - MD5: 185,185.2 hashes/sec. | |
// - SHA1: 1,250,000.0 hashes/sec. | |
// - SHA256: 833,333.3 hashes/sec. | |
// - SHA384: 714,285.7 hashes/sec. | |
// - SHA512: 714,285.7 hashes/sec. | |
// - RipeMD160: 1,250,000.0 hashes/sec. | |
// - PBKDF2_1000: 217.0 hashes/sec. | |
// - PBKDF2_10000: 21.5 hashes/sec. | |
// - PBKDF2_20000: 11.0 hashes/sec. | |
// - PBKDF2_40000: 5.4 hashes/sec. | |
// - PBKDF2_80000: 2.6 hashes/sec. | |
// - PBKDF2_120000: 1.8 hashes/sec. | |
// Alex Kamsteeg (http://atlex.nl/) | |
internal class Program | |
{ | |
public static void Main(string[] args) | |
{ | |
byte[] hash = BytesToHash(16); | |
byte[] password = Encoding.UTF8.GetBytes("e7K7ZB$z"); | |
const int maxHashes = 5000; | |
HashFormat[] formatsToTest = new[] | |
{ | |
HashFormat.MD5, | |
HashFormat.SHA1, | |
HashFormat.SHA256, | |
HashFormat.SHA384, | |
HashFormat.SHA512, | |
HashFormat.RipeMD160, | |
HashFormat.PBKDF2_1000, | |
HashFormat.PBKDF2_10000, | |
HashFormat.PBKDF2_20000, | |
HashFormat.PBKDF2_40000, | |
HashFormat.PBKDF2_80000, | |
HashFormat.PBKDF2_120000, | |
}; | |
Stopwatch sw = new Stopwatch(); | |
Console.WriteLine("Time to calculate one hash:"); | |
for(int f = 0; f < formatsToTest.Length; f++) | |
{ | |
Console.Write("- {0}: ", formatsToTest[f]); | |
sw.Restart(); | |
HashBytes(hash, password, formatsToTest[f]); | |
sw.Stop(); | |
Console.WriteLine("{0:N3} sec.", sw.ElapsedMilliseconds/1000.0); | |
} | |
Console.WriteLine(); | |
Console.WriteLine("Hashes per second:"); | |
for(int f = 0; f < formatsToTest.Length; f++) | |
{ | |
Console.Write("- {0}: ", formatsToTest[f]); | |
sw.Restart(); | |
for (int i = 0; i < maxHashes; i++) | |
HashBytes(hash, password, formatsToTest[f]); | |
sw.Stop(); | |
Console.WriteLine("{0:N1} hashes/sec.", maxHashes/(sw.ElapsedMilliseconds/1000.0)); | |
} | |
} | |
private static byte[] BytesToHash(int numberOfBytes) | |
{ | |
byte[] bytesToHash = new byte[numberOfBytes]; | |
new RNGCryptoServiceProvider().GetNonZeroBytes(bytesToHash); | |
return bytesToHash; | |
} | |
private static byte[] HashBytes(byte[] hash, byte[] password, HashFormat format) | |
{ | |
byte[] result = null; | |
switch (format) | |
{ | |
case HashFormat.PBKDF2_1000: | |
case HashFormat.PBKDF2_10000: | |
case HashFormat.PBKDF2_20000: | |
case HashFormat.PBKDF2_40000: | |
case HashFormat.PBKDF2_80000: | |
case HashFormat.PBKDF2_120000: | |
{ | |
int iterations = Convert.ToInt32(format.ToString().Split('_')[1]); | |
var pbkdf2 = new Rfc2898DeriveBytes(password, hash, iterations); | |
result = pbkdf2.GetBytes(20); | |
break; | |
} | |
case HashFormat.RipeMD160: | |
case HashFormat.SHA512: | |
case HashFormat.SHA384: | |
case HashFormat.SHA256: | |
case HashFormat.SHA1: | |
case HashFormat.MD5: | |
{ | |
// Create a buffer of buffer.Length * 2 and fill it to | |
// make sure we're hashing salted (just like PBKDF2) | |
var saltAndPassword = new byte[hash.Length + password.Length]; | |
Buffer.BlockCopy(hash, 0, saltAndPassword, 0, hash.Length); | |
Buffer.BlockCopy(password, 0, saltAndPassword, hash.Length, password.Length); | |
HashAlgorithm algorithm = null; | |
if (format == HashFormat.RipeMD160) | |
algorithm = new RIPEMD160Managed(); | |
else if (format == HashFormat.SHA512) | |
algorithm = new SHA512Managed(); | |
else if (format == HashFormat.SHA384) | |
algorithm = new SHA384Managed(); | |
else if (format == HashFormat.SHA256) | |
algorithm = new SHA256Managed(); | |
else if (format == HashFormat.SHA1) | |
algorithm = new SHA1Managed(); | |
else if (format == HashFormat.MD5) | |
algorithm = MD5.Create(); | |
//Console.WriteLine(algorithm.GetType().Name); | |
using (algorithm) | |
{ | |
result = algorithm.ComputeHash(saltAndPassword); | |
} | |
break; | |
} | |
default: | |
throw new InvalidOperationException(); | |
} | |
return result; | |
} | |
} | |
internal enum HashFormat | |
{ | |
MD5, | |
SHA1, | |
SHA256, | |
SHA384, | |
SHA512, | |
RipeMD160, | |
PBKDF2_1000, | |
PBKDF2_10000, | |
PBKDF2_20000, | |
PBKDF2_40000, | |
PBKDF2_80000, | |
PBKDF2_120000, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment