Mercurial > projects > dcrypt
view 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 source
/** * 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]~")"); } } } }