changeset 12:8c7f8fecdd75

Added ManagedBlockCipher, changed Crypto to just import everything, made Hash.update() return itself (for chaining) and ditched BlockCipherWrapper.
author Thomas Dixon <reikon@reikon.us>
date Sat, 30 Aug 2008 14:38:23 -0400
parents 02970e63257d
children 7ea528b61802
files dcrypt/crypto/BlockCipherWrapper.d dcrypt/crypto/Cipher.d dcrypt/crypto/Crypto.d dcrypt/crypto/Hash.d dcrypt/crypto/ManagedBlockCipher.d dcrypt/crypto/ciphers/AES.d dcrypt/crypto/ciphers/Blowfish.d dcrypt/crypto/ciphers/RC4.d dcrypt/crypto/ciphers/RC6.d dcrypt/crypto/ciphers/TEA.d dcrypt/crypto/ciphers/XTEA.d dcrypt/crypto/modes/CBC.d dcrypt/crypto/modes/CTR.d
diffstat 13 files changed, 319 insertions(+), 286 deletions(-) [+]
line wrap: on
line diff
--- a/dcrypt/crypto/BlockCipherWrapper.d	Wed Aug 20 23:33:02 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/**
- * 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.BlockCipherWrapper;
-
-import dcrypt.crypto.BlockCipher;
-import dcrypt.crypto.BlockCipherPadding;
-
-/** 
- * Wraps a block cipher, enabling the encryption of a stream
- * whose length is a multiple of blockSize. Padding, if specified,
- * is to be applied otherwise and the result subsequently processed.
- */
-class BlockCipherWrapper : BlockCipher {
-    BlockCipher cipher;
-    BlockCipherPadding padding;
-    bool encrypt;
-    
-    this(BlockCipher c, BlockCipherPadding p=null) {
-        cipher = c;
-        padding = p; // null signifies no padding is to be applied
-    }
-    
-    void init(bool encrypt, CipherParameters params) {
-        this.encrypt = encrypt;
-        cipher.init(encrypt, params);
-    }
-    
-    char[] name() {
-        if (padding !is null)
-            return cipher.name~"/"~padding.name;
-        return cipher.name;
-    }
-    
-    uint blockSize() {
-        return cipher.blockSize;
-    }
-    
-    ubyte[] process(void[] input_) {
-        ubyte[] input = cast(ubyte[]) input_;
-        
-        uint padLen = blockSize - (input.length % blockSize);
-        if (padding !is null && !encrypt && padLen != blockSize)
-            throw new ShortBufferError(
-                    name()~": Encrypted padded block should be multiple of block size.");
-        
-        ubyte[] output = new ubyte[input.length];
-        
-        uint len = input.length,
-             i = 0;
-        while (len >= blockSize) {
-            output[i..i+blockSize] = cipher.process(input[i..i+blockSize]);
-            len -= blockSize;
-            i += blockSize;
-        }
-                
-        if (padding !is null) {
-            if (encrypt) {
-                output.length = output.length + padLen;
-                ubyte[] t = new ubyte[blockSize];
-                if (len > 0) {
-                    t[0..len] = input[i..i+len];
-                    t[len..blockSize] = padding.pad(padLen);
-                } else
-                    t[] = padding.pad(padLen);
-                output[i..i+blockSize] = cipher.process(t);
-            } else {
-                output.length = output.length - padding.unpad(output);
-            }
-        } else
-            if (len > 0)
-                output[i..i+len] = cipher.process(input[i..i+len]);
-            
-        return output;
-    }
-    
-    void reset() {
-        cipher.reset();
-    }
-}
\ No newline at end of file
--- a/dcrypt/crypto/Cipher.d	Wed Aug 20 23:33:02 2008 -0400
+++ b/dcrypt/crypto/Cipher.d	Sat Aug 30 14:38:23 2008 -0400
@@ -31,14 +31,15 @@
     
     /**
      * Process a block of plaintext data from the input array
-     * and return the encrypted data.
+     * and place it in the output array.
      *
      * Params:
      *     input_  = Array containing input data.
+     *     output_  = Array to hold the output data.
      *
-     * Returns: The encrypted data.
+     * Returns: The amount of encrypted data processed.
      */
-    ubyte[] process(void[] input_);
+    uint update(void[] input_, void[] output_);
     
     /** Returns: The name of the algorithm of this cipher. */
     char[] name();
--- a/dcrypt/crypto/Crypto.d	Wed Aug 20 23:33:02 2008 -0400
+++ b/dcrypt/crypto/Crypto.d	Sat Aug 30 14:38:23 2008 -0400
@@ -6,16 +6,17 @@
  * Authors:   Thomas Dixon
  */
 
+/** Import _everything_ */
 module dcrypt.crypto.Crypto;
 
 // 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;
+public import dcrypt.crypto.hashes.MD5;
+public import dcrypt.crypto.hashes.SHA1;
+public import dcrypt.crypto.hashes.SHA224;
+public import dcrypt.crypto.hashes.SHA256;
+public import dcrypt.crypto.hashes.SHA384;
+public import dcrypt.crypto.hashes.SHA512;
 
 // Message authentication codes
 public import dcrypt.crypto.MAC;
@@ -29,138 +30,22 @@
 public import dcrypt.crypto.Cipher;
 public import dcrypt.crypto.BlockCipher;
 public import dcrypt.crypto.StreamCipher;
-public import dcrypt.crypto.BlockCipherWrapper;
-import dcrypt.crypto.ciphers.Blowfish;
-import dcrypt.crypto.ciphers.RC6;
-import dcrypt.crypto.ciphers.TEA;
-import dcrypt.crypto.ciphers.XTEA;
-import dcrypt.crypto.ciphers.AES;
-import dcrypt.crypto.ciphers.RC4;
+public import dcrypt.crypto.ManagedBlockCipher;
+public import dcrypt.crypto.ciphers.Blowfish;
+public import dcrypt.crypto.ciphers.RC6;
+public import dcrypt.crypto.ciphers.TEA;
+public import dcrypt.crypto.ciphers.XTEA;
+public import dcrypt.crypto.ciphers.AES;
+public import dcrypt.crypto.ciphers.RC4;
 
 // Block modes
-import dcrypt.crypto.modes.CBC;
-import dcrypt.crypto.modes.CTR;
+public import dcrypt.crypto.modes.CBC;
+public import dcrypt.crypto.modes.CTR;
 
 // Block padding
 public import dcrypt.crypto.BlockCipherPadding;
-import dcrypt.crypto.padding.NullByte;
-import dcrypt.crypto.padding.X923;
-import dcrypt.crypto.padding.PKCS7;
-import dcrypt.crypto.padding.RFC1321;
-
-enum Hashes {
-    MD5=0,
-    SHA1,
-    SHA224,
-    SHA256,
-    SHA384,
-    SHA512
-}
-
-enum Ciphers {
-    // Block ciphers
-    Blowfish=0,
-    AES,
-    RC6,
-    TEA,
-    XTEA,
-    
-    // Stream ciphers
-    RC4
-}
-
-enum Modes {
-    ECB=-1,
-    CTR,
-    CBC
-}
-
-enum Padding {
-    None=-1,
-    NullByte, // lol
-    X923,
-    PKCS7,
-    RFC1321
-}
+public import dcrypt.crypto.padding.NullByte;
+public import dcrypt.crypto.padding.X923;
+public import dcrypt.crypto.padding.PKCS7;
+public import dcrypt.crypto.padding.RFC1321;
 
-struct Crypto {
-    static Hash createHash(uint hash) {
-        switch (hash) {
-            case Hashes.MD5:
-                return new MD5;
-            case Hashes.SHA1:
-                return new SHA1;
-            case Hashes.SHA224:
-                return new SHA224;
-            case Hashes.SHA256:
-                return new SHA256;
-            case Hashes.SHA384:
-                return new SHA384;
-            case Hashes.SHA512:
-                return new SHA512;
-            default:
-                throw new InvalidParameterError("Unknown hash function passed to createHash()");
-        }
-    }
-    
-    static Cipher createCipher(uint cipher, int mode=Modes.ECB, int padding=Padding.None) {
-        BlockCipher c = null,
-                    m = null;
-        BlockCipherPadding p = null;
-        switch (cipher) {
-            case Ciphers.Blowfish:
-                c = new Blowfish;
-                break;
-            case Ciphers.AES:
-                c = new AES;
-                break;
-            case Ciphers.RC6:
-                c = new RC6;
-                break;
-            case Ciphers.TEA:
-                c = new TEA;
-                break;
-            case Ciphers.XTEA:
-                c = new XTEA;
-                break;
-            case Ciphers.RC4:
-                return new RC4; // Note the return
-            default:
-                throw new InvalidParameterError("Unknown cipher passed to createCipher()");
-        }
-        
-        switch (mode) {
-            case Modes.ECB:
-                break;
-            case Modes.CTR:
-                m = new CTR(c);
-                break;
-            case Modes.CBC:
-                m = new CBC(c);
-                break;
-            default:
-                throw new InvalidParameterError("Unknown mode passed to createCipher()");
-        }
-        
-        switch (padding) {
-            case Padding.None:
-                break;
-            case Padding.NullByte:
-                p = new NullByte;
-                break;
-            case Padding.X923:
-                p = new X923;
-                break;
-            case Padding.PKCS7:
-                p = new PKCS7;
-                break;
-            case Padding.RFC1321:
-                p = new RFC1321;
-                break;
-            default:
-                throw new InvalidParameterError("Unknown padding passed to createCipher()");
-        }
-        
-        return new BlockCipherWrapper(((m is null) ? c : m), p);
-    }
-}
--- a/dcrypt/crypto/Hash.d	Wed Aug 20 23:33:02 2008 -0400
+++ b/dcrypt/crypto/Hash.d	Sat Aug 30 14:38:23 2008 -0400
@@ -48,8 +48,10 @@
      * 
      * Params:
      *     input_ = Data to be processed.
+     *     
+     * Returns: Self
      */
-    void update(void[] input_) {
+    Hash update(void[] input_) {
         ubyte[] input = cast(ubyte[]) input_;
         foreach (ubyte i; input) {
             bytes++;
@@ -59,7 +61,7 @@
                 index = 0;
             }   
         }
-            
+        return this;
     }
     
     /** Hash function's internal transformation. */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/ManagedBlockCipher.d	Sat Aug 30 14:38:23 2008 -0400
@@ -0,0 +1,199 @@
+/**
+ * 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.ManagedBlockCipher;
+
+public import dcrypt.crypto.BlockCipher;
+import dcrypt.crypto.BlockCipherPadding;
+
+/** 
+ * Wraps a block cipher, enabling the encryption of a stream.
+ * Padding, if specified, is to be applied in the finish() call.
+ * 
+ * Based on PaddedBufferedBlockCipher from BC.
+ */
+class ManagedBlockCipher : BlockCipher {
+    BlockCipher cipher;
+    BlockCipherPadding padding;
+    
+    protected {
+        ubyte[] buffer;
+        uint index;
+        bool encrypt,
+             streamMode = false;
+    }
+    
+    /**
+     * Create a managed block cipher.
+     * 
+     * Params:
+     *     cipher = Block cipher we're wrapping
+     *     padding = Padding or null if no padding
+     *     
+     * Returns: A new ManagedBlockCipher
+     */
+    this(BlockCipher cipher, BlockCipherPadding padding=null) {
+        this.cipher = cipher;
+        
+        char[] mode = cipher.name;
+        int i;
+        for (i = 0; i < mode.length; i++)
+            if (mode[i] == '/')
+                break;
+        if (i < mode.length) {
+            mode = mode[i+1..i+4];
+            this.streamMode = (mode == "CTR" || mode == "CFB" || mode == "OFB");
+        }
+        
+        this.padding = padding; // null signifies no padding is to be applied
+        buffer = new ubyte[blockSize];
+    }
+    
+    void init(bool encrypt, CipherParameters params) {
+        this.encrypt = encrypt;
+        cipher.init(encrypt, params);
+    }
+     
+    char[] name() {
+        if (padding is null)
+            return cipher.name;
+        return cipher.name~"/"~padding.name;
+    }
+    
+    uint blockSize() {
+        return cipher.blockSize;
+    }
+    
+    /**
+     * Update the cipher object with data from input_ and if it fills
+     * a block, place it in output.
+     * 
+     * Returns: The number of bytes placed in output_.
+     */
+    uint update(void[] input_, void[] output_) {
+        ubyte[] input = cast(ubyte[]) input_,
+                output = cast(ubyte[]) output_;
+        
+        if (encrypt && input.length > output.length)
+            throw new ShortBufferError("Managed "~name()~": Output buffer too short");
+        
+        uint result = 0,
+             len = input.length,
+             diff = buffer.length - index,
+             i = 0;
+        if (len > diff) {
+            buffer[index..buffer.length] = input[i..diff];
+            result += cipher.update(buffer, output[i..i+blockSize]);
+            index = 0;
+            len -= diff;
+            i += blockSize;
+            
+            while (len > blockSize) {
+                result += cipher.update(input[i..i+blockSize], output[i..i+blockSize]);
+                len -= blockSize;
+                i += blockSize;
+            }
+        }
+        
+        buffer[0..len] = input[i..i+len];
+        index += len;
+        
+        return result;
+    }
+    
+    /**
+     * Finalize the cipher, passing all remaining buffered input
+     * through the cipher (padding it first, if specified) and
+     * subsequently placing it in output_.
+     * 
+     * Returns: The number of bytes placed in output_.
+     */
+    uint finish(void[] output_) {
+        ubyte[] output = cast(ubyte[]) output_;
+        uint result = 0;
+        if (encrypt) {
+            if (index == blockSize) {
+                if (padding !is null && output.length < (blockSize << 1))
+                    throw new ShortBufferError("Managed "~name()~": Output buffer too short");
+                result += cipher.update(buffer, output[result..result+blockSize]);
+                index = 0;
+            }
+
+            if (padding !is null) {
+                uint diff = buffer.length - index;
+                buffer[index..buffer.length] = padding.pad(diff);
+                index += diff;
+            }
+            
+            if (index)
+                result += cipher.update(buffer[0..index], output[result..result+index]);
+        } else {
+            if (streamMode || index == blockSize) {
+                result += cipher.update(buffer[0..index], buffer[0..index]);
+                index = 0;
+            } else {
+                reset();
+                throw new ShortBufferError(
+                        "Managed "~name()~": Padded last block not equal to cipher's blocksize");
+            }
+            try {
+                if (padding !is null)
+                    result -= padding.unpad(buffer);
+                output[0..result] = buffer[0..result];
+            } finally {
+                reset();
+            }
+        }
+        reset();
+        return result;
+    }
+    
+    /**
+     * Params:
+     *     len = Number of bytes you plan on passing to update()
+     * 
+     * Returns: The number of bytes to be output upon a call to update()
+     *          with an input length of len bytes. 
+     */
+    uint updateOutputSize(uint len) {
+        uint result = len + index;
+        return result - (result % blockSize);
+    }
+    
+    /**
+     * Params:
+     *     len = Number of bytes you plan on passing to update()
+     * 
+     * Returns: The number of bytes to be output with a call to update()
+     *          using an input of len bytes, followed by a call to finish().
+     *          This method takes into account padding, mode, etc. Will
+     *          return 0 if your input is likely to error (i.e. len is 14
+     *          for AES in ECB mode).
+     */
+    uint finishOutputSize(uint len) {
+        uint result = len + index,
+             diff = result % blockSize;
+        
+        // Input is a multiple of block size
+        if (!diff)
+            return result;
+        
+        // No padding, return result if stream mode, 0 if not (it'll error)
+        if (padding is null)
+            return (streamMode ? result : 0);
+        
+        // Padding, return len(input+padding) if encrypting or 0 if not (it'll error)
+        return (encrypt ? result - diff + blockSize : 0);
+    }
+    
+    void reset() {
+        cipher.reset();
+        index = 0;
+        buffer[] = 0;
+    }
+}
--- a/dcrypt/crypto/ciphers/AES.d	Wed Aug 20 23:33:02 2008 -0400
+++ b/dcrypt/crypto/ciphers/AES.d	Sat Aug 30 14:38:23 2008 -0400
@@ -800,14 +800,18 @@
                        RS[cast(ubyte) t0];
     }
     
-    ubyte[] process(void[] input_) {
+    uint update(void[] input_, void[] output_) {
         if (!initialized)
             throw new NotInitializedError(name()~": Cipher not initialized.");
         
-        ubyte[] input = cast(ubyte[]) input_;
+        ubyte[] input = cast(ubyte[]) input_,
+                output = cast(ubyte[]) output_;
                     
-        if (input.length < blockSize)
+        if (input.length < BLOCK_SIZE)
             throw new ShortBufferError(name()~": Input buffer too short");
+            
+        if (output.length < BLOCK_SIZE)
+            throw new ShortBufferError(name()~": Output buffer too short");
         
         s0 = w[0] ^ Util.ubytesToUintBig(input, 0);
         s1 = w[1] ^ Util.ubytesToUintBig(input, 4);
@@ -816,13 +820,12 @@
              
         if (encrypt) encryptBlock(); else decryptBlock();
 
-        ubyte[] output = new ubyte[blockSize];
         Util.uintToUbytesBig(s0, output, 0);
         Util.uintToUbytesBig(s1, output, 4);
         Util.uintToUbytesBig(s2, output, 8);
         Util.uintToUbytesBig(s3, output, 12);
         
-        return output;
+        return BLOCK_SIZE;
     }
     
     void reset() {}
@@ -905,14 +908,14 @@
                 
                 // Encryption
                 t.init(true, key);
-                buffer = t.process(Util.hexToUbytes(test_plaintexts[i]));
+                t.update(Util.hexToUbytes(test_plaintexts[i]), buffer);
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_ciphertexts[i],
                         t.name~": ("~result~") != ("~test_ciphertexts[i]~")");
             
                 // Decryption
                 t.init(false, key);
-                buffer = t.process(Util.hexToUbytes(test_ciphertexts[i]));
+                t.update(Util.hexToUbytes(test_ciphertexts[i]), buffer);
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_plaintexts[i],
                         t.name~": ("~result~") != ("~test_plaintexts[i]~")");
--- a/dcrypt/crypto/ciphers/Blowfish.d	Wed Aug 20 23:33:02 2008 -0400
+++ b/dcrypt/crypto/ciphers/Blowfish.d	Sat Aug 30 14:38:23 2008 -0400
@@ -255,14 +255,18 @@
                 + S3[cast(ubyte)x]);
     }
     
-    ubyte[] process(void[] input_) {
+    uint update(void[] input_, void[] output_) {
         if (!initialized)
             throw new NotInitializedError(name()~": Cipher not initialized.");
         
-        ubyte[] input = cast(ubyte[]) input_;
+        ubyte[] input = cast(ubyte[]) input_,
+                output = cast(ubyte[]) output_;
                     
-        if (input.length < blockSize)
+        if (input.length < BLOCK_SIZE)
             throw new ShortBufferError(name()~": Input buffer too short");
+            
+        if (output.length < BLOCK_SIZE)
+            throw new ShortBufferError(name()~": Output buffer too short");
         
         uint xl = Util.ubytesToUintBig(input, 0),
              xr = Util.ubytesToUintBig(input, 4),
@@ -275,11 +279,10 @@
         }
         xr ^= P[i];
   
-        ubyte[] output = new ubyte[blockSize];
         Util.uintToUbytesBig(xr, output, 0);
         Util.uintToUbytesBig(xl, output, 4);
         
-        return output;
+        return BLOCK_SIZE;
     }
     
     void reset() {
@@ -301,31 +304,31 @@
         
         ubyte[] t = new ubyte[BLOCK_SIZE]; // Initialized to 0's
         for (int i = 0; i < PBOX_SIZE;) {
-            t = process(t);
+            update(t, t);
             P[i++] = Util.ubytesToUintBig(t, 0);
             P[i++] = Util.ubytesToUintBig(t, 4);
         }
         
         for (int i = 0; i < SBOX_SIZE;) {
-            t = process(t);
+            update(t, t);
             S0[i++] = Util.ubytesToUintBig(t, 0);
             S0[i++] = Util.ubytesToUintBig(t, 4);
         }
         
         for (int i = 0; i < SBOX_SIZE;) {
-            t = process(t);
+            update(t, t);
             S1[i++] = Util.ubytesToUintBig(t, 0);
             S1[i++] = Util.ubytesToUintBig(t, 4);
         }
         
         for (int i = 0; i < SBOX_SIZE;) {
-            t = process(t);
+            update(t, t);
             S2[i++] = Util.ubytesToUintBig(t, 0);
             S2[i++] = Util.ubytesToUintBig(t, 4);
         }
         
         for (int i = 0; i < SBOX_SIZE;) {
-            t = process(t);
+            update(t, t);
             S3[i++] = Util.ubytesToUintBig(t, 0);
             S3[i++] = Util.ubytesToUintBig(t, 4);
         }
@@ -375,14 +378,14 @@
                 
                 // Encryption
                 t.init(true, key);
-                buffer = t.process(Util.hexToUbytes(test_plaintexts[i]));
+                t.update(Util.hexToUbytes(test_plaintexts[i]), buffer);
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_ciphertexts[i],
                         t.name~": ("~result~") != ("~test_ciphertexts[i]~")");
             
                 // Decryption
                 t.init(false, key);
-                buffer = t.process(Util.hexToUbytes(test_ciphertexts[i]));
+                t.update(Util.hexToUbytes(test_ciphertexts[i]), buffer);
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_plaintexts[i],
                         t.name~": ("~result~") != ("~test_plaintexts[i]~")");
--- a/dcrypt/crypto/ciphers/RC4.d	Wed Aug 20 23:33:02 2008 -0400
+++ b/dcrypt/crypto/ciphers/RC4.d	Sat Aug 30 14:38:23 2008 -0400
@@ -49,12 +49,15 @@
         return (input^state[cast(ubyte)(state[x]+state[y])]);
     }
     
-    ubyte[] process(void[] input_) {
+    uint update(void[] input_, void[] output_) {
         if (!initialized)
             throw new NotInitializedError(name()~": Cipher not initialized");
             
         ubyte[] input = cast(ubyte[]) input_,
-                output = new ubyte[input.length];
+                output = cast(ubyte[]) output_;
+            
+        if (input.length > output.length)
+            throw new ShortBufferError(name()~": Output buffer too short");
             
         for (int i = 0; i < input.length; i++) {
             y += state[++x];
@@ -63,7 +66,7 @@
             state[y] = t;
             output[i] = input[i] ^ state[cast(ubyte)(state[x]+state[y])];
         }
-        return output;
+        return input.length;
     }
     
     void reset() { 
@@ -182,7 +185,7 @@
                 r.init(true, new SymmetricKey(Util.hexToUbytes(test_key)));
                 
                 // Encryption
-                buffer = r.process(Util.hexToUbytes(test_plaintexts[i]));
+                r.update(Util.hexToUbytes(test_plaintexts[i]), buffer);
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_ciphertexts[i],
                         r.name~": ("~result~") != ("~test_ciphertexts[i]~")");
@@ -190,7 +193,7 @@
                 r.reset();
                 
                 // Decryption
-                buffer = r.process(Util.hexToUbytes(test_ciphertexts[i]));
+                r.update(Util.hexToUbytes(test_ciphertexts[i]), buffer);
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_plaintexts[i],
                         r.name~": ("~result~") != ("~test_plaintexts[i]~")");
--- a/dcrypt/crypto/ciphers/RC6.d	Wed Aug 20 23:33:02 2008 -0400
+++ b/dcrypt/crypto/ciphers/RC6.d	Sat Aug 30 14:38:23 2008 -0400
@@ -65,14 +65,18 @@
         initialized = true;
     }
     
-    ubyte[] process(void[] input_) {
+    uint update(void[] input_, void[] output_) {
         if (!initialized)
             throw new NotInitializedError(name()~": Cipher not initialized");
             
-        ubyte[] input = cast(ubyte[]) input_;
+        ubyte[] input = cast(ubyte[]) input_,
+                output = cast(ubyte[]) output_;
                     
-        if (input.length > blockSize)
+        if (input.length < BLOCK_SIZE)
             throw new ShortBufferError(name()~": Input buffer too short");
+            
+        if (output.length < BLOCK_SIZE)
+            throw new ShortBufferError(name()~": Output buffer too short");
         
         uint A = Util.ubytesToUintLittle(input, 0),
              B = Util.ubytesToUintLittle(input, 4),
@@ -115,13 +119,12 @@
             B -= S[0];
         }
 
-        ubyte[] output = new ubyte[blockSize];
         Util.uintToUbytesLittle(A, output, 0);
         Util.uintToUbytesLittle(B, output, 4);
         Util.uintToUbytesLittle(C, output, 8);
         Util.uintToUbytesLittle(D, output, 12);
         
-        return output;
+        return BLOCK_SIZE;
     }
     
     void reset() {
@@ -192,14 +195,14 @@
                 
                 // Encryption
                 t.init(true, key);
-                buffer = t.process(Util.hexToUbytes(test_plaintexts[i]));
+                t.update(Util.hexToUbytes(test_plaintexts[i]), buffer);
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_ciphertexts[i],
                         t.name~": ("~result~") != ("~test_ciphertexts[i]~")");
             
                 // Decryption
                 t.init(false, key);
-                buffer = t.process(Util.hexToUbytes(test_ciphertexts[i]));
+                t.update(Util.hexToUbytes(test_ciphertexts[i]), buffer);
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_plaintexts[i],
                         t.name~": ("~result~") != ("~test_plaintexts[i]~")");
--- a/dcrypt/crypto/ciphers/TEA.d	Wed Aug 20 23:33:02 2008 -0400
+++ b/dcrypt/crypto/ciphers/TEA.d	Sat Aug 30 14:38:23 2008 -0400
@@ -54,14 +54,18 @@
         initialized = true;
     }
     
-    ubyte[] process(void[] input_) {
+    uint update(void[] input_, void[] output_) {
         if (!initialized)
             throw new NotInitializedError(name()~": Cipher not initialized");
             
-        ubyte[] input = cast(ubyte[]) input_;
+        ubyte[] input = cast(ubyte[]) input_,
+                output = cast(ubyte[]) output_;
                     
-        if (input.length < blockSize)
+        if (input.length < BLOCK_SIZE)
             throw new ShortBufferError(name()~": Input buffer too short");
+            
+        if (output.length < BLOCK_SIZE)
+            throw new ShortBufferError(name()~": Output buffer too short");
         
         uint v0 = Util.ubytesToUintBig(input, 0),
              v1 = Util.ubytesToUintBig(input, 4);
@@ -79,11 +83,10 @@
             }
         }
         
-        ubyte[] output = new ubyte[blockSize];
         Util.uintToUbytesBig(v0, output, 0);
         Util.uintToUbytesBig(v1, output, 4);
         
-        return output;
+        return BLOCK_SIZE;
     }
     
     /** Some TEA test vectors. */
@@ -118,14 +121,14 @@
             
             // Encryption
             t.init(true, key);
-            buffer = t.process(Util.hexToUbytes(test_plaintexts[i]));
+            t.update(Util.hexToUbytes(test_plaintexts[i]), buffer);
             result = Util.ubytesToHex(buffer);
             assert(result == test_ciphertexts[i],
                     t.name~": ("~result~") != ("~test_ciphertexts[i]~")");
 
             // Decryption
             t.init(false, key);
-            buffer = t.process(Util.hexToUbytes(test_ciphertexts[i]));
+            t.update(Util.hexToUbytes(test_ciphertexts[i]), buffer);
             result = Util.ubytesToHex(buffer);
             assert(result == test_plaintexts[i],
                     t.name~": ("~result~") != ("~test_plaintexts[i]~")");
--- a/dcrypt/crypto/ciphers/XTEA.d	Wed Aug 20 23:33:02 2008 -0400
+++ b/dcrypt/crypto/ciphers/XTEA.d	Sat Aug 30 14:38:23 2008 -0400
@@ -64,14 +64,18 @@
         initialized = true;
     }
     
-    ubyte[] process(void[] input_) {
+    uint update(void[] input_, void[] output_) {
         if (!initialized)
             throw new NotInitializedError(name()~": Cipher not initialized");
             
-        ubyte[] input = cast(ubyte[]) input_;
+        ubyte[] input = cast(ubyte[]) input_,
+                output = cast(ubyte[]) output_;
                     
-        if (input.length < blockSize)
+        if (input.length < BLOCK_SIZE)
             throw new ShortBufferError(name()~": Input buffer too short");
+            
+        if (output.length < BLOCK_SIZE)
+            throw new ShortBufferError(name()~": Output buffer too short");
         
         uint v0 = Util.ubytesToUintBig(input, 0),
              v1 = Util.ubytesToUintBig(input, 4);
@@ -88,11 +92,10 @@
             }
         }
         
-        ubyte[] output = new ubyte[blockSize];
         Util.uintToUbytesBig(v0, output, 0);
         Util.uintToUbytesBig(v1, output, 4);
         
-        return output;
+        return BLOCK_SIZE;
     }
     
     /** Some XTEA test vectors. */
@@ -145,14 +148,14 @@
                 
                 // Encryption
                 t.init(true, key);
-                buffer = t.process(Util.hexToUbytes(test_plaintexts[i]));
+                t.update(Util.hexToUbytes(test_plaintexts[i]), buffer);
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_ciphertexts[i],
                         t.name~": ("~result~") != ("~test_ciphertexts[i]~")");
     
                 // Decryption
                 t.init(false, key);
-                buffer = t.process(Util.hexToUbytes(test_ciphertexts[i]));
+                t.update(Util.hexToUbytes(test_ciphertexts[i]), buffer);
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_plaintexts[i],
                         t.name~": ("~result~") != ("~test_plaintexts[i]~")");
--- a/dcrypt/crypto/modes/CBC.d	Wed Aug 20 23:33:02 2008 -0400
+++ b/dcrypt/crypto/modes/CBC.d	Sat Aug 30 14:38:23 2008 -0400
@@ -9,7 +9,7 @@
 module dcrypt.crypto.modes.CBC;
 
 import dcrypt.crypto.BlockCipher;
-import dcrypt.crypto.params.ParametersWithIV;
+public import dcrypt.crypto.params.ParametersWithIV;
 
 version (UnitTest) {
     import dcrypt.crypto.ciphers.XTEA;
@@ -67,15 +67,19 @@
         initialized = true;
     }
     
-    ubyte[] process(void[] input_) {
+    uint update(void[] input_, void[] output_) {
         if (!initialized)
             throw new NotInitializedError(
                     name()~": Block mode not initialized");
             
-        ubyte[] input = cast(ubyte[]) input_;
-        
+        ubyte[] input = cast(ubyte[]) input_,
+                output = cast(ubyte[]) output_;
+                    
         if (input.length < blockSize)
             throw new ShortBufferError(name()~": Input buffer too short");
+            
+        if (output.length < blockSize)
+            throw new ShortBufferError(name()~": Output buffer too short");
         
         if (encrypt) {
             // P_i XOR C_i-1
@@ -83,27 +87,29 @@
                 previousCiphertext[i] ^= input[i];
                 
             // E_k(P_i XOR C_i-1)
-            cbcOutput[] = m_cipher.process(previousCiphertext);
+            m_cipher.update(previousCiphertext, cbcOutput);
             
             // Store C_i for next block
             previousCiphertext[] = cbcOutput;
             
+            // C_i = E_k(P_i XOR C_i-1)
+            output[0..blockSize] = cbcOutput;            
         } else {
             // Temporarily store C_i
             ubyte[] t = input[0..blockSize];
 
             // D_k(C_i)
-            cbcOutput[] = m_cipher.process(t);
+            m_cipher.update(t, cbcOutput);
             
             // P_i = D_k(C_i) XOR C_i-1
             for (int i = 0; i < blockSize; i++)
-                cbcOutput[i] ^= previousCiphertext[i];
+                output[i] = (cbcOutput[i] ^ previousCiphertext[i]);
              
             // Store C_i for next block
             previousCiphertext[] = t;
        }
         
-        return cbcOutput;
+        return blockSize;
     }
     
     uint blockSize() {
@@ -145,7 +151,7 @@
                 "c09d3c606614d84b8d184fa29c5cb5f6"~
                 "f26fa5a0b6b63ba0f7ebf2f8735f85e3"
             ];
-            
+
             XTEA x = new XTEA();
             CBC c = new CBC(x);
             ubyte[] iv = new ubyte[x.blockSize], // Initialized to 0
@@ -158,7 +164,7 @@
                 // Encryption
                 c.init(true, params);
                 for (int j = 0; j < 32; j+=x.blockSize)
-                    buffer[j..j+x.blockSize] = c.process(Util.hexToUbytes(test_plaintexts[i])[j..j+x.blockSize]);
+                    c.update(Util.hexToUbytes(test_plaintexts[i])[j..j+x.blockSize], buffer[j..j+x.blockSize]);
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_ciphertexts[i],
                         c.name()~": ("~result~") != ("~test_ciphertexts[i]~")");           
@@ -166,7 +172,7 @@
                 // Decryption
                 c.init(false, params);
                 for (int j = 0; j < 32; j+=x.blockSize)
-                    buffer[j..j+x.blockSize] = c.process(Util.hexToUbytes(test_ciphertexts[i])[j..j+x.blockSize]);
+                    c.update(Util.hexToUbytes(test_ciphertexts[i])[j..j+x.blockSize], buffer[j..j+x.blockSize]);
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_plaintexts[i],
                         c.name()~": ("~result~") != ("~test_plaintexts[i]~")");
--- a/dcrypt/crypto/modes/CTR.d	Wed Aug 20 23:33:02 2008 -0400
+++ b/dcrypt/crypto/modes/CTR.d	Sat Aug 30 14:38:23 2008 -0400
@@ -9,7 +9,7 @@
 module dcrypt.crypto.modes.CTR;
 
 import dcrypt.crypto.BlockCipher;
-import dcrypt.crypto.params.ParametersWithIV;
+public import dcrypt.crypto.params.ParametersWithIV;
 
 
 /** This class implements the counter (CTR/SIC/ICM) block mode,
@@ -62,16 +62,21 @@
         initialized = true;
     }
     
-    ubyte[] process(void[] input_) {
+    uint update(void[] input_, void[] output_) {
         if (!initialized)
             throw new NotInitializedError(
                     name()~": Block mode not initialized");
             
-        ubyte[] input = cast(ubyte[]) input_;
+        ubyte[] input = cast(ubyte[]) input_,
+                output = cast(ubyte[]) output_;
+                
         uint len = (counter.length > input.length) ? input.length : counter.length;
         
+        if (len > output.length)
+            throw new ShortBufferError(name()~": Output buffer too short");
+        
         // Encrypt the counter
-        counterOutput[] = m_cipher.process(counter);
+        m_cipher.update(counter, counterOutput);
         
         // XOR output with plaintext to create ciphertext
         for (int i = 0; i < len; i++)
@@ -81,7 +86,9 @@
         for (int i = counter.length-1; i >= 0; i--)
             if (++counter[i]) break;
 
-        return counterOutput[0..len];  
+        output[0..len] = counterOutput[0..len];
+        
+        return len;
     }
     
     uint blockSize() {