Mercurial > projects > dcrypt
view dcrypt/crypto/prngs/PBKDF2.d @ 15:0de48552be35
Added LimitReachedError and PBKDF2. Fixed some errors with the previous commit in PRNGFromHash, etc. Re-implemented HMAC. Changed the name() format of HMAC and PBKDF2.
author | Thomas Dixon <reikon@reikon.us> |
---|---|
date | Wed, 19 Nov 2008 19:30:52 -0500 |
parents | |
children | 4589f8c5eb3c |
line wrap: on
line source
/** * This file is part of the dcrypt project. * * Copyright: Copyright (C) dcrypt contributors 2008. All rights reserved. * License: MIT * Authors: Thomas Dixon */ module dcrypt.crypto.prngs.PBKDF2; import dcrypt.misc.Util; import dcrypt.crypto.PRNG; import dcrypt.crypto.MAC; import dcrypt.crypto.macs.HMAC; import dcrypt.crypto.hashes.SHA1; import dcrypt.crypto.errors.LimitReachedError; /** * Implementation of RSA Security's Password-Based Key Derivation Function 2 * * Conforms: PKCS #5 v2.0 / RFC 2898 * References: http://www.truecrypt.org/docs/pkcs5v2-0.pdf */ class PBKDF2 : PRNG { private { ubyte[] salt, buffer; char[] password; MAC prf; uint iterations, blockCount, index; } /** * Params: * password = User supplied password * salt = (preferably random) salt * iterations = The number of total iterations * prf = The pseudo-random function */ this(char[] password, void[] salt_, uint iterations=1000, MAC prf=new HMAC(new SHA1)) { salt = cast(ubyte[])salt_; if (salt == null) throw new InvalidParameterError(name()~": No salt specified."); this.password = password; if (this.password == null) throw new InvalidParameterError(name()~": No password specified."); this.prf = prf; if (this.prf is null) throw new InvalidParameterError(name()~": No PRF specified."); this.iterations = iterations; prf.init(new SymmetricKey(cast(ubyte[])this.password)); blockCount = 0; buffer = new ubyte[this.prf.macSize]; index = this.prf.macSize; _initialized = true; } void addEntropy(ubyte[] input) { throw new NotSupportedError(name()~": Not supported."); } /** * Throws: LimitReachedError after 2^32 blocks. */ uint read(ubyte[] output) { for (uint i = 0; i < output.length; i++) { if (index == buffer.length) { if (++blockCount == 0) // Catch rollover throw new LimitReachedError(name()~": Output limit reached."); buffer[] = 0; ubyte[] t = new ubyte[salt.length + uint.sizeof]; t[0..salt.length] = salt; Util.uintToUbytesBig(blockCount, t, salt.length); for (uint j = 0; j < iterations; j++) { prf.reset(); prf.update(t); t = prf.digest(); for (uint k = 0; k < buffer.length; k++) buffer[k] ^= t[k]; } index = 0; } output[i] = buffer[index++]; } return output.length; } char[] name() { return "PBKDF2-"~prf.name; } version (UnitTest) { unittest { static char[][] test_passwords = [ "password", "password", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"~ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"~ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ]; static char[][] test_salts = [ "ATHENA.MIT.EDUraeburn", "ATHENA.MIT.EDUraeburn", "pass phrase equals block size", "pass phrase exceeds block size" ]; static const int[] test_iterations = [ 1, 1200, 1200, 1200 ]; static const char[][] test_results = [ "cdedb5281bb2f801565a1122b2563515", "5c08eb61fdf71e4e4ec3cf6ba1f5512b"~ "a7e52ddbc5e5142f708a31e2e62b1e13", "139c30c0966bc32ba55fdbf212530ac9"~ "c5ec59f1a452f5cc9ad940fea0598ed1", "9ccad6d468770cd51b10e6a68721be61"~ "1a8b4d282601db3b36be9246915ec82a" ]; PBKDF2 pbkdf2; foreach (uint i, char[] p; test_passwords) { pbkdf2 = new PBKDF2(p, test_salts[i], test_iterations[i]); ubyte[] result = new ubyte[test_results[i].length >> 1]; pbkdf2.read(result); char[] hexResult = Util.ubytesToHex(result); assert(hexResult == test_results[i], pbkdf2.name~": ("~hexResult~") != ("~test_results[i]~")"); } } } }