comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:0e08791a1418
1 /**
2 * This file is part of the dcrypt project.
3 *
4 * Copyright: Copyright (C) dcrypt contributors 2008. All rights reserved.
5 * License: MIT
6 * Authors: Thomas Dixon
7 */
8
9 module dcrypt.crypto.modes.CBC;
10
11 import dcrypt.crypto.BlockCipher;
12 import dcrypt.crypto.params.ParametersWithIV;
13
14 version (UnitTest) {
15 import dcrypt.crypto.ciphers.XTEA;
16 import dcrypt.misc.Util;
17 }
18
19 /** This class implements the cipher block chaining (CBC) block mode. */
20 class CBC : BlockCipher {
21 private BlockCipher m_cipher;
22 private ubyte[] iv,
23 previousCiphertext,
24 cbcOutput;
25 private bool encrypt,
26 initialized;
27
28 /**
29 * Params:
30 * cipher = Block cipher to wrap.
31 */
32 this (BlockCipher cipher) {
33 m_cipher = cipher;
34 }
35
36 /** Returns: The underlying cipher we are wrapping. */
37 BlockCipher cipher() {
38 return m_cipher;
39 }
40
41 char[] name() {
42 return m_cipher.name~"/CBC";
43 }
44
45 /**
46 * Throws: dcrypt.crypto.errors.InvalidParameterError if params aren't
47 * an instance of dcrypt.crypto.params.ParametersWithIV.
48 */
49 void init(bool encrypt, CipherParameters params) {
50 ParametersWithIV ivParams = cast(ParametersWithIV)params;
51
52 if (!ivParams)
53 throw new InvalidParameterError(
54 name()~": Block mode requires IV (use ParametersWithIV)");
55 if (ivParams.iv.length != blockSize)
56 throw new InvalidParameterError(
57 name()~": IV must be same length as cipher block size");
58
59 this.encrypt = encrypt;
60 m_cipher.init(encrypt, ivParams.parameters);
61
62 iv = ivParams.iv[0..blockSize];
63 previousCiphertext = new ubyte[blockSize];
64 previousCiphertext[] = iv; // C_0 = IV
65 cbcOutput = new ubyte[blockSize]; // Output buffer for E_k/D_k(...)
66
67 initialized = true;
68 }
69
70 uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
71 if (!initialized)
72 throw new NotInitializedError(
73 name()~": Block mode not initialized");
74
75 ubyte[] input = cast(ubyte[]) input_,
76 output = cast(ubyte[]) output_;
77
78 if ((inOff + blockSize) > input.length)
79 throw new ShortBufferError(name()~": Input buffer too short");
80
81 if ((outOff + blockSize) > output.length)
82 throw new ShortBufferError(name()~": Output buffer too short");
83
84 if (encrypt) {
85 // P_i XOR C_i-1
86 for (int i = 0; i < blockSize; i++)
87 previousCiphertext[i] ^= input[inOff++];
88
89 // E_k(P_i XOR C_i-1)
90 m_cipher.processBlock(previousCiphertext, 0, cbcOutput, 0);
91
92 // Store C_i for next block
93 previousCiphertext[] = cbcOutput;
94
95 // C_i = E_k(P_i XOR C_i-1)
96 output[outOff..(outOff+blockSize)] = cbcOutput;
97 } else {
98 // Temporarily store C_i
99 ubyte[] t = input[inOff..(inOff+blockSize)];
100
101 // D_k(C_i)
102 m_cipher.processBlock(t, 0, cbcOutput, 0);
103
104 // P_i = D_k(C_i) XOR C_i-1
105 for (int i = 0; i < blockSize; i++)
106 output[outOff++] = (cbcOutput[i] ^ previousCiphertext[i]);
107
108 // Store C_i for next block
109 previousCiphertext[] = t;
110 }
111 return blockSize;
112 }
113
114 uint blockSize() {
115 return m_cipher.blockSize;
116 }
117
118 void reset() {
119 previousCiphertext[] = iv;
120 m_cipher.reset();
121 }
122
123 /** Test vectors for CBC mode. Assumes XTEA passes test vectors. */
124 version (UnitTest) {
125 unittest {
126 static const char[][] test_keys = [
127 "00000000000000000000000000000000",
128 "00000000000000000000000000000000",
129 "0123456789abcdef0123456789abcdef"
130 ];
131
132 static const char[][] test_plaintexts = [
133 "00000000000000000000000000000000"~
134 "00000000000000000000000000000000",
135
136 "41414141414141414141414141414141"~
137 "41414141414141414141414141414141",
138
139 "01010101010101010101010101010101"~
140 "01010101010101010101010101010101"
141 ];
142
143 static const char[][] test_ciphertexts = [
144 "dee9d4d8f7131ed9b0e40a036a85d2c4"~
145 "4602d6e67f0c603738197998166ef281",
146
147 "ed23375a821a8c2d0e1f03d719874eaa"~
148 "4b71be74f261e22f4cd2285883a61a23",
149
150 "c09d3c606614d84b8d184fa29c5cb5f6"~
151 "f26fa5a0b6b63ba0f7ebf2f8735f85e3"
152 ];
153
154 XTEA x = new XTEA();
155 CBC c = new CBC(x);
156 ubyte[] iv = new ubyte[x.blockSize], // Initialized to 0
157 buffer = new ubyte[32];
158 char[] result;
159 for (int i = 0; i < test_keys.length; i++) {
160 SymmetricKey key = new SymmetricKey(Util.hexToUbytes(test_keys[i]));
161 ParametersWithIV params = new ParametersWithIV(key, iv);
162
163 // Encryption
164 c.init(true, params);
165 for (int j = 0; j < 32; j+=x.blockSize)
166 c.processBlock(Util.hexToUbytes(test_plaintexts[i]), j, buffer, j);
167 result = Util.ubytesToHex(buffer);
168 assert(result == test_ciphertexts[i],
169 c.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
170
171 // Decryption
172 c.init(false, params);
173 for (int j = 0; j < 32; j+=x.blockSize)
174 c.processBlock(Util.hexToUbytes(test_ciphertexts[i]), j, buffer, j);
175 result = Util.ubytesToHex(buffer);
176 assert(result == test_plaintexts[i],
177 c.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
178 }
179 }
180 }
181 }