Mercurial > projects > dcrypt
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 } |