comparison dcrypt/crypto/ManagedBlockCipher.d @ 12:8c7f8fecdd75

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