Mercurial > projects > dcrypt
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dcrypt/crypto/prngs/PBKDF2.d Wed Nov 19 19:30:52 2008 -0500 @@ -0,0 +1,150 @@ +/** + * 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]~")"); + } + } + } +} \ No newline at end of file