Created
July 20, 2014 22:02
-
-
Save davisnw/991912f1a6fe2b63a73b to your computer and use it in GitHub Desktop.
Use native block size for pbkdf2 and increase iteration count.
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
/* | |
This is free and unencumbered software released into the public domain. | |
Anyone is free to copy, modify, publish, use, compile, sell, or | |
distribute this software, either in source code form or as a compiled | |
binary, for any purpose, commercial or non-commercial, and by any | |
means. | |
In jurisdictions that recognize copyright laws, the author or authors | |
of this software dedicate any and all copyright interest in the | |
software to the public domain. We make this dedication for the benefit | |
of the public at large and to the detriment of our heirs and | |
successors. We intend this dedication to be an overt act of | |
relinquishment in perpetuity of all present and future rights to this | |
software under copyright law. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
OTHER DEALINGS IN THE SOFTWARE. | |
For more information, please refer to <http://unlicense.org/> | |
*/ | |
using System; | |
using System.Diagnostics; | |
using System.Security.Cryptography; | |
namespace Pbkdf2Perf | |
{ | |
/// <summary> | |
/// As mentioned in the comments of http://security.stackexchange.com/questions/17994/with-pbkdf2-what-is-an-optimal-hash-size-in-bytes-what-about-the-size-of-the-s, | |
/// and http://security.stackexchange.com/a/51430/28362 with related info https://hashcat.net/forum/thread-2238.html, when using pbkdf2 for password hashing, the configured | |
/// size of the output hash should *not* exceed the native block size of the underlying hash algorithm. | |
/// | |
/// The reason is that this only harms you, the defender, since only you experience the slowdown. The attacker only has to check the first block, but you the defender | |
/// have to compute all the blocks. | |
/// | |
/// Instead, for password hashing, use the native block size, but increase the pbkdf2 iteration count, which makes the attacker's job harder. | |
/// | |
/// This program demonstrates the slowdown experienced by the defender solely as a result of using block sizes greater than native. | |
/// </summary> | |
class BlockSizeTiming | |
{ | |
static void Main(string[] args) | |
{ | |
int blockSize = 20; //.Net implementation of Rfc2898DeriveBytes currently uses SHA1, which has a block size of 20. | |
int iterationCount = 1000; | |
int saltSize = 32; | |
int benchmarkRepetitions = 4; | |
string password = Guid.NewGuid().ToString(); | |
double blockSizeTicks = (double)Benchmark(password, blockSize, iterationCount, saltSize, benchmarkRepetitions); | |
for (int i = 2; i <= 10000; ++i) | |
{ | |
int outputBytes = blockSize * i; | |
var elapsedTicks = (double)Benchmark(password, outputBytes, iterationCount, saltSize, benchmarkRepetitions); | |
Console.WriteLine(string.Format("OutputBytes {0} \t ElapsedTicks {1} \t SlowFactor {2}", outputBytes, elapsedTicks, elapsedTicks / blockSizeTicks)); | |
} | |
} | |
static long Benchmark(string password, int pbkdf2OutputBytes, int pbkdf2IterationCount, int pbkdf2SaltSize, int benchmarkRepetitions) | |
{ | |
var stopwatch = new Stopwatch(); | |
stopwatch.Start(); | |
for (int i = 0; i < benchmarkRepetitions; ++i) | |
{ | |
RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider(); | |
byte[] salt = new byte[pbkdf2SaltSize]; | |
csprng.GetBytes(salt); | |
Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt); | |
pbkdf2.IterationCount = pbkdf2IterationCount; | |
pbkdf2.GetBytes(pbkdf2OutputBytes); | |
} | |
stopwatch.Stop(); | |
return stopwatch.ElapsedTicks; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment