diff dcrypt/crypto/modes/CBC.d @ 0:0e08791a1418

Initial import.
author Thomas Dixon <reikon@reikon.us>
date Sun, 10 Aug 2008 14:20:17 -0400
parents
children 23c62e28b3a4
line wrap: on
line diff
--- /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]~")");
+            }   
+        }
+    }
+}