comparison dcrypt/crypto/ManagedBlockCipher.d @ 27:8b5eaf3c2979

Fixed error in hash message padding reported by Glenn Haecker.
author Thomas Dixon <reikon@reikon.us>
date Sat, 09 May 2009 23:29:20 -0400
parents 4589f8c5eb3c
children ad687db713a4
comparison
equal deleted inserted replaced
26:176c933827a8 27:8b5eaf3c2979
15 * Wraps a block cipher, enabling the encryption of a stream. 15 * Wraps a block cipher, enabling the encryption of a stream.
16 * Padding, if specified, is to be applied in the finish() call. 16 * Padding, if specified, is to be applied in the finish() call.
17 * 17 *
18 * Based on PaddedBufferedBlockCipher from BC. 18 * Based on PaddedBufferedBlockCipher from BC.
19 */ 19 */
20 class ManagedBlockCipher : BlockCipher { 20 class ManagedBlockCipher : BlockCipher
21 {
21 BlockCipher cipher; 22 BlockCipher cipher;
22 BlockCipherPadding padding; 23 BlockCipherPadding padding;
23 24
24 protected { 25 protected
26 {
25 ubyte[] buffer; 27 ubyte[] buffer;
26 uint index; 28 uint index;
27 bool encrypt, 29 bool encrypt,
28 streamMode = false; 30 streamMode = false;
29 } 31 }
35 * cipher = Block cipher we're wrapping 37 * cipher = Block cipher we're wrapping
36 * padding = Padding or null if no padding 38 * padding = Padding or null if no padding
37 * 39 *
38 * Returns: A new ManagedBlockCipher 40 * Returns: A new ManagedBlockCipher
39 */ 41 */
40 this(BlockCipher cipher, BlockCipherPadding padding=null) { 42 this(BlockCipher cipher, BlockCipherPadding padding=null)
43 {
41 this.cipher = cipher; 44 this.cipher = cipher;
42 45
43 char[] mode = cipher.name; 46 char[] mode = cipher.name;
44 int i; 47 int i;
45 for (i = 0; i < mode.length; i++) 48 for (i = 0; i < mode.length; i++)
46 if (mode[i] == '/') 49 if (mode[i] == '/')
47 break; 50 break;
48 if (i < mode.length) { 51
52 if (i < mode.length)
53 {
49 mode = mode[i+1..i+4]; 54 mode = mode[i+1..i+4];
50 this.streamMode = (mode == "CTR" || mode == "CFB" || mode == "OFB"); 55 this.streamMode = (mode == "CTR" /*|| mode == "CFB" || mode == "OFB"*/);
51 } 56 }
52 57
53 this.padding = padding; // null signifies no padding is to be applied 58 this.padding = padding; // null signifies no padding is to be applied
54 buffer = new ubyte[blockSize]; 59 buffer = new ubyte[blockSize];
55 } 60 }
56 61
57 void init(bool encrypt, CipherParameters params) { 62 void init(bool encrypt, CipherParameters params)
63 {
58 this.encrypt = encrypt; 64 this.encrypt = encrypt;
59 cipher.init(encrypt, params); 65 cipher.init(encrypt, params);
60 } 66 }
61 67
62 char[] name() { 68 char[] name()
69 {
63 if (padding is null) 70 if (padding is null)
64 return cipher.name; 71 return cipher.name;
72
65 return cipher.name~"/"~padding.name; 73 return cipher.name~"/"~padding.name;
66 } 74 }
67 75
68 uint blockSize() { 76 uint blockSize()
77 {
69 return cipher.blockSize; 78 return cipher.blockSize;
70 } 79 }
71 80
72 /** 81 /**
73 * Update the cipher object with data from input_ and if it fills 82 * Update the cipher object with data from input_ and if it fills
74 * a block, place it in output. 83 * a block, place it in output.
75 * 84 *
76 * Returns: The number of bytes placed in output_. 85 * Returns: The number of bytes placed in output_.
77 */ 86 */
78 uint update(void[] input_, void[] output_) { 87 uint update(void[] input_, void[] output_)
88 {
79 ubyte[] input = cast(ubyte[]) input_, 89 ubyte[] input = cast(ubyte[]) input_,
80 output = cast(ubyte[]) output_; 90 output = cast(ubyte[]) output_;
81 91
82 if (encrypt && input.length > output.length) 92 if (encrypt && input.length > output.length)
83 throw new ShortBufferError("Managed "~name()~": Output buffer too short"); 93 throw new ShortBufferError("Managed "~name()~": Output buffer too short");
84 94
85 uint result = 0, 95 uint result = 0,
86 len = input.length, 96 len = input.length,
87 diff = buffer.length - index, 97 diff = buffer.length - index,
88 i = 0; 98 i = 0;
89 if (len >= diff) { 99 if (len >= diff)
100 {
90 buffer[index..buffer.length] = input[i..diff]; 101 buffer[index..buffer.length] = input[i..diff];
91 result += cipher.update(buffer, output[i..i+blockSize]); 102 result += cipher.update(buffer, output[i..i+blockSize]);
92 index = 0; 103 index = 0;
93 len -= diff; 104 len -= diff;
94 i += blockSize; 105 i += blockSize;
95 106
96 while (len > blockSize) { 107 while (len > blockSize)
108 {
97 result += cipher.update(input[i..i+blockSize], output[i..i+blockSize]); 109 result += cipher.update(input[i..i+blockSize], output[i..i+blockSize]);
98 len -= blockSize; 110 len -= blockSize;
99 i += blockSize; 111 i += blockSize;
100 } 112 }
101 } 113 }
111 * through the cipher (padding it first, if specified) and 123 * through the cipher (padding it first, if specified) and
112 * subsequently placing it in output_. 124 * subsequently placing it in output_.
113 * 125 *
114 * Returns: The number of bytes placed in output_. 126 * Returns: The number of bytes placed in output_.
115 */ 127 */
116 uint finish(void[] output_) { 128 uint finish(void[] output_)
129 {
117 ubyte[] output = cast(ubyte[]) output_; 130 ubyte[] output = cast(ubyte[]) output_;
118 uint result = 0; 131 uint result = 0;
119 if (encrypt) { 132 if (encrypt)
120 if (index == blockSize) { 133 {
134 if (index == blockSize)
135 {
121 if (padding !is null && output.length < (blockSize << 1)) 136 if (padding !is null && output.length < (blockSize << 1))
122 throw new ShortBufferError("Managed "~name()~": Output buffer too short"); 137 throw new ShortBufferError("Managed "~name()~": Output buffer too short");
138
123 result += cipher.update(buffer, output[result..result+blockSize]); 139 result += cipher.update(buffer, output[result..result+blockSize]);
124 index = 0; 140 index = 0;
125 } 141 }
126 142
127 if (padding !is null) { 143 if (padding !is null)
144 {
128 uint diff = buffer.length - index; 145 uint diff = buffer.length - index;
129 buffer[index..buffer.length] = padding.pad(diff); 146 buffer[index..buffer.length] = padding.pad(diff);
130 index += diff; 147 index += diff;
131 } 148 }
132 149
133 if (index) 150 if (index)
134 result += cipher.update(buffer[0..index], output[result..result+index]); 151 result += cipher.update(buffer[0..index], output[result..result+index]);
135 } else { // decrypt 152
136 if (streamMode || index == blockSize) { 153 }
154 else // decrypt
155 {
156 if (streamMode || index == blockSize)
157 {
137 result += cipher.update(buffer[0..index], buffer[0..index]); 158 result += cipher.update(buffer[0..index], buffer[0..index]);
138 index = 0; 159 index = 0;
139 } else { 160 }
161 else
162 {
140 reset(); 163 reset();
141 throw new ShortBufferError( 164 throw new ShortBufferError(
142 "Managed "~name()~": Padded last block not equal to cipher's blocksize"); 165 "Managed "~name()~": Padded last block not equal to cipher's blocksize");
143 } 166 }
144 try { 167
168 try
169 {
145 if (padding !is null) 170 if (padding !is null)
146 result -= padding.unpad(buffer); 171 result -= padding.unpad(buffer);
172
147 output[0..result] = buffer[0..result]; 173 output[0..result] = buffer[0..result];
148 } finally { 174 }
175 finally
176 {
149 reset(); 177 reset();
150 } 178 }
151 } 179 }
180
152 reset(); 181 reset();
182
153 return result; 183 return result;
154 } 184 }
155 185
156 /** 186 /**
157 * Params: 187 * Params:
158 * len = Number of bytes you plan on passing to update() 188 * len = Number of bytes you plan on passing to update()
159 * 189 *
160 * Returns: The number of bytes to be output upon a call to update() 190 * Returns: The number of bytes to be output upon a call to update()
161 * with an input length of len bytes. 191 * with an input length of len bytes.
162 */ 192 */
163 uint updateOutputSize(uint len) { 193 uint updateOutputSize(uint len)
194 {
164 uint result = len + index; 195 uint result = len + index;
165 return result - (result % blockSize); 196 return result - (result % blockSize);
166 } 197 }
167 198
168 /** 199 /**
173 * using an input of len bytes, followed by a call to finish(). 204 * using an input of len bytes, followed by a call to finish().
174 * This method takes into account padding, mode, etc. Will 205 * This method takes into account padding, mode, etc. Will
175 * return 0 if your input is likely to error (i.e. len is 14 206 * return 0 if your input is likely to error (i.e. len is 14
176 * for AES in ECB mode). 207 * for AES in ECB mode).
177 */ 208 */
178 uint finishOutputSize(uint len) { 209 uint finishOutputSize(uint len)
210 {
179 uint result = len + index, 211 uint result = len + index,
180 diff = result % blockSize; 212 diff = result % blockSize;
181 213
182 // Input is a multiple of block size 214 // Input is a multiple of block size
183 if (!diff) 215 if (!diff)
189 221
190 // Padding, return len(input+padding) if encrypting or 0 if not (it'll error) 222 // Padding, return len(input+padding) if encrypting or 0 if not (it'll error)
191 return (encrypt ? result - diff + blockSize : 0); 223 return (encrypt ? result - diff + blockSize : 0);
192 } 224 }
193 225
194 void reset() { 226 void reset()
227 {
195 cipher.reset(); 228 cipher.reset();
196 index = 0; 229 index = 0;
197 buffer[] = 0; 230 buffer[] = 0;
198 } 231 }
199 } 232 }