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