Mercurial > projects > dcrypt
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]~")"); + } + } + } +}