0
|
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 }
|