changeset 0:0e08791a1418

Initial import.
author Thomas Dixon <reikon@reikon.us>
date Sun, 10 Aug 2008 14:20:17 -0400
parents
children 483e4467b5f6
files CONTRIBUTORS LICENSE dcrypt/crypto/BlockCipher.d dcrypt/crypto/BlockCipherPadding.d dcrypt/crypto/Cipher.d dcrypt/crypto/Crypto.d dcrypt/crypto/Hash.d dcrypt/crypto/StreamCipher.d dcrypt/crypto/SymmetricCipher.d dcrypt/crypto/ciphers/RC4.d dcrypt/crypto/ciphers/RC6.d dcrypt/crypto/ciphers/TEA.d dcrypt/crypto/ciphers/XTEA.d dcrypt/crypto/errors/InvalidKeyError.d dcrypt/crypto/errors/InvalidPaddingError.d dcrypt/crypto/errors/InvalidParameterError.d dcrypt/crypto/errors/NotInitializedError.d dcrypt/crypto/errors/ShortBufferError.d dcrypt/crypto/hashes/MD5.d dcrypt/crypto/hashes/SHA1.d dcrypt/crypto/hashes/SHA224.d dcrypt/crypto/hashes/SHA256.d dcrypt/crypto/hashes/SHA384.d dcrypt/crypto/hashes/SHA512.d dcrypt/crypto/modes/CBC.d dcrypt/crypto/modes/CTR.d dcrypt/crypto/padding/NullByte.d dcrypt/crypto/padding/PKCS7.d dcrypt/crypto/padding/RFC1321.d dcrypt/crypto/padding/X923.d dcrypt/crypto/params/CipherParameters.d dcrypt/crypto/params/ParametersWithIV.d dcrypt/crypto/params/SymmetricKey.d dcrypt/misc/Util.d dsss.conf
diffstat 35 files changed, 2879 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CONTRIBUTORS	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,1 @@
+Thomas Dixon <reikon@reikon.us>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENSE	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,23 @@
+Copyright (c) 2008 dcrypt contributors.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+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 OR COPYRIGHT
+HOLDERS 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.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/BlockCipher.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,38 @@
+/**
+ * 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.BlockCipher;
+
+public import dcrypt.crypto.Cipher;
+public import dcrypt.crypto.params.SymmetricKey;
+
+/** Interface for a standard block cipher. */
+abstract class BlockCipher : Cipher {
+    
+    /** Returns: The block size in bytes that this cipher will operate on. */
+    uint blockSize();
+    
+    /**
+     * Process a block of data from the input array
+     * and place it into the output array.
+     *
+     * Params:
+     *     input_  = Array containing input data.
+     *     inOff   = Offset at where the data in input_ starts.
+     *     output_ = Array which will hold the output data.
+     *     outOff  = Offset at which to begin placing data in output_.
+     *
+     * Returns: The number of bytes processed (typically blockSize).
+     *
+     * Throws: dcrypt.crypto.errors.NotInitializedError if cipher
+     *         was not initialized.
+     */
+    uint processBlock(void[] input_, 
+                          uint inOff, void[] output_, uint outOff);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/BlockCipherPadding.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,41 @@
+/**
+ * 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.BlockCipherPadding;
+ 
+ public import dcrypt.crypto.errors.InvalidPaddingError;
+ 
+ /** Base padding class for implementing block padding schemes. */
+ abstract class BlockCipherPadding {
+     /** Returns: The name of the padding scheme implemented. */
+     char[] name();
+     
+     /**
+      * Pad the (last) block of plaintext to block length.
+      *
+      * Params:
+      *     input_ = Plaintext block to be padded.
+      *     inOffset = Offset at which to begin padding input_.
+      *
+      * Returns: The number of padding bytes added.
+      */ 
+     uint padBlock(void[] input_, uint inOff);
+     
+     /**
+      * Return the number of pad bytes in the block.
+      *
+      * Params:
+      *     input_ = Padded block of which to count the pad bytes.
+      *
+      * Returns: The number of pad bytes in the block.
+      *
+      * Throws: dcrypt.crypto.errors.InvalidPaddingError if 
+      *         pad length cannot be discerned.
+      */
+     uint padLength(void[] input_);
+ }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/Cipher.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,38 @@
+/**
+ * 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.Cipher;
+
+public import dcrypt.crypto.errors.InvalidKeyError;
+public import dcrypt.crypto.errors.ShortBufferError;
+public import dcrypt.crypto.errors.NotInitializedError;
+public import dcrypt.crypto.errors.InvalidParameterError;
+
+public import dcrypt.crypto.params.CipherParameters;
+
+/** Base cipher class */
+interface Cipher {
+    static const bool ENCRYPT = true,
+                      DECRYPT = false;
+    
+    /**
+     * Initialize a cipher.
+     * 
+     * Params:
+     *     encrypt = True if we are encrypting.
+     *     params  = Parameters to be passed to the cipher. (Key, rounds, etc.)
+     */
+    abstract void init(bool encrypt, CipherParameters params);
+    
+    /** Returns: The name of the algorithm of this cipher. */
+    abstract char[] name();
+    
+    /** Reset cipher to its state immediately subsequent the last init. */
+    abstract void reset();
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/Crypto.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,43 @@
+/**
+ * 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.Crypto;
+
+import dcrypt.crypto.Cipher;
+
+import dcrypt.misc.Util;
+
+// Hash functions
+public import dcrypt.crypto.Hash;
+import dcrypt.crypto.hashes.MD5;
+import dcrypt.crypto.hashes.SHA1;
+import dcrypt.crypto.hashes.SHA224;
+import dcrypt.crypto.hashes.SHA256;
+import dcrypt.crypto.hashes.SHA384;
+import dcrypt.crypto.hashes.SHA512;
+
+struct Crypto {
+    static Hash hashByName(char[] name) {
+        switch (Util.stringToAlphanumeric(Util.stringToUpper(name))) {
+            case "MD5":
+                return new MD5();
+            case "SHA1":
+                return new SHA1();
+            case "SHA224":
+                return new SHA224();
+            case "SHA256":
+                return new SHA256();
+            case "SHA384":
+                return new SHA384();
+            case "SHA512":
+                return new SHA512();
+            default:
+                throw new InvalidParameterError("Unknown hash function: "~name);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/Hash.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,110 @@
+/**
+ * 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.Hash;
+
+public import dcrypt.misc.Util;
+
+/** Base class for all cryptographic hash functions */
+class Hash {
+	private const enum {
+		MODE_MD=0, // MDx, RipeMD, etc
+		MODE_SHA,
+		MODE_TIGER
+	}
+	private ubyte[] buffer;
+	uint bytes,
+		 index;
+	
+	this (void[] input_=null) {
+		buffer = new ubyte[blockSize];
+        ubyte[] input = cast(ubyte[]) input_;
+        if (input)
+            update(input);
+	}
+
+	/** Returns: The block size of the hash function in bytes. */
+	abstract uint blockSize();
+	
+	/** Returns: The output size of the hash function in bytes. */
+	abstract uint digestSize();
+	
+	/** Returns: The name of the algorithm we're implementing. */
+	abstract char[] name();
+	
+	/**
+	 * Introduce data into the hash function.
+	 * 
+	 * Params:
+	 *     input_ = Data to be processed.
+	 */
+	void update(void[] input_) {
+		ubyte[] input = cast(ubyte[]) input_;
+		foreach (ubyte i; input)
+			update(i);
+	}
+	
+	void update(ubyte input) {
+		bytes++;
+		buffer[index++] = input;
+		if (index == blockSize) {
+			transform(buffer);
+			index = 0;
+		}
+	}
+	
+	/** Hash function's internal transformation. */
+	protected abstract void transform(ubyte[] input);
+    
+	/** 
+	 * Pad message in the respective manner.
+	 * 
+	 * Params:
+	 *     mode = Mode constant dictating in which manner
+	 *            to pad the message.
+	 */
+	protected void padMessage(uint mode) {
+		ulong length = bytes << 3;
+		
+		update(((mode == MODE_TIGER) ? 0x01 : 0x80));
+        uint count = (blockSize-(blockSize >> 3));
+        if (index > count)
+            while (index != 0)
+                    update(0);
+        while (index < (count + ((blockSize == 128) ? 8 : 0)))
+            update(0);
+        if (mode == MODE_SHA)
+            for (int i = 56; i >= 0; i-=8) // big endian
+                update(length >> i);
+        else
+            for (int i = 0; i < 64; i+=8) // little endian
+                update(length >> i);
+    }
+    
+    /**
+     * Process all data, pad and finalize. This method will
+     * reset the digest to its original state ofr subsequent use.
+     * 
+     * Returns: Binary representation of the hash in bytes.
+     */
+    abstract ubyte[] digest();
+    
+    /**
+     * Same as digest() but returns hash value in hex.
+     * 
+     * Returns: Representation of the final hash value in hex.
+     */
+    char[] hexDigest() {
+        return Util.ubytesToHex(digest());
+    }
+    
+    /** Reset hash to initial state. */
+    void reset() {
+        bytes = index = 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/StreamCipher.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,43 @@
+/**
+ * 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.StreamCipher;
+
+public import dcrypt.crypto.Cipher;
+public import dcrypt.crypto.params.CipherParameters;
+public import dcrypt.crypto.params.SymmetricKey;
+
+/** Interface for a standard stream cipher. */
+class StreamCipher : Cipher {   
+    /**
+     * Process one byte of input.
+     *
+     * Params:
+     *     input = Byte to XOR with keystream.
+     *
+     * Returns: One byte of input XORed with the keystream.
+     */
+    abstract ubyte returnByte(ubyte input);
+    
+    /**
+     * Process data from the input array
+     * and place it into the output array.
+     *
+     * Params:
+     *     input_  = Array containing input data.
+     *     inOff   = Offset at where the data in input_ starts.
+     *     len     = Length of input_ to process.
+     *     output_ = Array which will hold the output data.
+     *     outOff  = Offset at which to begin placing data in output_.
+     *
+     * Throws: dcrypt.crypto.errors.NotInitializedError if cipher
+     *         was not initialized.
+     */
+    abstract void processBytes(void[] input_, uint inOff, 
+                                   uint len, void[] output_, uint outOff);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/SymmetricCipher.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,32 @@
+/**
+ * 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.SymmetricCipher;
+
+import dcrypt.crypto.Cipher;
+import dcrypt.crypto.params.CipherParameters;
+
+/** Unified cipher class for high-level API. */
+interface SymmetricCipher : Cipher {
+    /** 
+     * Pass bytes through the cipher object.
+     * 
+     * Params:
+     *     input_  = Array containing input data.
+     *     inOff   = Offset at where the data in input_ starts.
+     *     len     = Length of input_ to process.
+     *     output_ = Array which will hold the output data.
+     *     outOff  = Offset at which to begin placing data in output_.
+     *
+     * Returns: The amount of bytes processed.
+     */
+    uint update(void[] input_, uint inOff, uint len, void[] output_, uint outOff);
+    
+    /** Finalize and output the rest of the buffer. */
+    uint finish(void[] output_, uint outOff);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/ciphers/RC4.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,203 @@
+/**
+ * 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.ciphers.RC4;
+
+import dcrypt.crypto.StreamCipher;
+
+version (UnitTest) {
+    import dcrypt.misc.Util;
+}
+
+/** Implementation of RC4 designed by Ron Rivest of RSA Security. */
+class RC4 : StreamCipher {
+    private ubyte[] state,
+                    workingKey;
+    private ubyte x, y;
+    private bool initialized;
+    
+    void init(bool encrypt, CipherParameters params) {
+        SymmetricKey keyParams = cast(SymmetricKey)params;
+        if (!keyParams)
+            throw new InvalidParameterError(
+                    name()~": Invalid parameter passed to init");
+        if (keyParams.key.length < 0 || keyParams.key.length > 256)
+            throw new InvalidKeyError(
+                name()~": Invalid key length (requires 1-256 bytes)");
+        workingKey = keyParams.key;
+        state = new ubyte[256];
+        setup(workingKey);
+        initialized = true;
+    }
+    
+    char[] name() {
+        return "RC4";
+    }
+    
+    ubyte returnByte(ubyte input) {
+        y += state[++x];
+        ubyte t = state[x];
+        state[x] = state[y];
+        state[y] = t;
+        return (input^state[cast(ubyte)(state[x]+state[y])]);
+    }
+    
+    void processBytes(void[] input_, uint inOff, uint len, void[] output_, uint outOff) {
+        if (!initialized)
+            throw new NotInitializedError(name()~": Cipher not initialized");
+            
+        ubyte[] input = cast(ubyte[]) input_;
+        ubyte[] output = cast(ubyte[]) output_;
+        
+        if ((inOff + len) > input.length)
+            throw new ShortBufferError(name()~": Input buffer too short");
+            
+        if ((outOff + len) > output.length)
+            throw new ShortBufferError(name()~": Output buffer too short");
+            
+        for (int i = 0; i < len; i++) {
+            y += state[++x];
+            ubyte t = state[x];
+            state[x] = state[y];
+            state[y] = t;
+            output[outOff++] = input[inOff++] ^ state[cast(ubyte)(state[x]+state[y])];
+        }
+    }
+    
+    void reset() { 
+        setup(workingKey);
+    }
+    
+    // Do RC4's key setup in a separate method to ease resetting
+    private void setup(ubyte[] key) {
+        for (int i = 0; i < 256; i++)
+            state[i] = cast(ubyte)i;
+            
+        x = 0;
+        for (int i = 0; i < 256; i++) {
+            x += key[i % key.length] + state[i];
+            ubyte t = state[i];
+            state[i] = state[x];
+            state[x] = t;
+        }
+        x = y = 0;
+    }
+    
+    /** Some RC4 test vectors. */
+    version (UnitTest) {
+        unittest {
+            static const char[][] test_keys = [
+                "0123456789abcdef",
+                "0123456789abcdef",
+                "0000000000000000",
+                "ef012345",
+                "0123456789abcdef"
+            ];
+                 
+            static const char[][] test_plaintexts = [
+                "0123456789abcdef",
+                "0000000000000000",
+                "0000000000000000",
+                "00000000000000000000",
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"
+            ];
+                 
+            static const char[][] test_ciphertexts = [
+                "75b7878099e0c596",
+                "7494c2e7104b0879",
+                "de188941a3375d3a",
+                "d6a141a7ec3c38dfbd61",
+                "7595c3e6114a09780c4ad452338e1ffd"~
+                "9a1be9498f813d76533449b6778dcad8"~
+                "c78a8d2ba9ac66085d0e53d59c26c2d1"~
+                "c490c1ebbe0ce66d1b6b1b13b6b919b8"~
+                "47c25a91447a95e75e4ef16779cde8bf"~
+                "0a95850e32af9689444fd377108f98fd"~
+                "cbd4e726567500990bcc7e0ca3c4aaa3"~
+                "04a387d20f3b8fbbcd42a1bd311d7a43"~
+                "03dda5ab078896ae80c18b0af66dff31"~
+                "9616eb784e495ad2ce90d7f772a81747"~
+                "b65f62093b1e0db9e5ba532fafec4750"~
+                "8323e671327df9444432cb7367cec82f"~
+                "5d44c0d00b67d650a075cd4b70dedd77"~
+                "eb9b10231b6b5b741347396d62897421"~
+                "d43df9b42e446e358e9c11a9b2184ecb"~
+                "ef0cd8e7a877ef968f1390ec9b3d35a5"~
+                "585cb009290e2fcde7b5ec66d9084be4"~
+                "4055a619d9dd7fc3166f9487f7cb2729"~ 
+                "12426445998514c15d53a18c864ce3a2"~ 
+                "b7555793988126520eacf2e3066e230c"~  
+                "91bee4dd5304f5fd0405b35bd99c7313"~
+                "5d3d9bc335ee049ef69b3867bf2d7bd1"~
+                "eaa595d8bfc0066ff8d31509eb0c6caa"~
+                "006c807a623ef84c3d33c195d23ee320"~
+                "c40de0558157c822d4b8c569d849aed5"~
+                "9d4e0fd7f379586b4b7ff684ed6a189f"~
+                "7486d49b9c4bad9ba24b96abf924372c"~
+                "8a8fffb10d55354900a77a3db5f205e1"~
+                "b99fcd8660863a159ad4abe40fa48934"~
+                "163ddde542a6585540fd683cbfd8c00f"~
+                "12129a284deacc4cdefe58be7137541c"~
+                "047126c8d49e2755ab181ab7e940b0c0"
+            ];
+    
+            RC4 r = new RC4();
+            foreach (uint i, char[] test_key; test_keys) {
+                ubyte[] buffer = new ubyte[test_plaintexts[i].length>>1];
+                char[] result;
+                
+                r.init(true, new SymmetricKey(Util.hexToUbytes(test_key)));
+                
+                // Encryption
+                r.processBytes(Util.hexToUbytes(test_plaintexts[i]), 0, buffer.length, buffer, 0);
+                result = Util.ubytesToHex(buffer);
+                assert(result == test_ciphertexts[i],
+                        r.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
+                        
+                r.reset();
+                
+                // Decryption
+                r.processBytes(Util.hexToUbytes(test_ciphertexts[i]), 0, buffer.length, buffer, 0);
+                result = Util.ubytesToHex(buffer);
+                assert(result == test_plaintexts[i],
+                        r.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/ciphers/RC6.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,208 @@
+/**
+ * This file is part of the dcrypt project.
+ *
+ * It should be noted that this algorithm is very similar to RC5.
+ * Currently there are no plans to implement RC5, but should that change
+ * in the future, it may be wise to rewrite both RC5 and RC6 to use some
+ * kind of template or base class.
+ *
+ * Copyright: Copyright (C) dcrypt contributors 2008. All rights reserved.
+ * License:   MIT
+ * Authors:   Thomas Dixon
+ */
+
+module dcrypt.crypto.ciphers.RC6;
+
+import dcrypt.misc.Util;
+import dcrypt.crypto.BlockCipher;
+
+/**
+ * Implementation of the RC6-32/20/b cipher designed by 
+ * Ron Rivest et al. of RSA Security.
+ *
+ * Note: This algorithm is patented and trademarked.
+ */
+class RC6 : BlockCipher {
+    private const uint ROUNDS = 20,
+                       BLOCK_SIZE = 16,
+                       // Magic constants for a 32 bit word size
+                       P = 0xb7e15163,
+                       Q = 0x9e3779b9;
+    private uint[] S;
+    private ubyte[] workingKey;
+    private bool initialized,
+                 encrypt;
+    
+    char[] name() {
+        return "RC6";
+    }
+    
+    uint blockSize() {
+        return BLOCK_SIZE;
+    }
+    
+    void init(bool encrypt, CipherParameters params) {
+        SymmetricKey keyParams = cast(SymmetricKey)params;
+        if (!keyParams)
+            throw new InvalidParameterError(
+                    name()~": Invalid parameter passed to init");
+        this.encrypt = encrypt;
+        
+        uint len = keyParams.key.length;
+        if (len != 16 && len != 24 && len != 32)
+            throw new InvalidKeyError(
+                    name()~": Invalid key length (requires 16/24/32 bytes)");
+        
+        S = new uint[2*ROUNDS+4];        
+                   
+        workingKey = keyParams.key;
+        setup(workingKey);
+        
+        initialized = true;
+    }
+    
+    uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
+        if (!initialized)
+            throw new NotInitializedError(name()~": Cipher not initialized");
+            
+        ubyte[] input = cast(ubyte[]) input_;
+        ubyte[] output = cast(ubyte[]) output_;
+                    
+        if ((inOff + BLOCK_SIZE) > input.length)
+            throw new ShortBufferError(name()~": Input buffer too short");
+            
+        if ((outOff + BLOCK_SIZE) > output.length)
+            throw new ShortBufferError(name()~": Output buffer too short");
+        
+        uint A = Util.ubytesToUintLittle(input, inOff),
+             B = Util.ubytesToUintLittle(input, inOff+4),
+             C = Util.ubytesToUintLittle(input, inOff+8),
+             D = Util.ubytesToUintLittle(input, inOff+12),
+             t,
+             u;
+             
+        if (encrypt) {
+            B += S[0];
+            D += S[1];
+            for (int i = 1; i <= ROUNDS; i++) {
+                t = Util.rotateLeft(B*((B<<1)+1), 5);
+                u = Util.rotateLeft(D*((D<<1)+1), 5);
+                A = Util.rotateLeft(A^t, u) + S[i<<1];
+                C = Util.rotateLeft(C^u, t) + S[(i<<1)+1];
+                t = A;
+                A = B;
+                B = C;
+                C = D;
+                D = t;
+            }
+            A += S[2*ROUNDS+2];
+            C += S[2*ROUNDS+3];
+        } else {
+            C -= S[2*ROUNDS+3];
+            A -= S[2*ROUNDS+2];
+            for (int i = ROUNDS; i >= 1; i--) {
+                t = D;
+                D = C;
+                C = B;
+                B = A;
+                A = t;
+                u = Util.rotateLeft(D*((D<<1)+1), 5);
+                t = Util.rotateLeft(B*((B<<1)+1), 5);
+                C = Util.rotateRight(C-S[(i<<1)+1], t) ^ u;
+                A = Util.rotateRight(A-S[i<<1], u) ^ t;
+            }
+            D -= S[1];
+            B -= S[0];
+        }
+
+        Util.uintToUbytesLittle(A, output, outOff);
+        Util.uintToUbytesLittle(B, output, outOff+4);
+        Util.uintToUbytesLittle(C, output, outOff+8);
+        Util.uintToUbytesLittle(D, output, outOff+12);
+        
+        return BLOCK_SIZE;
+    }
+    
+    void reset() {
+        setup(workingKey);
+    }
+    
+    void setup(ubyte[] key) {
+        uint c = key.length/4;
+        uint[] L = new uint[c];
+        for (int i = 0, j = 0; i < c; i++, j+=4)
+            L[i] = Util.ubytesToUintLittle(key, j);
+        S[0] = P;
+        for (int i = 1; i <= 2*ROUNDS+3; i++)
+            S[i] = S[i-1] + Q;
+        uint A, B, i, j, v = 3*(2*ROUNDS+4); // Relying on ints initializing to 0
+        for (int s = 1; s <= v; s++) {
+            A = S[i] = Util.rotateLeft(S[i]+A+B, 3);
+            B = L[j] = Util.rotateLeft(L[j]+A+B, A+B);
+            i = (i + 1) % (2*ROUNDS+4);
+            j = (j + 1) % c;
+        }
+    }
+    
+    /** Some RC6 test vectors from the spec. */
+    version (UnitTest) {
+        unittest {
+            static const char[][] test_keys = [
+                "00000000000000000000000000000000",
+                
+                "0123456789abcdef0112233445566778",
+                
+                "00000000000000000000000000000000"~
+                "0000000000000000",
+                
+                "0123456789abcdef0112233445566778"~
+                "899aabbccddeeff0",
+                
+                "00000000000000000000000000000000"~
+                "00000000000000000000000000000000",
+                
+                "0123456789abcdef0112233445566778"~
+                "899aabbccddeeff01032547698badcfe"
+            ];
+                 
+            static const char[][] test_plaintexts = [
+                "00000000000000000000000000000000",
+                "02132435465768798a9bacbdcedfe0f1",
+                "00000000000000000000000000000000",
+                "02132435465768798a9bacbdcedfe0f1",
+                "00000000000000000000000000000000",
+                "02132435465768798a9bacbdcedfe0f1"
+            ];
+                
+            static const char[][] test_ciphertexts = [
+                "8fc3a53656b1f778c129df4e9848a41e",
+                "524e192f4715c6231f51f6367ea43f18",
+                "6cd61bcb190b30384e8a3f168690ae82",
+                "688329d019e505041e52e92af95291d4",
+                "8f5fbd0510d15fa893fa3fda6e857ec2",
+                "c8241816f0d7e48920ad16a1674e5d48"
+            ];
+                
+            RC6 t = new RC6();
+            foreach (uint i, char[] test_key; test_keys) {
+                ubyte[] buffer = new ubyte[t.blockSize];
+                char[] result;
+                SymmetricKey key = new SymmetricKey(Util.hexToUbytes(test_key));
+                
+                // Encryption
+                t.init(true, key);
+                t.processBlock(Util.hexToUbytes(test_plaintexts[i]), 0, buffer, 0);
+                result = Util.ubytesToHex(buffer);
+                assert(result == test_ciphertexts[i],
+                        t.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
+            
+                // Decryption
+                t.init(false, key);
+                t.processBlock(Util.hexToUbytes(test_ciphertexts[i]), 0, buffer, 0);
+                result = Util.ubytesToHex(buffer);
+                assert(result == test_plaintexts[i],
+                        t.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/ciphers/TEA.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,135 @@
+/**
+ * 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.ciphers.TEA;
+
+import dcrypt.misc.Util;
+import dcrypt.crypto.BlockCipher;
+
+/** Implementation of the TEA cipher designed by
+    David Wheeler and Roger Needham. */
+class TEA : BlockCipher {
+    private const uint ROUNDS = 32,
+                       KEY_SIZE = 16,
+                       BLOCK_SIZE = 8,
+                       DELTA = 0x9e3779b9,
+                       DECRYPT_SUM = 0xc6ef3720;
+    private uint sk0, sk1, sk2, sk3, sum;
+    private bool initialized,
+                 encrypt;
+    
+    void reset(){}
+    
+    char[] name() {
+        return "TEA";
+    }
+    
+    uint blockSize() {
+        return BLOCK_SIZE;
+    }
+    
+    void init(bool encrypt, CipherParameters params) {
+        SymmetricKey keyParams = cast(SymmetricKey)params;
+        if (!keyParams)
+            throw new InvalidParameterError(
+                    name()~": Invalid parameter passed to init");
+        this.encrypt = encrypt;
+                    
+        if (keyParams.key.length != KEY_SIZE)
+            throw new InvalidKeyError(
+                    name()~": Invalid key length (requires 16 bytes)");
+        
+        sk0 = Util.ubytesToUintBig(keyParams.key, 0);
+        sk1 = Util.ubytesToUintBig(keyParams.key, 4);
+        sk2 = Util.ubytesToUintBig(keyParams.key, 8);
+        sk3 = Util.ubytesToUintBig(keyParams.key, 12);
+
+        initialized = true;
+    }
+    
+    uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
+        if (!initialized)
+            throw new NotInitializedError(name()~": Cipher not initialized");
+            
+        ubyte[] input = cast(ubyte[]) input_;
+        ubyte[] output = cast(ubyte[]) output_;
+                    
+        if ((inOff + BLOCK_SIZE) > input.length)
+            throw new ShortBufferError(name()~": Input buffer too short");
+            
+        if ((outOff + BLOCK_SIZE) > output.length)
+            throw new ShortBufferError(name()~": Output buffer too short");
+        
+        uint v0 = Util.ubytesToUintBig(input, inOff),
+             v1 = Util.ubytesToUintBig(input, inOff+4);
+        
+        sum = encrypt ? 0 : DECRYPT_SUM;
+        for (int i = 0; i < ROUNDS; i++) {
+            if (encrypt) {
+                sum += DELTA;
+                v0 += ((v1 << 4) + sk0) ^ (v1 + sum) ^ ((v1 >> 5) + sk1);
+                v1 += ((v0 << 4) + sk2) ^ (v0 + sum) ^ ((v0 >> 5) + sk3);
+            } else {
+                v1 -= ((v0 << 4) + sk2) ^ (v0 + sum) ^ ((v0 >> 5) + sk3);
+                v0 -= ((v1 << 4) + sk0) ^ (v1 + sum) ^ ((v1 >> 5) + sk1);
+                sum -= DELTA;
+            }
+        }
+        
+        Util.uintToUbytesBig(v0, output, outOff);
+        Util.uintToUbytesBig(v1, output, outOff+4);
+        
+        return BLOCK_SIZE;
+    }
+    
+    /** Some TEA test vectors. */
+    unittest {
+        static const char[][] test_keys = [
+            "00000000000000000000000000000000",
+            "00000000000000000000000000000000",
+            "0123456712345678234567893456789a",
+            "0123456712345678234567893456789a"
+        ];
+             
+        static const char[][] test_plaintexts = [
+            "0000000000000000",
+            "0102030405060708",
+            "0000000000000000",
+            "0102030405060708"
+        ];
+            
+        static const char[][] test_ciphertexts = [
+            "41ea3a0a94baa940",
+            "6a2f9cf3fccf3c55",
+            "34e943b0900f5dcb",
+            "773dc179878a81c0"
+        ];
+            
+        
+        TEA t = new TEA();
+        foreach (uint i, char[] test_key; test_keys) {
+            ubyte[] buffer = new ubyte[t.blockSize];
+            char[] result;
+            SymmetricKey key = new SymmetricKey(Util.hexToUbytes(test_key));
+            
+            // Encryption
+            t.init(true, key);
+            t.processBlock(Util.hexToUbytes(test_plaintexts[i]), 0, buffer, 0);
+            result = Util.ubytesToHex(buffer);
+            assert(result == test_ciphertexts[i],
+                    t.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
+
+            // Decryption
+            t.init(false, key);
+            t.processBlock(Util.hexToUbytes(test_ciphertexts[i]), 0, buffer, 0);
+            result = Util.ubytesToHex(buffer);
+            assert(result == test_plaintexts[i],
+                    t.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/ciphers/XTEA.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,163 @@
+/**
+ * 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.ciphers.XTEA;
+
+import dcrypt.misc.Util;
+import dcrypt.crypto.BlockCipher;
+
+/** Implementation of the XTEA cipher designed by
+    David Wheeler and Roger Needham. */
+class XTEA : BlockCipher {
+    private const uint ROUNDS = 32,
+                       KEY_SIZE = 16,
+                       BLOCK_SIZE = 8,
+                       DELTA = 0x9e3779b9;
+    private uint[] subkeys,
+                   sum0,
+                   sum1;
+    private bool initialized,
+                 encrypt;
+    
+    void reset(){}
+    
+    char[] name() {
+        return "XTEA";
+    }
+    
+    uint blockSize() {
+        return BLOCK_SIZE;
+    }
+    
+    void init(bool encrypt, CipherParameters params) {
+        SymmetricKey keyParams = cast(SymmetricKey)params;
+        if (!keyParams)
+            throw new InvalidParameterError(
+                    name()~": Invalid parameter passed to init");
+        this.encrypt = encrypt;
+                    
+        if (keyParams.key.length != KEY_SIZE)
+            throw new InvalidKeyError(
+                    name()~": Invalid key length (requires 16 bytes)");
+        
+        subkeys = new uint[4];
+        sum0 = new uint[32];
+        sum1 = new uint[32];
+        
+        int i, j;
+        for (i = j = 0; i < 4; i++, j+=4)
+            subkeys[i] = Util.ubytesToUintBig(keyParams.key, j);
+            
+        // Precompute the values of sum + k[] to speed up encryption
+        for (i = j = 0; i < ROUNDS; i++) {
+            sum0[i] = (j + subkeys[j & 3]);
+            j += DELTA;
+            sum1[i] = (j + subkeys[j >> 11 & 3]);
+        }
+        initialized = true;
+    }
+    
+    uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
+        if (!initialized)
+            throw new NotInitializedError(name()~": Cipher not initialized");
+            
+        ubyte[] input = cast(ubyte[]) input_;
+        ubyte[] output = cast(ubyte[]) output_;
+                    
+        if ((inOff + BLOCK_SIZE) > input.length)
+            throw new ShortBufferError(name()~": Input buffer too short");
+            
+        if ((outOff + BLOCK_SIZE) > output.length)
+            throw new ShortBufferError(name()~": Output buffer too short");
+        
+        uint v0 = Util.ubytesToUintBig(input, inOff),
+             v1 = Util.ubytesToUintBig(input, inOff+4);
+             
+        if (encrypt) {
+            for (int i = 0; i < ROUNDS; i++) {
+                v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ sum0[i];
+                v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ sum1[i];
+            }
+        } else {
+            for (int i = ROUNDS-1; i >= 0; i--) {
+                v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ sum1[i];
+                v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ sum0[i];
+            }
+        }
+        
+        Util.uintToUbytesBig(v0, output, outOff);
+        Util.uintToUbytesBig(v1, output, outOff+4);
+        
+        return BLOCK_SIZE;
+    }
+    
+    /** Some XTEA test vectors. */
+    version (UnitTest) {
+        unittest {
+            static const char[][] test_keys = [
+                "00000000000000000000000000000000",
+                "00000000000000000000000000000000",
+                "0123456712345678234567893456789a",
+                "0123456712345678234567893456789a",
+                "00000000000000000000000000000001",
+                "01010101010101010101010101010101",
+                "0123456789abcdef0123456789abcdef",
+                "0123456789abcdef0123456789abcdef",
+                "00000000000000000000000000000000",
+                "00000000000000000000000000000000"
+            ];
+                 
+            static const char[][] test_plaintexts = [
+                "0000000000000000",
+                "0102030405060708",
+                "0000000000000000",
+                "0102030405060708",
+                "0000000000000001",
+                "0101010101010101",
+                "0123456789abcdef",
+                "0000000000000000",
+                "0123456789abcdef",
+                "4141414141414141"
+            ];
+                
+            static const char[][] test_ciphertexts = [
+                "dee9d4d8f7131ed9",
+                "065c1b8975c6a816",
+                "1ff9a0261ac64264",
+                "8c67155b2ef91ead",
+                "9f25fa5b0f86b758",
+                "c2eca7cec9b7f992",
+                "27e795e076b2b537",
+                "5c8eddc60a95b3e1",
+                "7e66c71c88897221",
+                "ed23375a821a8c2d"
+            ];
+                
+            XTEA t = new XTEA();
+            foreach (uint i, char[] test_key; test_keys) {
+                ubyte[] buffer = new ubyte[t.blockSize];
+                char[] result;
+                SymmetricKey key = new SymmetricKey(Util.hexToUbytes(test_key));
+                
+                // Encryption
+                t.init(true, key);
+                t.processBlock(Util.hexToUbytes(test_plaintexts[i]), 0, buffer, 0);
+                result = Util.ubytesToHex(buffer);
+                assert(result == test_ciphertexts[i],
+                        t.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
+    
+                // Decryption
+                t.init(false, key);
+                t.processBlock(Util.hexToUbytes(test_ciphertexts[i]), 0, buffer, 0);
+                result = Util.ubytesToHex(buffer);
+                assert(result == test_plaintexts[i],
+                        t.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/errors/InvalidKeyError.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,15 @@
+/**
+ * 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.errors.InvalidKeyError;
+
+import dcrypt.crypto.errors.InvalidParameterError;
+
+class InvalidKeyError : InvalidParameterError {
+    this(char[] msg) { super(msg); }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/errors/InvalidPaddingError.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,13 @@
+/**
+ * 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.errors.InvalidPaddingError;
+
+class InvalidPaddingError : Exception {
+    this(char[] msg) { super(msg); }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/errors/InvalidParameterError.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,13 @@
+/**
+ * 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.errors.InvalidParameterError;
+
+class InvalidParameterError : Exception {
+    this(char[] msg) { super(msg); }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/errors/NotInitializedError.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,13 @@
+/**
+ * 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.errors.NotInitializedError;
+
+class NotInitializedError : Exception {
+    this(char[] msg) { super(msg); }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/errors/ShortBufferError.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,13 @@
+/**
+ * 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.errors.ShortBufferError;
+
+class ShortBufferError : Exception {
+    this(char[] msg) { super(msg); }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/hashes/MD5.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,245 @@
+/**
+ * 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.hashes.MD5;
+
+public import dcrypt.crypto.Hash;
+
+/** Implementation of Ron Rivest's MD5. */
+class MD5 : Hash {
+	private uint h0, h1, h2, h3;
+    
+    // Shift amounts
+    private enum {
+        S11 =  7,
+        S12 = 12,
+        S13 = 17,
+        S14 = 22,
+        
+        S21 =  5,
+        S22 =  9,
+        S23 = 14,
+        S24 = 20,
+        
+        S31 =  4,
+        S32 = 11,
+        S33 = 16,
+        S34 = 23,
+        
+        S41 =  6,
+        S42 = 10,
+        S43 = 15,
+        S44 = 21
+    };
+    
+    this (void[] input_=null) {
+        reset();
+        super(input_);
+    }
+
+    uint blockSize() {
+        return 64;
+    }
+    
+    uint digestSize() {
+        return 16;
+    }
+    
+    char[] name() {
+        return "MD5";
+    }
+    
+    void transform(ubyte[] input) {
+        uint[] w = new uint[16];
+        
+        for (int i = 0, j = 0; i < 16; i++,j+=4)
+            w[i] = Util.ubytesToUintLittle(input, j);
+        
+        uint a = h0,
+             b = h1,
+             c = h2,
+             d = h3;
+        
+        // Round 1 -- FIGHT!
+        ff(a, b, c, d, w[ 0], S11, 3614090360); /* 1 */
+        ff(d, a, b, c, w[ 1], S12, 3905402710); /* 2 */
+        ff(c, d, a, b, w[ 2], S13,  606105819); /* 3 */
+        ff(b, c, d, a, w[ 3], S14, 3250441966); /* 4 */
+        ff(a, b, c, d, w[ 4], S11, 4118548399); /* 5 */
+        ff(d, a, b, c, w[ 5], S12, 1200080426); /* 6 */
+        ff(c, d, a, b, w[ 6], S13, 2821735955); /* 7 */
+        ff(b, c, d, a, w[ 7], S14, 4249261313); /* 8 */
+        ff(a, b, c, d, w[ 8], S11, 1770035416); /* 9 */
+        ff(d, a, b, c, w[ 9], S12, 2336552879); /* 10 */
+        ff(c, d, a, b, w[10], S13, 4294925233); /* 11 */
+        ff(b, c, d, a, w[11], S14, 2304563134); /* 12 */
+        ff(a, b, c, d, w[12], S11, 1804603682); /* 13 */
+        ff(d, a, b, c, w[13], S12, 4254626195); /* 14 */
+        ff(c, d, a, b, w[14], S13, 2792965006); /* 15 */
+        ff(b, c, d, a, w[15], S14, 1236535329); /* 16 */
+        
+        // Round 2
+        gg(a, b, c, d, w[ 1], S21, 4129170786); /* 17 */
+        gg(d, a, b, c, w[ 6], S22, 3225465664); /* 18 */
+        gg(c, d, a, b, w[11], S23,  643717713); /* 19 */
+        gg(b, c, d, a, w[ 0], S24, 3921069994); /* 20 */
+        gg(a, b, c, d, w[ 5], S21, 3593408605); /* 21 */
+        gg(d, a, b, c, w[10], S22,   38016083); /* 22 */
+        gg(c, d, a, b, w[15], S23, 3634488961); /* 23 */
+        gg(b, c, d, a, w[ 4], S24, 3889429448); /* 24 */
+        gg(a, b, c, d, w[ 9], S21,  568446438); /* 25 */
+        gg(d, a, b, c, w[14], S22, 3275163606); /* 26 */
+        gg(c, d, a, b, w[ 3], S23, 4107603335); /* 27 */
+        gg(b, c, d, a, w[ 8], S24, 1163531501); /* 28 */
+        gg(a, b, c, d, w[13], S21, 2850285829); /* 29 */
+        gg(d, a, b, c, w[ 2], S22, 4243563512); /* 30 */
+        gg(c, d, a, b, w[ 7], S23, 1735328473); /* 31 */
+        gg(b, c, d, a, w[12], S24, 2368359562); /* 32 */
+        
+        // Round 3
+        hh(a, b, c, d, w[ 5], S31, 4294588738); /* 33 */
+        hh(d, a, b, c, w[ 8], S32, 2272392833); /* 34 */
+        hh(c, d, a, b, w[11], S33, 1839030562); /* 35 */
+        hh(b, c, d, a, w[14], S34, 4259657740); /* 36 */
+        hh(a, b, c, d, w[ 1], S31, 2763975236); /* 37 */
+        hh(d, a, b, c, w[ 4], S32, 1272893353); /* 38 */
+        hh(c, d, a, b, w[ 7], S33, 4139469664); /* 39 */
+        hh(b, c, d, a, w[10], S34, 3200236656); /* 40 */
+        hh(a, b, c, d, w[13], S31,  681279174); /* 41 */
+        hh(d, a, b, c, w[ 0], S32, 3936430074); /* 42 */
+        hh(c, d, a, b, w[ 3], S33, 3572445317); /* 43 */
+        hh(b, c, d, a, w[ 6], S34,   76029189); /* 44 */
+        hh(a, b, c, d, w[ 9], S31, 3654602809); /* 45 */
+        hh(d, a, b, c, w[12], S32, 3873151461); /* 46 */
+        hh(c, d, a, b, w[15], S33,  530742520); /* 47 */
+        hh(b, c, d, a, w[ 2], S34, 3299628645); /* 48 */
+        
+        // Round 4
+        ii(a, b, c, d, w[ 0], S41, 4096336452); /* 49 */
+        ii(d, a, b, c, w[ 7], S42, 1126891415); /* 50 */
+        ii(c, d, a, b, w[14], S43, 2878612391); /* 51 */
+        ii(b, c, d, a, w[ 5], S44, 4237533241); /* 52 */
+        ii(a, b, c, d, w[12], S41, 1700485571); /* 53 */
+        ii(d, a, b, c, w[ 3], S42, 2399980690); /* 54 */
+        ii(c, d, a, b, w[10], S43, 4293915773); /* 55 */
+        ii(b, c, d, a, w[ 1], S44, 2240044497); /* 56 */
+        ii(a, b, c, d, w[ 8], S41, 1873313359); /* 57 */
+        ii(d, a, b, c, w[15], S42, 4264355552); /* 58 */
+        ii(c, d, a, b, w[ 6], S43, 2734768916); /* 59 */
+        ii(b, c, d, a, w[13], S44, 1309151649); /* 60 */
+        ii(a, b, c, d, w[ 4], S41, 4149444226); /* 61 */
+        ii(d, a, b, c, w[11], S42, 3174756917); /* 62 */
+        ii(c, d, a, b, w[ 2], S43,  718787259); /* 63 */
+        ii(b, c, d, a, w[ 9], S44, 3951481745); /* 64 */
+        
+        // FINISH HIM!
+        h0 += a;
+        h1 += b;
+        h2 += c;
+        h3 += d;
+        // FATALITY! MD5 Wins. \o/ ('cept not because it's insecure, but whatev)
+    }
+    
+    private uint f(uint x, uint y, uint z) {
+            return (x&y)|(~x&z);
+    }
+
+    private uint h(uint x, uint y, uint z) {
+            return x^y^z;
+    }
+    
+    private uint g(uint x, uint y, uint z) {
+        return (x&z)|(y&~z);
+    }
+
+    private uint i(uint x, uint y, uint z) {
+        return y^(x|~z);
+    }
+
+    private void ff(ref uint a, uint b, uint c,
+                        uint d, uint x, uint s, uint ac) {
+        a += f(b, c, d) + x + ac;
+        a = Util.rotateLeft(a, s);
+        a += b;
+    }
+
+    private void gg(ref uint a, uint b, uint c,
+                        uint d, uint x, uint s, uint ac) {
+        a += g(b, c, d) + x + ac;
+        a = Util.rotateLeft(a, s);
+        a += b;
+    }
+
+    private void hh(ref uint a, uint b, uint c,
+                        uint d, uint x, uint s, uint ac) {
+        a += h(b, c, d) + x + ac;
+        a = Util.rotateLeft(a, s);
+        a += b;
+    }
+
+    private void ii(ref uint a, uint b, uint c,
+                        uint d, uint x, uint s, uint ac) {
+        a += i(b, c, d) + x + ac;
+        a = Util.rotateLeft(a, s);
+        a += b;
+    }
+    
+    ubyte[] digest() {
+    	padMessage(MODE_MD);
+        ubyte[] result = new ubyte[digestSize];
+        
+        Util.uintToUbytesLittle(h0, result, 0);
+        Util.uintToUbytesLittle(h1, result, 4);
+        Util.uintToUbytesLittle(h2, result, 8);
+        Util.uintToUbytesLittle(h3, result, 12);
+        
+        reset();
+        return result;
+    }
+
+    void reset() {
+        super.reset();
+        h0 = 0x67452301u,
+        h1 = 0xefcdab89u,
+        h2 = 0x98badcfeu,
+        h3 = 0x10325476u;
+    }
+    
+    version (UnitTest) {
+        // Found in Tango <3
+        unittest {
+            static const char[][] test_inputs = [
+                "",
+                "a",
+                "abc",
+                "message digest",
+                "abcdefghijklmnopqrstuvwxyz",
+                "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+                "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+            ];
+            
+            static const char[][] test_results = [
+                "d41d8cd98f00b204e9800998ecf8427e",
+                "0cc175b9c0f1b6a831c399e269772661",
+                "900150983cd24fb0d6963f7d28e17f72",
+                "f96b697d7cb7938d525a2f31aaf161d0",
+                "c3fcd3d76192e4007dfb496cca67e13b",
+                "d174ab98d277d9f5a5611c2c9f419d9f",
+                "57edf4a22be3c955ac49da2e2107b67a"
+            ];
+            
+            MD5 h = new MD5();
+            foreach (uint i, char[] input; test_inputs) {
+                h.update(input);
+                char[] digest = h.hexDigest();
+                assert(digest == test_results[i], 
+                        h.name()~": ("~digest~") != ("~test_results[i]~")");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/hashes/SHA1.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,196 @@
+/**
+ * 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.hashes.SHA1;
+
+public import dcrypt.crypto.Hash;
+
+/** Implementation of SHA-1. */
+class SHA1 : Hash {
+	private uint h0, h1, h2, h3, h4;
+    
+    this (void[] input_=null) {
+        reset();
+        super(input_);
+    }
+
+    uint blockSize() {
+        return 64;
+    }
+    
+    uint digestSize() {
+        return 20;
+    }
+    
+    char[] name() {
+        return "SHA1";
+    }
+    
+    void transform(ubyte[] input) {
+        uint[] w = new uint[80];
+        
+        for (int i = 0, j = 0; i < 16; i++,j+=4)
+            w[i] = Util.ubytesToUintBig(input, j);
+        
+        for (int i = 16; i < 80; i++)
+            w[i] = Util.rotateLeft(w[i-3]^w[i-8]^w[i-14]^w[i-16], 1);
+        
+        uint a = h0,
+             b = h1,
+             c = h2,
+             d = h3,
+             e = h4;
+
+        int i = 0;
+        for (; i < 20;) {
+            e += Util.rotateLeft(a, 5) + f0(b, c, d) + w[i++];
+            b = Util.rotateLeft(b, 30);
+            
+            d += Util.rotateLeft(e, 5) + f0(a, b, c) + w[i++];
+            a = Util.rotateLeft(a, 30);
+       
+            c += Util.rotateLeft(d, 5) + f0(e, a, b) + w[i++];
+            e = Util.rotateLeft(e, 30);
+       
+            b += Util.rotateLeft(c, 5) + f0(d, e, a) + w[i++];
+            d = Util.rotateLeft(d, 30);
+
+            a += Util.rotateLeft(b, 5) + f0(c, d, e) + w[i++];
+            c = Util.rotateLeft(c, 30);
+        }
+        
+        for (; i < 40;) {
+            e += Util.rotateLeft(a, 5) + f1(b, c, d) + w[i++];
+            b = Util.rotateLeft(b, 30);
+            
+            d += Util.rotateLeft(e, 5) + f1(a, b, c) + w[i++];
+            a = Util.rotateLeft(a, 30);
+       
+            c += Util.rotateLeft(d, 5) + f1(e, a, b) + w[i++];
+            e = Util.rotateLeft(e, 30);
+       
+            b += Util.rotateLeft(c, 5) + f1(d, e, a) + w[i++];
+            d = Util.rotateLeft(d, 30);
+
+            a += Util.rotateLeft(b, 5) + f1(c, d, e) + w[i++];
+            c = Util.rotateLeft(c, 30);
+        }
+        
+        for (; i < 60;) {
+            e += Util.rotateLeft(a, 5) + f2(b, c, d) + w[i++];
+            b = Util.rotateLeft(b, 30);
+            
+            d += Util.rotateLeft(e, 5) + f2(a, b, c) + w[i++];
+            a = Util.rotateLeft(a, 30);
+       
+            c += Util.rotateLeft(d, 5) + f2(e, a, b) + w[i++];
+            e = Util.rotateLeft(e, 30);
+       
+            b += Util.rotateLeft(c, 5) + f2(d, e, a) + w[i++];
+            d = Util.rotateLeft(d, 30);
+
+            a += Util.rotateLeft(b, 5) + f2(c, d, e) + w[i++];
+            c = Util.rotateLeft(c, 30);
+        }
+        
+        for (; i < 80;) {
+            e += Util.rotateLeft(a, 5) + f3(b, c, d) + w[i++];
+            b = Util.rotateLeft(b, 30);
+            
+            d += Util.rotateLeft(e, 5) + f3(a, b, c) + w[i++];
+            a = Util.rotateLeft(a, 30);
+       
+            c += Util.rotateLeft(d, 5) + f3(e, a, b) + w[i++];
+            e = Util.rotateLeft(e, 30);
+       
+            b += Util.rotateLeft(c, 5) + f3(d, e, a) + w[i++];
+            d = Util.rotateLeft(d, 30);
+
+            a += Util.rotateLeft(b, 5) + f3(c, d, e) + w[i++];
+            c = Util.rotateLeft(c, 30);
+        }
+
+        h0 += a;
+        h1 += b;
+        h2 += c;
+        h3 += d;
+        h4 += e;
+    }
+    
+    private uint f0(uint x, uint y, uint z) {
+        return (z^(x&(y^z))) + 0x5a827999;
+    }
+
+    private uint f1(uint x, uint y, uint z) {
+        return (x^y^z) + 0x6ed9eba1;
+    }
+
+    private uint f2(uint x, uint y, uint z) {
+        return ((x&y)|(z&(x|y))) + 0x8f1bbcdc;
+    }
+
+    private uint f3(uint x, uint y, uint z) {
+        return (x^y^z) + 0xca62c1d6;
+    }
+    
+    ubyte[] digest() {
+    	padMessage(MODE_SHA);
+        ubyte[] result = new ubyte[digestSize];
+        
+        Util.uintToUbytesBig(h0, result, 0);
+        Util.uintToUbytesBig(h1, result, 4);
+        Util.uintToUbytesBig(h2, result, 8);
+        Util.uintToUbytesBig(h3, result, 12);
+        Util.uintToUbytesBig(h4, result, 16);
+        
+        reset();
+        return result;
+    }
+
+    void reset() {
+        super.reset();
+        h0 = 0x67452301u;
+        h1 = 0xefcdab89u;
+        h2 = 0x98badcfeu;
+        h3 = 0x10325476u;
+        h4 = 0xc3d2e1f0u;
+    }
+    
+    version (UnitTest) {
+        unittest {
+            static const char[][] test_inputs = [
+                "",
+                "abc",
+                "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+                "a",
+                "0123456701234567012345670123456701234567012345670123456701234567"
+            ];
+            
+            static const int[] test_repeat = [
+                1, 1, 1, 1000000, 10
+            ];
+            
+            static const char[][] test_results = [
+                "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+                "a9993e364706816aba3e25717850c26c9cd0d89d",
+                "84983e441c3bd26ebaae4aa1f95129e5e54670f1",
+                "34aa973cd4c4daa4f61eeb2bdbad27316534016f",
+                "dea356a2cddd90c7a7ecedc5ebb563934f460452"
+            ];
+            
+            SHA1 h = new SHA1();
+            foreach (uint i, char[] input; test_inputs) {
+                for (int j = 0; j < test_repeat[i]; j++)
+                    h.update(input);
+                char[] digest = h.hexDigest();
+                assert(digest == test_results[i], 
+                        h.name()~": ("~digest~") != ("~test_results[i]~")");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/hashes/SHA224.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,87 @@
+/**
+ * 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.hashes.SHA224;
+
+import dcrypt.crypto.hashes.SHA256;
+
+/** Implementation of SHA-224. */
+class SHA224 : SHA256 {
+    this (void[] input_=null) {
+        reset();
+        super(input_);
+    }
+    
+    uint digestSize() {
+        return 28;
+    }
+    
+    char[] name() {
+        return "SHA224";
+    }
+    
+    ubyte[] digest() {
+    	padMessage(MODE_SHA);
+
+        ubyte[] result = new ubyte[digestSize];
+        
+        Util.uintToUbytesBig(h0, result, 0);
+        Util.uintToUbytesBig(h1, result, 4);
+        Util.uintToUbytesBig(h2, result, 8);
+        Util.uintToUbytesBig(h3, result, 12);
+        Util.uintToUbytesBig(h4, result, 16);
+        Util.uintToUbytesBig(h5, result, 20);
+        Util.uintToUbytesBig(h6, result, 24);
+
+        reset();
+        return result;
+    }
+
+    void reset() {
+        super.reset();
+        h0 = 0xc1059ed8u;
+        h1 = 0x367cd507u;
+        h2 = 0x3070dd17u;
+        h3 = 0xf70e5939u;
+        h4 = 0xffc00b31u;
+        h5 = 0x68581511u;
+        h6 = 0x64f98fa7u;
+        h7 = 0xbefa4fa4u;
+    }
+    
+    version (UnitTest) {
+        unittest {
+            static const char[][] test_inputs = [
+                "",
+                "abc",
+                "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+                "a"
+            ];
+            
+            static const int[] test_repeat = [
+                1, 1, 1, 1000000
+            ];
+            
+            static const char[][] test_results = [
+                "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f",
+                "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7",
+                "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525",
+                "20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67"
+            ];
+            
+            SHA224 h = new SHA224();
+            foreach (uint i, char[] input; test_inputs) {
+                for (int j = 0; j < test_repeat[i]; j++)
+                    h.update(input);
+                char[] digest = h.hexDigest();
+                assert(digest == test_results[i], 
+                        h.name()~": ("~digest~") != ("~test_results[i]~")");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/hashes/SHA256.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,176 @@
+/**
+ * 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.hashes.SHA256;
+
+public import dcrypt.crypto.Hash;
+
+/** Implementation of SHA-256. */
+class SHA256 : Hash {
+    private const uint[] K = [
+        0x428a2f98u, 0x71374491u, 0xb5c0fbcfu, 0xe9b5dba5u,
+        0x3956c25bu, 0x59f111f1u, 0x923f82a4u, 0xab1c5ed5u,
+        0xd807aa98u, 0x12835b01u, 0x243185beu, 0x550c7dc3u,
+        0x72be5d74u, 0x80deb1feu, 0x9bdc06a7u, 0xc19bf174u,
+        0xe49b69c1u, 0xefbe4786u, 0x0fc19dc6u, 0x240ca1ccu,
+        0x2de92c6fu, 0x4a7484aau, 0x5cb0a9dcu, 0x76f988dau,
+        0x983e5152u, 0xa831c66du, 0xb00327c8u, 0xbf597fc7u,
+        0xc6e00bf3u, 0xd5a79147u, 0x06ca6351u, 0x14292967u,
+        0x27b70a85u, 0x2e1b2138u, 0x4d2c6dfcu, 0x53380d13u,
+        0x650a7354u, 0x766a0abbu, 0x81c2c92eu, 0x92722c85u,
+        0xa2bfe8a1u, 0xa81a664bu, 0xc24b8b70u, 0xc76c51a3u,
+        0xd192e819u, 0xd6990624u, 0xf40e3585u, 0x106aa070u,
+        0x19a4c116u, 0x1e376c08u, 0x2748774cu, 0x34b0bcb5u,
+        0x391c0cb3u, 0x4ed8aa4au, 0x5b9cca4fu, 0x682e6ff3u,
+        0x748f82eeu, 0x78a5636fu, 0x84c87814u, 0x8cc70208u,
+        0x90befffau, 0xa4506cebu, 0xbef9a3f7u, 0xc67178f2u
+    ];
+	protected uint h0, h1, h2, h3, h4, h5, h6, h7;
+    
+    this (void[] input_=null) {
+        reset();
+        super(input_);
+    }
+
+    uint blockSize() {
+        return 64;
+    }
+    
+    uint digestSize() {
+        return 32;
+    }
+    
+    char[] name() {
+        return "SHA256";
+    }
+    
+    void transform(ubyte[] input) {
+        uint[] w = new uint[64];
+        
+        for (int i = 0, j = 0; i < 16; i++,j+=4)
+            w[i] = Util.ubytesToUintBig(input, j);
+        
+        for (int i = 16; i < 64; i++)
+            w[i] = theta1(w[i-2]) + w[i-7] + theta0(w[i-15]) + w[i-16];
+        
+        uint a = h0,
+             b = h1,
+             c = h2,
+             d = h3,
+             e = h4,
+             f = h5,
+             g = h6,
+             h = h7;
+
+        for (int i = 0; i < 64; i++) {
+            uint t1 = h + sum1(e) + ch(e,f,g) + K[i] + w[i],
+                 t2 = sum0(a) + maj(a,b,c);
+            h = g;
+            g = f;
+            f = e;
+            e = d + t1;
+            d = c;
+            c = b;
+            b = a;
+            a = t1 + t2;
+        }
+            
+        h0 += a;
+        h1 += b;
+        h2 += c;
+        h3 += d;
+        h4 += e;
+        h5 += f;
+        h6 += g;
+        h7 += h;
+    }
+    
+    private uint ch(uint x, uint y, uint z) {
+            return (x&y)^(~x&z);
+    }
+    
+    private uint maj(uint x, uint y, uint z) {
+            return (x&y)^(x&z)^(y&z);
+    }
+
+    private uint sum0(uint x) {
+            return Util.rotateRight(x,2)^Util.rotateRight(x,13)^Util.rotateRight(x,22);
+    }
+
+    private uint sum1(uint x) {
+            return Util.rotateRight(x,6)^Util.rotateRight(x,11)^Util.rotateRight(x,25);
+    }
+
+    private uint theta0(uint x) {
+        return Util.rotateRight(x,7)^Util.rotateRight(x,18)^(x >> 3);
+    }
+
+    private uint theta1(uint x) {
+        return Util.rotateRight(x,17)^Util.rotateRight(x,19)^(x >> 10);
+    }
+    
+    ubyte[] digest() {
+    	padMessage(MODE_SHA);
+        ubyte[] result = new ubyte[digestSize];
+        
+        Util.uintToUbytesBig(h0, result, 0);
+        Util.uintToUbytesBig(h1, result, 4);
+        Util.uintToUbytesBig(h2, result, 8);
+        Util.uintToUbytesBig(h3, result, 12);
+        Util.uintToUbytesBig(h4, result, 16);
+        Util.uintToUbytesBig(h5, result, 20);
+        Util.uintToUbytesBig(h6, result, 24);
+        Util.uintToUbytesBig(h7, result, 28);
+
+        reset();
+        return result;
+    }
+
+    void reset() {
+        super.reset();
+        h0 = 0x6a09e667u;
+        h1 = 0xbb67ae85u;
+        h2 = 0x3c6ef372u;
+        h3 = 0xa54ff53au;
+        h4 = 0x510e527fu;
+        h5 = 0x9b05688cu;
+        h6 = 0x1f83d9abu;
+        h7 = 0x5be0cd19u;
+    }
+    
+    version (UnitTest) {
+        unittest {
+            static const char[][] test_inputs = [
+                "",
+                "abc",
+                "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+                "a"
+            ];
+            
+            static const int[] test_repeat = [
+                1, 1, 1, 1000000
+            ];
+            
+            static const char[][] test_results = [
+                "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+                "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
+                "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1",
+                "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"
+            ];
+            
+            SHA256 h = new SHA256();
+            foreach (uint i, char[] input; test_inputs) {
+                for (int j = 0; j < test_repeat[i]; j++)
+                    h.update(input);
+                char[] digest = h.hexDigest();
+                assert(digest == test_results[i], 
+                        h.name()~": ("~digest~") != ("~test_results[i]~")");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/hashes/SHA384.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,93 @@
+/**
+ * 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.hashes.SHA384;
+
+import dcrypt.crypto.hashes.SHA512;
+
+/** Implementation of SHA-384. */
+class SHA384 : SHA512 {
+    this (void[] input_=null) {
+        reset();
+        super(input_);
+    }
+    
+    uint digestSize() {
+        return 48;
+    }
+    
+    char[] name() {
+        return "SHA384";
+    }
+
+    ubyte[] digest() {
+    	padMessage(MODE_SHA);
+        ubyte[] result = new ubyte[digestSize];
+        
+        Util.ulongToUbytesBig(h0, result, 0);
+        Util.ulongToUbytesBig(h1, result, 8);
+        Util.ulongToUbytesBig(h2, result, 16);
+        Util.ulongToUbytesBig(h3, result, 24);
+        Util.ulongToUbytesBig(h4, result, 32);
+        Util.ulongToUbytesBig(h5, result, 40);
+
+        reset();
+        return result;
+    }
+
+    void reset() {
+        super.reset();
+        h0 = 0xcbbb9d5dc1059ed8u,
+        h1 = 0x629a292a367cd507u,
+        h2 = 0x9159015a3070dd17u,
+        h3 = 0x152fecd8f70e5939u,
+        h4 = 0x67332667ffc00b31u,
+        h5 = 0x8eb44a8768581511u,
+        h6 = 0xdb0c2e0d64f98fa7u,
+        h7 = 0x47b5481dbefa4fa4u;
+    }
+    
+    version (UnitTest) {
+        unittest {
+            static const char[][] test_inputs = [
+                "",
+                "abc",
+                "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"~
+                "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+                "a"
+            ];
+            
+            static const int[] test_repeat = [
+                1, 1, 1, 1000000
+            ];
+            
+            static const char[][] test_results = [
+                "38b060a751ac96384cd9327eb1b1e36a21fdb71114be0743"~
+                "4c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b",
+                
+                "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded163"~
+                "1a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7",
+                
+                "09330c33f71147e83d192fc782cd1b4753111b173b3b05d2"~
+                "2fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039",
+                
+                "9d0e1809716474cb086e834e310a4a1ced149e9c00f24852"~
+                "7972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985"
+            ];
+
+            SHA384 h = new SHA384();
+            foreach (uint i, char[] input; test_inputs) {
+                for (int j = 0; j < test_repeat[i]; j++)
+                    h.update(input);
+                char[] digest = h.hexDigest();
+                assert(digest == test_results[i], 
+                        h.name()~": ("~digest~") != ("~test_results[i]~")");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/hashes/SHA512.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,192 @@
+/**
+ * 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.hashes.SHA512;
+
+public import dcrypt.crypto.Hash;
+
+/** Implementation of SHA-512. */
+class SHA512 : Hash {
+    private const ulong[] K = [
+        0x428a2f98d728ae22u, 0x7137449123ef65cdu, 0xb5c0fbcfec4d3b2fu, 0xe9b5dba58189dbbcu, 
+        0x3956c25bf348b538u, 0x59f111f1b605d019u, 0x923f82a4af194f9bu, 0xab1c5ed5da6d8118u, 
+        0xd807aa98a3030242u, 0x12835b0145706fbeu, 0x243185be4ee4b28cu, 0x550c7dc3d5ffb4e2u,
+        0x72be5d74f27b896fu, 0x80deb1fe3b1696b1u, 0x9bdc06a725c71235u, 0xc19bf174cf692694u, 
+        0xe49b69c19ef14ad2u, 0xefbe4786384f25e3u, 0x0fc19dc68b8cd5b5u, 0x240ca1cc77ac9c65u, 
+        0x2de92c6f592b0275u, 0x4a7484aa6ea6e483u, 0x5cb0a9dcbd41fbd4u, 0x76f988da831153b5u,
+        0x983e5152ee66dfabu, 0xa831c66d2db43210u, 0xb00327c898fb213fu, 0xbf597fc7beef0ee4u, 
+        0xc6e00bf33da88fc2u, 0xd5a79147930aa725u, 0x06ca6351e003826fu, 0x142929670a0e6e70u, 
+        0x27b70a8546d22ffcu, 0x2e1b21385c26c926u, 0x4d2c6dfc5ac42aedu, 0x53380d139d95b3dfu,
+        0x650a73548baf63deu, 0x766a0abb3c77b2a8u, 0x81c2c92e47edaee6u, 0x92722c851482353bu, 
+        0xa2bfe8a14cf10364u, 0xa81a664bbc423001u, 0xc24b8b70d0f89791u, 0xc76c51a30654be30u,
+        0xd192e819d6ef5218u, 0xd69906245565a910u, 0xf40e35855771202au, 0x106aa07032bbd1b8u,
+        0x19a4c116b8d2d0c8u, 0x1e376c085141ab53u, 0x2748774cdf8eeb99u, 0x34b0bcb5e19b48a8u,
+        0x391c0cb3c5c95a63u, 0x4ed8aa4ae3418acbu, 0x5b9cca4f7763e373u, 0x682e6ff3d6b2b8a3u,
+        0x748f82ee5defb2fcu, 0x78a5636f43172f60u, 0x84c87814a1f0ab72u, 0x8cc702081a6439ecu,
+        0x90befffa23631e28u, 0xa4506cebde82bde9u, 0xbef9a3f7b2c67915u, 0xc67178f2e372532bu,
+        0xca273eceea26619cu, 0xd186b8c721c0c207u, 0xeada7dd6cde0eb1eu, 0xf57d4f7fee6ed178u,
+        0x06f067aa72176fbau, 0x0a637dc5a2c898a6u, 0x113f9804bef90daeu, 0x1b710b35131c471bu,
+        0x28db77f523047d84u, 0x32caab7b40c72493u, 0x3c9ebe0a15c9bebcu, 0x431d67c49c100d4cu, 
+        0x4cc5d4becb3e42b6u, 0x597f299cfc657e2au, 0x5fcb6fab3ad6faecu, 0x6c44198c4a475817u
+    ];
+	protected ulong h0, h1, h2, h3, h4, h5, h6, h7;
+    
+    this (void[] input_=null) {
+        reset();
+        super(input_);
+    }
+
+    uint blockSize() {
+        return 128;
+    }
+    
+    uint digestSize() {
+        return 64;
+    }
+    
+    char[] name() {
+        return "SHA512";
+    }
+    
+    void transform(ubyte[] input) {
+        ulong[] w = new ulong[80];
+        
+        for (int i = 0, j = 0; i < 16; i++,j+=8)
+            w[i] = Util.ubytesToUlongBig(input, j);
+        
+        for (int i = 16; i < 80; i++)
+            w[i] = theta1(w[i-2]) + w[i-7] + theta0(w[i-15]) + w[i-16];
+        
+        ulong a = h0,
+              b = h1,
+              c = h2,
+              d = h3,
+              e = h4,
+              f = h5,
+              g = h6,
+              h = h7;
+        
+        for (int i = 0; i < 80; i++) {
+            ulong t1 = h + sum1(e) + ch(e,f,g) + K[i] + w[i],
+                  t2 = sum0(a) + maj(a,b,c);
+            h = g;
+            g = f;
+            f = e;
+            e = d + t1;
+            d = c;
+            c = b;
+            b = a;
+            a = t1 + t2;
+        }
+        
+        h0 += a;
+        h1 += b;
+        h2 += c;
+        h3 += d;
+        h4 += e;
+        h5 += f;
+        h6 += g;
+        h7 += h;
+    }
+    
+    private ulong ch(ulong x, ulong y, ulong z) {
+            return (x&y)^(~x&z);
+    }
+    
+    private ulong maj(ulong x, ulong y, ulong z) {
+            return (x&y)^(x&z)^(y&z);
+    }
+
+    private ulong sum0(ulong x) {
+            return (Util.rotateRight64(x,28)^
+                    Util.rotateRight64(x,34)^
+                    Util.rotateRight64(x,39));
+    }
+
+    private ulong sum1(ulong x) {
+            return (Util.rotateRight64(x,14)^
+                    Util.rotateRight64(x,18)^
+                    Util.rotateRight64(x,41));
+    }
+
+    private ulong theta0(ulong x) {
+        return Util.rotateRight64(x,1)^Util.rotateRight64(x,8)^(x >> 7);
+    }
+
+    private ulong theta1(ulong x) {
+        return Util.rotateRight64(x,19)^Util.rotateRight64(x,61)^(x >> 6);
+    }
+    
+    ubyte[] digest() {
+    	padMessage(MODE_SHA);
+        ubyte[] result = new ubyte[digestSize];
+        
+        Util.ulongToUbytesBig(h0, result, 0);
+        Util.ulongToUbytesBig(h1, result, 8);
+        Util.ulongToUbytesBig(h2, result, 16);
+        Util.ulongToUbytesBig(h3, result, 24);
+        Util.ulongToUbytesBig(h4, result, 32);
+        Util.ulongToUbytesBig(h5, result, 40);
+        Util.ulongToUbytesBig(h6, result, 48);
+        Util.ulongToUbytesBig(h7, result, 56);
+
+        reset();
+        return result;
+    }
+
+    void reset() {
+        super.reset();
+        h0 = 0x6a09e667f3bcc908u;
+        h1 = 0xbb67ae8584caa73bu;
+        h2 = 0x3c6ef372fe94f82bu;
+        h3 = 0xa54ff53a5f1d36f1u;
+        h4 = 0x510e527fade682d1u;
+        h5 = 0x9b05688c2b3e6c1fu;
+        h6 = 0x1f83d9abfb41bd6bu;
+        h7 = 0x5be0cd19137e2179u;
+    }
+    
+    version (UnitTest) {
+        unittest {
+            static const char[][] test_inputs = [
+                "",
+                "abc",
+                "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"~
+                "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+                "a"
+            ];
+            
+            static const int[] test_repeat = [
+                1, 1, 1, 1000000
+            ];
+            
+            static const char[][] test_results = [
+                "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce"~
+                "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
+                
+                "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a"~
+                "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
+                
+                "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018"~
+                "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909",
+                
+                "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb"~
+                "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"
+            ];
+
+            SHA512 h = new SHA512();
+            foreach (uint i, char[] input; test_inputs) {
+                for (int j = 0; j < test_repeat[i]; j++)
+                    h.update(input);
+                char[] digest = h.hexDigest();
+                assert(digest == test_results[i], 
+                        h.name()~": ("~digest~") != ("~test_results[i]~")");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/modes/CBC.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,181 @@
+/**
+ * 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.modes.CBC;
+
+import dcrypt.crypto.BlockCipher;
+import dcrypt.crypto.params.ParametersWithIV;
+
+version (UnitTest) {
+    import dcrypt.crypto.ciphers.XTEA;
+    import dcrypt.misc.Util;
+}
+
+/** This class implements the cipher block chaining (CBC) block mode. */
+class CBC : BlockCipher {
+    private BlockCipher m_cipher;
+    private ubyte[] iv,
+                    previousCiphertext,
+                    cbcOutput;
+    private bool encrypt,
+                 initialized;
+    
+    /**
+     * Params:
+     *     cipher = Block cipher to wrap.
+     */
+    this (BlockCipher cipher) {
+        m_cipher = cipher;
+    }
+    
+    /** Returns: The underlying cipher we are wrapping. */
+    BlockCipher cipher() {
+        return m_cipher;
+    }
+    
+    char[] name() {
+        return m_cipher.name~"/CBC";
+    }
+    
+    /** 
+     * Throws: dcrypt.crypto.errors.InvalidParameterError if params aren't 
+     *          an instance of dcrypt.crypto.params.ParametersWithIV.
+     */
+    void init(bool encrypt, CipherParameters params) {
+        ParametersWithIV ivParams = cast(ParametersWithIV)params;
+        
+        if (!ivParams)
+            throw new InvalidParameterError(
+                    name()~": Block mode requires IV (use ParametersWithIV)");
+        if (ivParams.iv.length != blockSize)
+            throw new InvalidParameterError(
+                    name()~": IV must be same length as cipher block size");
+                    
+        this.encrypt = encrypt;
+        m_cipher.init(encrypt, ivParams.parameters);
+        
+        iv = ivParams.iv[0..blockSize];
+        previousCiphertext = new ubyte[blockSize];
+        previousCiphertext[] = iv; // C_0 = IV
+        cbcOutput = new ubyte[blockSize]; // Output buffer for E_k/D_k(...)
+        
+        initialized = true;
+    }
+    
+    uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
+        if (!initialized)
+            throw new NotInitializedError(
+                    name()~": Block mode not initialized");
+            
+        ubyte[] input = cast(ubyte[]) input_,
+                output = cast(ubyte[]) output_;
+        
+        if ((inOff + blockSize) > input.length)
+            throw new ShortBufferError(name()~": Input buffer too short");
+            
+        if ((outOff + blockSize) > output.length)
+            throw new ShortBufferError(name()~": Output buffer too short");
+        
+        if (encrypt) {
+            // P_i XOR C_i-1
+            for (int i = 0; i < blockSize; i++)
+                previousCiphertext[i] ^= input[inOff++];
+                
+            // E_k(P_i XOR C_i-1)
+            m_cipher.processBlock(previousCiphertext, 0, cbcOutput, 0);
+            
+            // Store C_i for next block
+            previousCiphertext[] = cbcOutput;            
+            
+            // C_i = E_k(P_i XOR C_i-1)
+            output[outOff..(outOff+blockSize)] = cbcOutput;
+        } else {
+            // Temporarily store C_i
+            ubyte[] t = input[inOff..(inOff+blockSize)];
+
+            // D_k(C_i)
+            m_cipher.processBlock(t, 0, cbcOutput, 0);
+            
+            // P_i = D_k(C_i) XOR C_i-1
+            for (int i = 0; i < blockSize; i++)
+                output[outOff++] = (cbcOutput[i] ^ previousCiphertext[i]);
+             
+            // Store C_i for next block
+            previousCiphertext[] = t;
+       }
+        return blockSize;
+    }
+    
+    uint blockSize() {
+        return m_cipher.blockSize;
+    }
+    
+    void reset() {
+        previousCiphertext[] = iv;
+        m_cipher.reset();
+    }
+    
+    /** Test vectors for CBC mode. Assumes XTEA passes test vectors. */
+    version (UnitTest) {
+        unittest {
+            static const char[][] test_keys = [
+                "00000000000000000000000000000000",            
+                "00000000000000000000000000000000",
+                "0123456789abcdef0123456789abcdef"
+            ];
+                 
+            static const char[][] test_plaintexts = [
+                "00000000000000000000000000000000"~
+                "00000000000000000000000000000000",
+                 
+                "41414141414141414141414141414141"~
+                "41414141414141414141414141414141",
+                 
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"
+            ];
+                 
+            static const char[][] test_ciphertexts = [
+                "dee9d4d8f7131ed9b0e40a036a85d2c4"~
+                "4602d6e67f0c603738197998166ef281",
+                 
+                "ed23375a821a8c2d0e1f03d719874eaa"~
+                "4b71be74f261e22f4cd2285883a61a23",
+                 
+                "c09d3c606614d84b8d184fa29c5cb5f6"~
+                "f26fa5a0b6b63ba0f7ebf2f8735f85e3"
+            ];
+            
+            XTEA x = new XTEA();
+            CBC c = new CBC(x);
+            ubyte[] iv = new ubyte[x.blockSize], // Initialized to 0
+                    buffer = new ubyte[32];
+            char[] result;
+            for (int i = 0; i < test_keys.length; i++) {
+                SymmetricKey key = new SymmetricKey(Util.hexToUbytes(test_keys[i]));
+                ParametersWithIV params = new ParametersWithIV(key, iv);
+                
+                // Encryption
+                c.init(true, params);
+                for (int j = 0; j < 32; j+=x.blockSize)
+                    c.processBlock(Util.hexToUbytes(test_plaintexts[i]), j, buffer, j);
+                result = Util.ubytesToHex(buffer);
+                assert(result == test_ciphertexts[i],
+                        c.name()~": ("~result~") != ("~test_ciphertexts[i]~")");           
+                
+                // Decryption
+                c.init(false, params);
+                for (int j = 0; j < 32; j+=x.blockSize)
+                    c.processBlock(Util.hexToUbytes(test_ciphertexts[i]), j, buffer, j);
+                result = Util.ubytesToHex(buffer);
+                assert(result == test_plaintexts[i],
+                        c.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
+            }   
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/modes/CTR.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,95 @@
+/**
+ * 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.modes.CTR;
+
+import dcrypt.crypto.BlockCipher;
+import dcrypt.crypto.params.ParametersWithIV;
+
+
+/** This class implements the counter (CTR/SIC/ICM) block mode,
+    treating the counter as a big endian integer. */
+class CTR : BlockCipher {
+    private BlockCipher m_cipher;
+    private ubyte[] iv,
+                    counter,
+                    counterOutput;
+    private bool initialized = false;
+    
+    /**
+     * Params:
+     *     cipher = Block cipher to wrap.
+     */
+    this (BlockCipher cipher) {
+        m_cipher = cipher;
+    }
+    
+    /** Returns: The underlying cipher we are wrapping. */
+    BlockCipher cipher() {
+        return m_cipher;
+    }
+    
+    char[] name() {
+        return m_cipher.name~"/CTR";
+    }
+    
+    /** 
+     * Throws: dcrypt.crypto.errors.InvalidParameterError if params aren't 
+     *          an instance of dcrypt.crypto.params.ParametersWithIV.
+     */
+    void init(bool encrypt, CipherParameters params) {
+        ParametersWithIV ivParams = cast(ParametersWithIV)params;
+        
+        if (!ivParams)
+            throw new InvalidParameterError(
+                    name()~": Block mode requires IV (use ParametersWithIV)");
+        if (ivParams.iv.length != blockSize)
+            throw new InvalidParameterError(
+                    name()~": IV must be same length as cipher block size");
+                    
+        m_cipher.init(true, ivParams.parameters);
+        
+        iv = ivParams.iv[0..blockSize];
+        counter = new ubyte[blockSize];
+        counter[] = iv;
+        counterOutput = new ubyte[blockSize];
+        
+        initialized = true;
+    }
+    
+    uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
+        if (!initialized)
+            throw new NotInitializedError(
+                    name()~": Block mode not initialized");
+            
+        ubyte[] input = cast(ubyte[]) input_;
+        ubyte[] output = cast(ubyte[]) output_;
+        
+        // Encrypt the counter
+        m_cipher.processBlock(counter, 0, counterOutput, 0);
+        
+        // XOR output with plaintext to create ciphertext
+        for (int i = 0; i < counter.length; i++)
+            output[outOff++] = (counterOutput[i] ^ input[inOff++]);
+            
+        // Increment the counter
+        for (int i = counter.length-1; i >= 0; i--)
+            if (++counter[i]) break;
+
+        return counter.length;  
+    }
+    
+    uint blockSize() {
+        return m_cipher.blockSize;
+    }
+    
+    void reset() {
+        counter[] = iv;
+        m_cipher.reset();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/padding/NullByte.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,44 @@
+/**
+ * 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.padding.NullByte;
+
+import dcrypt.crypto.BlockCipherPadding; 
+
+/**
+ * This class implements Null/Zero byte padding.
+ * Ex. [... 0x00, 0x00 ... 0x00]
+ */
+class NullByte : BlockCipherPadding {
+    char[] name() {
+        return "NullByte";   
+    }
+    
+    /* Assumes input_ is a multiple of the underlying
+     * block cipher's block size.
+     */
+    uint padBlock(void[] input_, uint inOff) {
+        ubyte[] input = cast(ubyte[]) input_;
+
+        uint len = (input.length - inOff);
+        
+        input[inOff..input.length] = 0;
+
+        return len;
+    }
+    
+    uint padLength(void[] input_) {
+        ubyte[] input = cast(ubyte[]) input_;
+        
+        uint len = input.length;
+        while (len-- > 0)
+            if (input[len-1] != 0) break;
+            
+        return (input.length - len);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/padding/PKCS7.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,50 @@
+/**
+ * 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.padding.PKCS7;
+
+import dcrypt.crypto.BlockCipherPadding; 
+
+/**
+ * This class implements the padding scheme described in PKCS7
+ * from RSA Security. Ex. [... 0x03, 0x03, 0x03]
+ */
+class PKCS7 : BlockCipherPadding {
+    char[] name() {
+        return "PKCS7";   
+    }
+    
+    /* Assumes input_ is a multiple of the underlying
+     * block cipher's block size.
+     */
+    uint padBlock(void[] input_, uint inOff) {
+        ubyte[] input = cast(ubyte[]) input_;
+
+        ubyte len = (input.length - inOff);
+        
+        input[inOff..input.length] = len;
+
+        return len;
+    }
+    
+    uint padLength(void[] input_) {
+        ubyte[] input = cast(ubyte[]) input_;
+        
+        ubyte len = input[input.length-1];
+         
+        if (len > input.length || len == 0)
+            throw new InvalidPaddingError(name()~": Incorrect padding.");
+        
+        uint limit = input.length;
+        for (int i = 0; i < len; i++)
+            if (input[--limit] != len)
+                throw new InvalidPaddingError(
+                        name()~": Pad value does not match pad length.");
+        return len;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/padding/RFC1321.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,50 @@
+/**
+ * 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.padding.RFC1321;
+
+import dcrypt.crypto.BlockCipherPadding; 
+
+/** 
+ * This class implements the padding described in RFC1321 (MD5 spec).
+ * Ex. [... 0x80, 0x00 ... 0x00]
+ */
+class RFC1321 : BlockCipherPadding {
+    char[] name() {
+        return "RFC1321";   
+    }
+    
+    /* Assumes input_ is a multiple of the underlying
+     * block cipher's block size.
+     */
+    uint padBlock(void[] input_, uint inOff) {
+        ubyte[] input = cast(ubyte[]) input_;
+
+        uint len = (input.length - inOff);
+        
+        input[inOff++] = 0x80;
+        input[inOff..input.length] = 0;
+
+        return len;
+    }
+    
+    uint padLength(void[] input_) {
+        ubyte[] input = cast(ubyte[]) input_;
+        
+        uint len = input.length;
+        
+        while (len-- > 0)
+            if (input[len] != 0) break;
+            
+        if (input[len] != 0x80)
+            throw new  InvalidPaddingError(
+                    name()~": Incorrect padding.");
+                    
+        return (input.length - len);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/padding/X923.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,46 @@
+/**
+ * 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.padding.X923;
+
+import dcrypt.crypto.BlockCipherPadding; 
+
+/**
+ * This class implements the Null/Zero byte padding described in ANSI X.923.
+ * Ex. [... 0x00, 0x00, 0x03]
+ */
+class X923 : BlockCipherPadding {
+    char[] name() {
+        return "X923";   
+    }
+    
+    /* Assumes input_ is a multiple of the underlying
+     * block cipher's block size.
+     */
+    uint padBlock(void[] input_, uint inOff) {
+        ubyte[] input = cast(ubyte[]) input_;
+
+        ubyte len = (input.length - inOff);
+        
+        input[inOff..input.length-1] = 0;
+        input[input.length-1] = len;
+
+        return len;
+    }
+    
+    uint padLength(void[] input_) {
+        ubyte[] input = cast(ubyte[]) input_;
+        
+        ubyte len = input[input.length-1];
+         
+        if (len > input.length || len == 0)
+            throw new InvalidPaddingError(name()~": Incorrect padding.");
+            
+        return len;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/params/CipherParameters.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,12 @@
+/**
+ * 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.params.CipherParameters;
+
+/** Base class for all cipher parameters. */
+class CipherParameters {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/params/ParametersWithIV.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,63 @@
+/**
+ * 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.params.ParametersWithIV;
+
+public import dcrypt.crypto.params.CipherParameters;
+
+/** Wrap cipher parameters and IV. */
+class ParametersWithIV : CipherParameters {
+    private ubyte[] m_iv;
+    private CipherParameters m_params;
+    
+    /**
+     * Params:
+     *     params = Parameters to wrap.
+     *     iv     = IV to be held.
+     */
+    this(CipherParameters params=null, ubyte[] iv_=null) {
+        if (params)
+            m_params = params;
+        ubyte[] iv = cast(ubyte[]) iv_;
+        if (iv)
+            m_iv = iv;
+    }
+    
+    /** Returns: The IV. */
+    ubyte[] iv() {
+        return m_iv;
+    }
+    
+    /**
+     * Set the IV held by this object.
+     *
+     * Params:
+     *     newIV = The new IV for this parameter object.
+     * Returns: The new IV.
+     */
+    ubyte[] iv(void[] newIV_) {
+        ubyte[] newIV = cast(ubyte[]) newIV_;
+        return m_iv = newIV;
+    }
+    
+    /** Returns: The parameters for this object. */
+    CipherParameters parameters() {
+        return m_params;
+    }
+    
+    /**
+     * Set the parameters held by this object.
+     *
+     * Params:
+     *     newParams = The new parameters to be held.
+     * Returns: The new parameters.
+     */
+    CipherParameters parameters(CipherParameters newParams) {
+        return m_params = newParams;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/params/SymmetricKey.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,43 @@
+/**
+ * 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.params.SymmetricKey;
+
+import dcrypt.crypto.params.CipherParameters;
+
+/** Object representing and wrapping a symmetric key in bytes. */
+class SymmetricKey : CipherParameters {
+    private ubyte[] m_key;
+    
+    /**
+     * Params:
+     *     key = Key to be held.
+     */
+    this(void[] key_=null) {
+        ubyte[] key = cast(ubyte[]) key_;
+        if (key)
+            m_key = key;
+    }
+    
+    /** Returns: Key in ubytes held by this object. */
+    ubyte[] key() {
+        return m_key;
+    }
+    
+    /**
+     * Set the key held by this object.
+     *
+     * Params:
+     *     newKey = New key to be held.
+     * Returns: The new key.
+     */
+    ubyte[] key(void[] newKey_) {
+        ubyte[] newKey = cast(ubyte[])newKey_;
+        return m_key = newKey;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/misc/Util.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,159 @@
+/**
+ * This file is part of the dcrypt project.
+ *
+ * Copyright: Copyright (C) dcrypt contributors 2008. All rights reserved.
+ * License:   MIT
+ * Authors:   Thomas Dixon
+ */
+
+// TODO: WRITE DOCS, IMPLEMENT TO/FROM HEX FUNCTIONS, SIMPLIFY UNITTESTS 
+module dcrypt.misc.Util;
+
+/** Utility functions */
+struct Util {
+    static const char[] hexits = "0123456789abcdef";
+    
+    final static uint ubytesToUintBig(ubyte[] x, uint offset) {
+        return cast(uint) (((x[offset++] & 0xff) << 24) |
+                           ((x[offset++] & 0xff) << 16) |
+                           ((x[offset++] & 0xff) << 8)  |
+                            (x[offset] & 0xff));
+    }
+    
+    final static uint ubytesToUintLittle(ubyte[] x, uint offset) {
+        return cast(uint) ((x[offset++] & 0xff)        |
+                          ((x[offset++] & 0xff) << 8)  |
+                          ((x[offset++] & 0xff) << 16) |
+                          ((x[offset] & 0xff) << 24));
+    }
+    
+    final static ulong ubytesToUlongLittle(ubyte[] x, uint offset) {
+        return cast(ulong) ((x[offset++] & 0xff)        |
+                           ((x[offset++] & 0xff) << 8)  |
+                           ((x[offset++] & 0xff) << 16) |
+                           ((x[offset++] & 0xff) << 24) |
+                           (cast(ulong)(x[offset++] & 0xff) << 32) |
+                           (cast(ulong)(x[offset++] & 0xff) << 40) |
+                           (cast(ulong)(x[offset++] & 0xff) << 48) |
+                           (cast(ulong)(x[offset] & 0xff) << 56));
+    }
+    
+    final static ulong ubytesToUlongBig(ubyte[] x, uint offset) {
+        return cast(ulong) ((cast(ulong)(x[offset++] & 0xff) << 56) |
+                            (cast(ulong)(x[offset++] & 0xff) << 48) |
+                            (cast(ulong)(x[offset++] & 0xff) << 40) |
+                            (cast(ulong)(x[offset++] & 0xff) << 32) |
+                            ((x[offset++] & 0xff) << 24) |
+                            ((x[offset++] & 0xff) << 16) |
+                            ((x[offset++] & 0xff) << 8)  |
+                             (x[offset] & 0xff));
+    }
+    
+    final static ushort ubytesToUshortLittle(ubyte[] x, uint offset) {
+        return cast(ushort) ((x[offset++] & 0xff)       |
+                            ((x[offset] & 0xff) << 8));
+    }
+    
+    final static ushort ubytesToUshortBig(ubyte[] x, uint offset) {
+        return cast(ushort) (((x[offset++] & 0xff) << 8) |
+                              (x[offset] & 0xff));
+    }
+    
+    final static void uintToUbytesBig(uint x, ubyte[] output, uint outOff) {
+        output[outOff++] = cast(ubyte)(x >> 24);
+        output[outOff++] = cast(ubyte)(x >> 16);
+        output[outOff++] = cast(ubyte)(x >> 8);
+        output[outOff] = cast(ubyte)(x);
+    }
+    
+    final static void uintToUbytesLittle(uint x, ubyte[] output, uint outOff) {
+        output[outOff++] = cast(ubyte)(x);
+        output[outOff++] = cast(ubyte)(x >> 8);
+        output[outOff++] = cast(ubyte)(x >> 16);
+        output[outOff] = cast(ubyte)(x >> 24);
+    }
+    
+    final static void ulongToUbytesBig(ulong x, ubyte[] output, uint outOff) {
+        output[outOff++] = cast(ubyte)(x >> 56);
+        output[outOff++] = cast(ubyte)(x >> 48);
+        output[outOff++] = cast(ubyte)(x >> 40);
+        output[outOff++] = cast(ubyte)(x >> 32);
+        output[outOff++] = cast(ubyte)(x >> 24);
+        output[outOff++] = cast(ubyte)(x >> 16);
+        output[outOff++] = cast(ubyte)(x >> 8);
+        output[outOff] = cast(ubyte)(x);
+    }
+    
+    final static void ulongToUbytesLittle(ulong x, ubyte[] output, uint outOff) {
+        output[outOff++] = cast(ubyte)(x);
+        output[outOff++] = cast(ubyte)(x >> 8);
+        output[outOff++] = cast(ubyte)(x >> 16);
+        output[outOff++] = cast(ubyte)(x >> 24);
+        output[outOff++] = cast(ubyte)(x >> 32);
+        output[outOff++] = cast(ubyte)(x >> 40);
+        output[outOff++] = cast(ubyte)(x >> 48);
+        output[outOff] = cast(ubyte)(x >> 56);
+    }
+    
+    final static uint rotateLeft(uint x, uint y) {
+        return (x << y) | (x >> (32-y));
+    }
+    
+    final static ulong rotateLeft64(ulong x, uint y) {
+        return (x << y) | (x >> (64-y));
+    }
+    
+    final static uint rotateRight(uint x, uint y) {
+        return (x >> y) | (x << (32-y));    
+    }
+    
+    final static ulong rotateRight64(ulong x, uint y) {
+        return (x >> y) | (x << (64-y));    
+    }
+    
+    final static char[] ubytesToHex(ubyte[] input) {
+        char[] output = new char[input.length<<1];
+        int i = 0;
+        foreach (ubyte j; input) { 
+            output[i++] = hexits[j >> 4];
+            output[i++] = hexits[j & 0xf];
+        }
+        return output;    
+    }
+    
+    final static ubyte[] hexToUbytes(char[] input) {
+        ubyte[] output = new ubyte[input.length>>1];
+        static ubyte[char] hexitIndex;
+        for (int i = 0; i < hexits.length; i++)
+            hexitIndex[hexits[i]] = i;
+        for (int i = 0, j = 0; i < output.length; i++) {
+            output[i] = hexitIndex[input[j++]] << 4;
+            output[i] |= hexitIndex[input[j++]]; 
+        }
+        return output;
+    }
+    
+    final static char[] stringToLower(char[] input) {
+        char[] output = new char[input.length];
+        foreach (uint i, char c; input) 
+            output[i] = ((c >= 'A' && c <= 'Z') ? c+32 : c);
+        return output;
+    }
+    
+    final static char[] stringToUpper(char[] input) {
+        char[] output = new char[input.length];
+        foreach (uint i, char c; input) 
+            output[i] = ((c >= 'a' && c <= 'z') ? c-32 : c);
+        return output;
+    }
+    
+    final static char[] stringToAlphanumeric(char[] input) {
+        char[] output = "";
+        foreach (char c; input)
+            if ((c >= 'a' && c <= 'z') ||
+                (c >= 'A' && c <= 'Z') ||
+                (c >= '0' && c <= '9'))
+                    output ~= c;
+        return output;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dsss.conf	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,2 @@
+name = dcrypt
+[dcrypt]