1 module dcrypt.blockcipher.padding.padding;
2 
3 public import dcrypt.exceptions: InvalidCipherTextException;
4 public import dcrypt.blockcipher.blockcipher;
5 
6 ///
7 /// test if T is a block cipher padding
8 ///
9 @safe
10 template isBlockCipherPadding(T)
11 {
12 	enum bool isBlockCipherPadding =
13 		is(T == struct) &&
14 			is(typeof(
15 					{
16 						ubyte[] block;
17 						T padding = void; //Can define
18 						string name = T.name;
19 						padding.addPadding(block, cast(uint) 0);
20 						uint padcount = padding.padCount(block);
21 					}));
22 }
23 
24 // TODO test vectors, doFinal() require minimal output buffer length
25 /// Test PaddedBufferedBlockCipher with AES and PKCS7 padding.
26 @safe
27 unittest {
28 	import dcrypt.blockcipher.padding.pkcs7;
29 	import dcrypt.blockcipher.aes;
30 	
31 	PaddedBufferedBlockCipher!(AES, PKCS7Pad) c;
32 	
33 	immutable ubyte[] key = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
34 	ubyte[] plain = new ubyte[65];
35 	ubyte[] output = new ubyte[96];
36 	
37 	foreach(plainTextLength; [0,1,15,16,17,24,31,32,33,63,64,65]) { // some odd number, smaller than one block, larger than two blocks
38 		
39 		plain.length = plainTextLength;
40 		
41 		foreach(i,ref b; plain) {
42 			b = cast(ubyte)i;
43 		}
44 		
45 		output.length = ((plainTextLength/c.blockSize)+2)*c.blockSize; // next even block size
46 		
47 		c.start(true, key);
48 		
49 		size_t len = c.processBytes(plain, output);
50 		len += c.doFinal(output[len..$]);
51 		output = output[0..len]; // crop slice to its size
52 		
53 		ubyte[] cipher = output.dup;
54 		output[] = 0;
55 		c.start(false, key);
56 		len = c.processBytes(cipher, output);
57 		len += c.doFinal(output[len..$]);
58 		
59 		assert(len == plain.length, "length does not match");
60 		assert(output[0..len] == plain, "decrypted ciphertext is not equal to original plaintext");
61 	}
62 }
63 
64 ///
65 /// PaddedBufferedBlockCipher extends a block cipher or mode (CTR, CBC, ...) by
66 /// the ability to process data that is not a multiple of the block size.
67 /// The last block will be padded according to the chosen padding scheme. If the
68 /// last block is full, then a additional padding block will be appended.
69 /// 
70 @safe
71 public struct PaddedBufferedBlockCipher(C, Padding, bool permitPartialBlock = false)
72 	if(isBlockCipher!C && isBlockCipherPadding!Padding)
73 {
74 
75 	public enum blockSize = C.blockSize;
76 	public enum name = C.name ~ "/" ~ Padding.name;
77 
78 	public {
79 		
80 		void start(bool forEncryption, in ubyte[] key, in ubyte[] iv = null) nothrow @nogc {
81 			this.forEncryption = forEncryption;
82 			cipher.start(forEncryption, key, iv);
83 			reset();
84 		}
85 		
86 		/**
87 		 * takes one byte and stores it in a buffer. If the buffer is already full it gets
88 		 * encrypted and written to output
89 		 * 
90 		 * Params:
91 		 * b	=	the byte to encrypt
92 		 * output	=	the output buffer
93 		 * 
94 		 * Returns: the number of bytes written to output. Will be 0 or BLOCKSIZE of underlying cipher.
95 		 */
96 		@nogc
97 		uint processByte(in ubyte b, ubyte[] output) nothrow
98 		in {
99 			assert(bufOff < blockSize, "bufOff can't be larger than buf.length");
100 			assert(output.length >= blockSize, "output buffer too small");
101 		}
102 		body {
103 			uint outLen = 0;
104 			
105 			if(bufOff == blockSize) {
106 				bufOff = 0;
107 				outLen = cipher.processBlock(buf, output);
108 			}
109 			buf[bufOff] = b;
110 			++bufOff;
111 			
112 			return outLen;
113 		}
114 		
115 		/**
116 		 * input length not limited to multiples of block size.
117 		 * ensure that length of output buffer is sufficiently large (see below).
118 		 * 
119 		 * Params:
120 		 * i	=	the input data
121 		 * output	=	the output buffer. this buffer must be able to hold the
122 		 * same amount of data as given by the input + padding bytes.
123 		 * output.length >= i.length rounded up to the next multiple of block size
124 		 * 
125 		 */
126 		@nogc
127 		uint processBytes(in ubyte[] i, ubyte[] output) nothrow
128 		in {
129 			assert(bufOff < blockSize, "bufOff can't be larger than buf.length");
130 			assert(output.length >= bufOff + i.length, "output buffer too small");
131 		}
132 		body {
133 			uint outLen = 0;
134 			
135 			const(ubyte)[] input = i;
136 			
137 			uint remainingBuf = blockSize-bufOff;
138 			
139 			if(input.length > remainingBuf) {
140 				// fill the buffer and process it if full
141 				
142 				buf[bufOff..bufOff + remainingBuf] = input[0..remainingBuf];
143 				bufOff += remainingBuf;
144 				// drop used input bytes
145 				input = input[remainingBuf..$];
146 				
147 				// process the now full buffer
148 				assert(bufOff == blockSize);
149 				uint len = cipher.processBlock(buf, output); 
150 				output = output[len..$];
151 				outLen += len;
152 				bufOff = 0;
153 				
154 				// got some more complete blocks?
155 				while (input.length > blockSize) {
156 					len = cipher.processBlock(input[0..blockSize], output);
157 					
158 					assert(len == blockSize); // this can be assumed.
159 					
160 					input = input[blockSize..$];
161 					output = output[blockSize..$];
162 					outLen += blockSize;
163 				}
164 			}
165 			assert(input.length <= blockSize-bufOff); // it should not be possible to have now more bytes than the buffer can take
166 			
167 			// still some remaining bytes?
168 			if(input.length > 0){
169 				buf[bufOff..bufOff+input.length] = input[];
170 				bufOff += cast(uint)input.length; // cast is safe, because length has to be smaller than blocksize
171 			}
172 			
173 			return outLen;
174 		}
175 		
176 		/**
177 		 * encrypt the remaining bytes in the buffer, add the padding
178 		 * 
179 		 * Params: output = output buffer. length should be 2*blockSize
180 		 */
181 		uint doFinal(ubyte[] output)
182 		in {
183 			
184 			assert(output.length >= buf.length, "output buffer too small");
185 			
186 			if(forEncryption){
187 				assert(output.length >= 2*blockSize, "output buffer too small. 2*blockSize required, because possibly appending one full padding block");
188 			}else{
189 				assert(bufOff == blockSize, "last block incomplete for decryption");
190 			}
191 		}
192 		body {
193 			scope(success) {reset();}
194 			
195 			static if(!is(Padding == void)) {
196 				return doFinalWithPad(output);
197 			} else {
198 				return doFinalNoPad(output);
199 			}
200 		}
201 		
202 		
203 		private uint doFinalWithPad(ubyte[] output) {
204 			size_t len; /// the number of bytes written to output[]
205 			if(forEncryption) {
206 				
207 				// for padded schemes only
208 				if(bufOff == blockSize) {
209 					// this block is full and therefore can't be padded
210 					// so an aditional (empty) block has to be appended
211 					
212 					assert(output.length >= 2*blockSize, "output buffer too small");
213 					
214 					len = cipher.processBlock(buf, output);
215 					output = output[blockSize..$];
216 					bufOff = 0; // the appended block does not contain data, only padding
217 					buf[] = 0; // clear buffer
218 				}
219 				
220 				// add padding to the last block
221 				padding.addPadding(buf, bufOff);
222 				len += cipher.processBlock(buf, output);
223 			} else{
224 				// remove padding
225 				cipher.processBlock(buf, buf);
226 				
227 				uint padBytes = padding.padCount(buf);
228 				len = buf.length - padBytes;
229 				output[0..len] = buf[0..len];
230 				
231 			}
232 			
233 			return cast(uint)len;
234 		}
235 		
236 		private uint doFinalNoPad(ubyte[] output) nothrow @nogc {
237 			// no padding scheme
238 			uint outLen = 0;
239 			if(bufOff != 0) {
240 				buf[bufOff..$] = 0; // don't encrypt old bytes
241 				cipher.processBlock(buf, buf);
242 				
243 				static if(permitPartialBlock) {
244 					output[0..bufOff] = buf[0..bufOff]; // copy partial block
245 				} else {
246 					output[0..buf.length] = buf[]; // copy full block
247 				}
248 				
249 				outLen = bufOff;
250 			}
251 			return outLen;
252 		}
253 		
254 		public void reset() nothrow @nogc {
255 			cipher.reset();
256 			bufOff = 0;
257 			buf[] = 0;
258 		}
259 	}
260 	
261 	public ref Padding getUnderlyingPadding() nothrow {
262 		return padding;
263 	}
264 	
265 	
266 	private {
267 		C cipher;
268 		uint bufOff = 0;
269 		ubyte[blockSize] buf;
270 		bool forEncryption;
271 		
272 		Padding padding;
273 	}
274 }