changeset 8:23c62e28b3a4

Reworked symmetric cipher classes to have SymmetricCipher as their superclass, and follow the general interface of init(), process(), etc. Made sure everything still passed test vectors. Removed Cipher class. I'll worry about that shit when we support something other than symmetric ciphers.
author Thomas Dixon <reikon@reikon.us>
date Mon, 18 Aug 2008 01:14:37 -0400
parents 23e6e80f8ee3
children 29b910949588
files dcrypt/crypto/BlockCipher.d dcrypt/crypto/Cipher.d dcrypt/crypto/StreamCipher.d dcrypt/crypto/SymmetricCipher.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 12 files changed, 124 insertions(+), 205 deletions(-) [+]
line wrap: on
line diff
--- a/dcrypt/crypto/BlockCipher.d	Sat Aug 16 22:55:38 2008 -0400
+++ b/dcrypt/crypto/BlockCipher.d	Mon Aug 18 01:14:37 2008 -0400
@@ -8,29 +8,12 @@
  
 module dcrypt.crypto.BlockCipher;
 
-public import dcrypt.crypto.Cipher;
+public import dcrypt.crypto.SymmetricCipher;
 public import dcrypt.crypto.params.SymmetricKey;
 
 /** Interface for a standard block cipher. */
-interface BlockCipher : Cipher {
+abstract class BlockCipher : SymmetricCipher {
     
     /** 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);
 }
--- a/dcrypt/crypto/Cipher.d	Sat Aug 16 22:55:38 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +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.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.)
-     */
-    void init(bool encrypt, CipherParameters params);
-    
-    /** Returns: The name of the algorithm of this cipher. */
-    char[] name();
-    
-    /** Reset cipher to its state immediately subsequent the last init. */
-    void reset();
-}
--- a/dcrypt/crypto/StreamCipher.d	Sat Aug 16 22:55:38 2008 -0400
+++ b/dcrypt/crypto/StreamCipher.d	Mon Aug 18 01:14:37 2008 -0400
@@ -8,12 +8,12 @@
 
 module dcrypt.crypto.StreamCipher;
 
-public import dcrypt.crypto.Cipher;
+public import dcrypt.crypto.SymmetricCipher;
 public import dcrypt.crypto.params.CipherParameters;
 public import dcrypt.crypto.params.SymmetricKey;
 
 /** Interface for a standard stream cipher. */
-class StreamCipher : Cipher {   
+abstract class StreamCipher : SymmetricCipher {   
     /**
      * Process one byte of input.
      *
@@ -22,22 +22,5 @@
      *
      * 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);
+    ubyte returnByte(ubyte input);
 }
--- a/dcrypt/crypto/SymmetricCipher.d	Sat Aug 16 22:55:38 2008 -0400
+++ b/dcrypt/crypto/SymmetricCipher.d	Mon Aug 18 01:14:37 2008 -0400
@@ -8,25 +8,41 @@
 
 module dcrypt.crypto.SymmetricCipher;
 
-import dcrypt.crypto.Cipher;
-import dcrypt.crypto.params.CipherParameters;
+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;
 
-/** Unified cipher class for high-level API. */
-interface SymmetricCipher : Cipher {
-    /** 
-     * Pass bytes through the cipher object.
+/** Base symmetric cipher class */
+abstract class SymmetricCipher {
+    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.)
+     */
+    void init(bool encrypt, CipherParameters params);
+    
+    /**
+     * Process a block of plaintext data from the input array
+     * and return the encrypted data.
+     *
+     * 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.
+     * Returns: The encrypted data.
      */
-    uint update(void[] input_, uint inOff, uint len, void[] output_, uint outOff);
+    ubyte[] process(void[] input_);
     
-    /** Finalize and output the rest of the buffer. */
-    uint finish(void[] output_, uint outOff);
+    /** Returns: The name of the algorithm of this cipher. */
+    char[] name();
+    
+    /** Reset cipher to its state immediately subsequent the last init. */
+    void reset();
 }
--- a/dcrypt/crypto/ciphers/AES.d	Sat Aug 16 22:55:38 2008 -0400
+++ b/dcrypt/crypto/ciphers/AES.d	Mon Aug 18 01:14:37 2008 -0400
@@ -800,32 +800,29 @@
                        RS[cast(ubyte) t0];
     }
     
-    uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
+    ubyte[] process(void[] input_) {
         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)
+        if (input.length < blockSize)
             throw new ShortBufferError(name()~": Input buffer too short");
-            
-        if ((outOff + BLOCK_SIZE) > output.length)
-            throw new ShortBufferError(name()~": Output buffer too short");
         
-        s0 = w[0] ^ Util.ubytesToUintBig(input, inOff);
-        s1 = w[1] ^ Util.ubytesToUintBig(input, inOff+4);
-        s2 = w[2] ^ Util.ubytesToUintBig(input, inOff+8);
-        s3 = w[3] ^ Util.ubytesToUintBig(input, inOff+12);
+        s0 = w[0] ^ Util.ubytesToUintBig(input, 0);
+        s1 = w[1] ^ Util.ubytesToUintBig(input, 4);
+        s2 = w[2] ^ Util.ubytesToUintBig(input, 8);
+        s3 = w[3] ^ Util.ubytesToUintBig(input, 12);
              
         if (encrypt) encryptBlock(); else decryptBlock();
 
-        Util.uintToUbytesBig(s0, output, outOff);
-        Util.uintToUbytesBig(s1, output, outOff+4);
-        Util.uintToUbytesBig(s2, output, outOff+8);
-        Util.uintToUbytesBig(s3, output, outOff+12);
+        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 BLOCK_SIZE;
+        return output;
     }
     
     void reset() {}
@@ -908,14 +905,14 @@
                 
                 // Encryption
                 t.init(true, key);
-                t.processBlock(Util.hexToUbytes(test_plaintexts[i]), 0, buffer, 0);
+                buffer = t.process(Util.hexToUbytes(test_plaintexts[i]));
                 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);
+                buffer = t.process(Util.hexToUbytes(test_ciphertexts[i]));
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_plaintexts[i],
                         t.name~": ("~result~") != ("~test_plaintexts[i]~")");
--- a/dcrypt/crypto/ciphers/Blowfish.d	Sat Aug 16 22:55:38 2008 -0400
+++ b/dcrypt/crypto/ciphers/Blowfish.d	Mon Aug 18 01:14:37 2008 -0400
@@ -255,21 +255,17 @@
                 + S3[cast(ubyte)x]);
     }
     
-    uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
+    ubyte[] process(void[] input_) {
         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)
+        if (input.length < blockSize)
             throw new ShortBufferError(name()~": Input buffer too short");
-            
-        if ((outOff + BLOCK_SIZE) > output.length)
-            throw new ShortBufferError(name()~": Output buffer too short");
         
-        uint xl = Util.ubytesToUintBig(input, inOff),
-             xr = Util.ubytesToUintBig(input, inOff+4),
+        uint xl = Util.ubytesToUintBig(input, 0),
+             xr = Util.ubytesToUintBig(input, 4),
              i = 0;
         
         xl ^= P[i++];
@@ -279,10 +275,11 @@
         }
         xr ^= P[i];
   
-        Util.uintToUbytesBig(xr, output, outOff);
-        Util.uintToUbytesBig(xl, output, outOff+4);
+        ubyte[] output = new ubyte[blockSize];
+        Util.uintToUbytesBig(xr, output, 0);
+        Util.uintToUbytesBig(xl, output, 4);
         
-        return BLOCK_SIZE;
+        return output;
     }
     
     void reset() {
@@ -304,31 +301,31 @@
         
         ubyte[] t = new ubyte[BLOCK_SIZE]; // Initialized to 0's
         for (int i = 0; i < PBOX_SIZE;) {
-            processBlock(t, 0, t, 0);
+            t = process(t);
             P[i++] = Util.ubytesToUintBig(t, 0);
             P[i++] = Util.ubytesToUintBig(t, 4);
         }
         
         for (int i = 0; i < SBOX_SIZE;) {
-            processBlock(t, 0, t, 0);
+            t = process(t);
             S0[i++] = Util.ubytesToUintBig(t, 0);
             S0[i++] = Util.ubytesToUintBig(t, 4);
         }
         
         for (int i = 0; i < SBOX_SIZE;) {
-            processBlock(t, 0, t, 0);
+            t = process(t);
             S1[i++] = Util.ubytesToUintBig(t, 0);
             S1[i++] = Util.ubytesToUintBig(t, 4);
         }
         
         for (int i = 0; i < SBOX_SIZE;) {
-            processBlock(t, 0, t, 0);
+            t = process(t);
             S2[i++] = Util.ubytesToUintBig(t, 0);
             S2[i++] = Util.ubytesToUintBig(t, 4);
         }
         
         for (int i = 0; i < SBOX_SIZE;) {
-            processBlock(t, 0, t, 0);
+            t = process(t);
             S3[i++] = Util.ubytesToUintBig(t, 0);
             S3[i++] = Util.ubytesToUintBig(t, 4);
         }
@@ -378,14 +375,14 @@
                 
                 // Encryption
                 t.init(true, key);
-                t.processBlock(Util.hexToUbytes(test_plaintexts[i]), 0, buffer, 0);
+                buffer = t.process(Util.hexToUbytes(test_plaintexts[i]));
                 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);
+                buffer = t.process(Util.hexToUbytes(test_ciphertexts[i]));
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_plaintexts[i],
                         t.name~": ("~result~") != ("~test_plaintexts[i]~")");
--- a/dcrypt/crypto/ciphers/RC4.d	Sat Aug 16 22:55:38 2008 -0400
+++ b/dcrypt/crypto/ciphers/RC4.d	Mon Aug 18 01:14:37 2008 -0400
@@ -49,26 +49,21 @@
         return (input^state[cast(ubyte)(state[x]+state[y])]);
     }
     
-    void processBytes(void[] input_, uint inOff, uint len, void[] output_, uint outOff) {
+    ubyte[] process(void[] input_) {
         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");
+        ubyte[] input = cast(ubyte[]) input_,
+                output = new ubyte[input.length];
             
-        if ((outOff + len) > output.length)
-            throw new ShortBufferError(name()~": Output buffer too short");
-            
-        for (int i = 0; i < len; i++) {
+        for (int i = 0; i < input.length; 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])];
+            output[i] = input[i] ^ state[cast(ubyte)(state[x]+state[y])];
         }
+        return output;
     }
     
     void reset() { 
@@ -187,7 +182,7 @@
                 r.init(true, new SymmetricKey(Util.hexToUbytes(test_key)));
                 
                 // Encryption
-                r.processBytes(Util.hexToUbytes(test_plaintexts[i]), 0, buffer.length, buffer, 0);
+                buffer = r.process(Util.hexToUbytes(test_plaintexts[i]));
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_ciphertexts[i],
                         r.name~": ("~result~") != ("~test_ciphertexts[i]~")");
@@ -195,7 +190,7 @@
                 r.reset();
                 
                 // Decryption
-                r.processBytes(Util.hexToUbytes(test_ciphertexts[i]), 0, buffer.length, buffer, 0);
+                buffer = r.process(Util.hexToUbytes(test_ciphertexts[i]));
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_plaintexts[i],
                         r.name~": ("~result~") != ("~test_plaintexts[i]~")");
--- a/dcrypt/crypto/ciphers/RC6.d	Sat Aug 16 22:55:38 2008 -0400
+++ b/dcrypt/crypto/ciphers/RC6.d	Mon Aug 18 01:14:37 2008 -0400
@@ -65,23 +65,19 @@
         initialized = true;
     }
     
-    uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
+    ubyte[] process(void[] input_) {
         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)
+        if (input.length > blockSize)
             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),
+        uint A = Util.ubytesToUintLittle(input, 0),
+             B = Util.ubytesToUintLittle(input, 4),
+             C = Util.ubytesToUintLittle(input, 8),
+             D = Util.ubytesToUintLittle(input, 12),
              t,
              u;
              
@@ -119,12 +115,13 @@
             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);
+        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 BLOCK_SIZE;
+        return output;
     }
     
     void reset() {
@@ -195,14 +192,14 @@
                 
                 // Encryption
                 t.init(true, key);
-                t.processBlock(Util.hexToUbytes(test_plaintexts[i]), 0, buffer, 0);
+                buffer = t.process(Util.hexToUbytes(test_plaintexts[i]));
                 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);
+                buffer = t.process(Util.hexToUbytes(test_ciphertexts[i]));
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_plaintexts[i],
                         t.name~": ("~result~") != ("~test_plaintexts[i]~")");
--- a/dcrypt/crypto/ciphers/TEA.d	Sat Aug 16 22:55:38 2008 -0400
+++ b/dcrypt/crypto/ciphers/TEA.d	Mon Aug 18 01:14:37 2008 -0400
@@ -54,21 +54,17 @@
         initialized = true;
     }
     
-    uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
+    ubyte[] process(void[] input_) {
         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)
+        if (input.length < blockSize)
             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);
+        uint v0 = Util.ubytesToUintBig(input, 0),
+             v1 = Util.ubytesToUintBig(input, 4);
         
         sum = encrypt ? 0 : DECRYPT_SUM;
         for (int i = 0; i < ROUNDS; i++) {
@@ -83,10 +79,11 @@
             }
         }
         
-        Util.uintToUbytesBig(v0, output, outOff);
-        Util.uintToUbytesBig(v1, output, outOff+4);
+        ubyte[] output = new ubyte[blockSize];
+        Util.uintToUbytesBig(v0, output, 0);
+        Util.uintToUbytesBig(v1, output, 4);
         
-        return BLOCK_SIZE;
+        return output;
     }
     
     /** Some TEA test vectors. */
@@ -121,14 +118,14 @@
             
             // Encryption
             t.init(true, key);
-            t.processBlock(Util.hexToUbytes(test_plaintexts[i]), 0, buffer, 0);
+            buffer = t.process(Util.hexToUbytes(test_plaintexts[i]));
             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);
+            buffer = t.process(Util.hexToUbytes(test_ciphertexts[i]));
             result = Util.ubytesToHex(buffer);
             assert(result == test_plaintexts[i],
                     t.name~": ("~result~") != ("~test_plaintexts[i]~")");
--- a/dcrypt/crypto/ciphers/XTEA.d	Sat Aug 16 22:55:38 2008 -0400
+++ b/dcrypt/crypto/ciphers/XTEA.d	Mon Aug 18 01:14:37 2008 -0400
@@ -64,21 +64,17 @@
         initialized = true;
     }
     
-    uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
+    ubyte[] process(void[] input_) {
         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)
+        if (input.length < blockSize)
             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);
+        uint v0 = Util.ubytesToUintBig(input, 0),
+             v1 = Util.ubytesToUintBig(input, 4);
              
         if (encrypt) {
             for (int i = 0; i < ROUNDS; i++) {
@@ -92,10 +88,11 @@
             }
         }
         
-        Util.uintToUbytesBig(v0, output, outOff);
-        Util.uintToUbytesBig(v1, output, outOff+4);
+        ubyte[] output = new ubyte[blockSize];
+        Util.uintToUbytesBig(v0, output, 0);
+        Util.uintToUbytesBig(v1, output, 4);
         
-        return BLOCK_SIZE;
+        return output;
     }
     
     /** Some XTEA test vectors. */
@@ -148,14 +145,14 @@
                 
                 // Encryption
                 t.init(true, key);
-                t.processBlock(Util.hexToUbytes(test_plaintexts[i]), 0, buffer, 0);
+                buffer = t.process(Util.hexToUbytes(test_plaintexts[i]));
                 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);
+                buffer = t.process(Util.hexToUbytes(test_ciphertexts[i]));
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_plaintexts[i],
                         t.name~": ("~result~") != ("~test_plaintexts[i]~")");
--- a/dcrypt/crypto/modes/CBC.d	Sat Aug 16 22:55:38 2008 -0400
+++ b/dcrypt/crypto/modes/CBC.d	Mon Aug 18 01:14:37 2008 -0400
@@ -67,48 +67,43 @@
         initialized = true;
     }
     
-    uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
+    ubyte[] process(void[] input_) {
         if (!initialized)
             throw new NotInitializedError(
                     name()~": Block mode not initialized");
             
-        ubyte[] input = cast(ubyte[]) input_,
-                output = cast(ubyte[]) output_;
+        ubyte[] input = cast(ubyte[]) input_;
         
-        if ((inOff + blockSize) > input.length)
+        if (input.length < blockSize)
             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++];
+                previousCiphertext[i] ^= input[i];
                 
             // E_k(P_i XOR C_i-1)
-            m_cipher.processBlock(previousCiphertext, 0, cbcOutput, 0);
+            cbcOutput[] = m_cipher.process(previousCiphertext);
             
             // Store C_i for next block
-            previousCiphertext[] = cbcOutput;            
+            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)];
+            ubyte[] t = input[0..blockSize];
 
             // D_k(C_i)
-            m_cipher.processBlock(t, 0, cbcOutput, 0);
+            cbcOutput[] = m_cipher.process(t);
             
             // P_i = D_k(C_i) XOR C_i-1
             for (int i = 0; i < blockSize; i++)
-                output[outOff++] = (cbcOutput[i] ^ previousCiphertext[i]);
+                cbcOutput[i] ^= previousCiphertext[i];
              
             // Store C_i for next block
             previousCiphertext[] = t;
        }
-        return blockSize;
+        
+        return cbcOutput;
     }
     
     uint blockSize() {
@@ -163,7 +158,7 @@
                 // Encryption
                 c.init(true, params);
                 for (int j = 0; j < 32; j+=x.blockSize)
-                    c.processBlock(Util.hexToUbytes(test_plaintexts[i]), j, buffer, j);
+                    buffer[j..j+x.blockSize] = c.process(Util.hexToUbytes(test_plaintexts[i])[j..j+x.blockSize]);
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_ciphertexts[i],
                         c.name()~": ("~result~") != ("~test_ciphertexts[i]~")");           
@@ -171,10 +166,10 @@
                 // Decryption
                 c.init(false, params);
                 for (int j = 0; j < 32; j+=x.blockSize)
-                    c.processBlock(Util.hexToUbytes(test_ciphertexts[i]), j, buffer, j);
+                    buffer[j..j+x.blockSize] = c.process(Util.hexToUbytes(test_ciphertexts[i])[j..j+x.blockSize]);
                 result = Util.ubytesToHex(buffer);
                 assert(result == test_plaintexts[i],
-                        c.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
+                        c.name()~": ("~result~") != ("~test_plaintexts[i]~")");
             }   
         }
     }
--- a/dcrypt/crypto/modes/CTR.d	Sat Aug 16 22:55:38 2008 -0400
+++ b/dcrypt/crypto/modes/CTR.d	Mon Aug 18 01:14:37 2008 -0400
@@ -62,26 +62,25 @@
         initialized = true;
     }
     
-    uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
+    ubyte[] process(void[] input_) {
         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);
+        counterOutput[] = m_cipher.process(counter);
         
         // XOR output with plaintext to create ciphertext
         for (int i = 0; i < counter.length; i++)
-            output[outOff++] = (counterOutput[i] ^ input[inOff++]);
+            counterOutput[i] ^= input[i];
             
         // Increment the counter
         for (int i = counter.length-1; i >= 0; i--)
             if (++counter[i]) break;
 
-        return counter.length;  
+        return counterOutput;  
     }
     
     uint blockSize() {