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