Skip to content

Instantly share code, notes, and snippets.

@davisnw
Created July 20, 2014 22:02
Show Gist options
  • Save davisnw/991912f1a6fe2b63a73b to your computer and use it in GitHub Desktop.
Save davisnw/991912f1a6fe2b63a73b to your computer and use it in GitHub Desktop.
Use native block size for pbkdf2 and increase iteration count.
/*
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