1 module dcrypt.crypto.modes.aead;
2 
3 public import dcrypt.crypto.blockcipher;
4 import dcrypt.crypto.params.keyparameter;
5 
6 ///
7 /// test if T is a AEAD cipher
8 ///
9 @safe
10 template isAEADCipher(T)
11 {
12 	enum bool isAEADCipher =
13 		is(T == struct) &&
14 			is(typeof(
15 					{
16 						ubyte[0] block;
17 						T bc = void; //Can define
18 
19 						bc.start(true, block, block); //  start with key, iv
20 
21 						string name = T.name;
22 						//BlockCipher c = bc.getUnderlyingCipher();
23 						bc.processAADBytes(cast (const ubyte[])block);
24 						bc.processBytes(cast(const ubyte[]) [0], cast(ubyte[]) [0]);
25 						bc.doFinal(cast(const ubyte[]) [0]);
26 						bc.getMac(cast(const ubyte[]) [0]);
27 						size_t s1 = bc.getUpdateOutputSize(cast(size_t) 0);
28 						size_t s2 = bc.getOutputSize(cast(size_t) 0);
29 						bc.reset();
30 					}));
31 }
32 
33 @safe
34 public interface AEADCipher
35 {
36 
37 	public {
38 		/**
39 		 * initialize the underlying cipher. Parameter can either be an AEADParameters or a ParametersWithIV object.
40 		 * Params:
41 		 * forEncryption = true if we are setting up for encryption, false otherwise.
42 		 * params = the necessary parameters for the underlying cipher to be initialised.
43 		 * macSize = Size of mac tag in bits.
44 		 */
45 		void start(bool forEncryption, in ubyte[] key, in ubyte[] iv, in uint macSize = 0) nothrow @nogc;
46 
47 		/**
48 		 * Return the name of the algorithm.
49 		 * 
50 		 * Returns: the algorithm name.
51 		 */
52 		@property
53 		string name() pure nothrow;
54 		
55 		/**
56 		 * return the cipher this object wraps.
57 		 *
58 		 * Returns: the cipher this object wraps.
59 		 */
60 		BlockCipher getUnderlyingCipher() nothrow;
61 
62 		
63 		/**
64 		 * Add a sequence of bytes to the associated data check.
65 		 * <br>If the implementation supports it, this will be an online operation and will not retain the associated data.
66 		 *
67 		 * Params: in = the input byte array.
68 		 */
69 		void processAADBytes(in ubyte[] aad) nothrow;
70 
71 		/**
72 		 * process a block of bytes from in putting the result into out.
73 		 * Params:
74 		 * in = the input byte array.
75 		 * out = the output buffer the processed bytes go into.
76 		 * Returns: the number of bytes written to out.
77 		 * Throws: Error if the output buffer is too small.
78 		 */
79 		size_t processBytes(in ubyte[] input, ubyte[] output) nothrow;
80 
81 		/**
82 		 * Finish the operation either appending or verifying the MAC at the end of the data.
83 		 *
84 		 * Params: out = space for any resulting output data.
85 		 * Returns: number of bytes written into out.
86 		 * Throws: IllegalStateError = if the cipher is in an inappropriate state.
87 		 * dcrypt.exceptions.InvalidCipherTextException =  if the MAC fails to match.
88 		 */
89 		size_t doFinal(ubyte[] output);
90 
91 		/**
92 		 * Write the MAC of the processed data to buf
93 		 * 
94 		 * Params: buf  = output buffer
95 		 */
96 		void getMac(ubyte[] buf) nothrow;
97 		
98 		/**
99 		 * return the size of the output buffer required for a processBytes
100 		 * an input of len bytes.
101 		 *
102 		 * Params: len = the length of the input.
103 		 * Returns: the space required to accommodate a call to processBytes
104 		 * with len bytes of input.
105 		 */
106 		size_t getUpdateOutputSize(size_t len) nothrow;
107 		
108 		/**
109 		 * return the size of the output buffer required for a processBytes plus a
110 		 * doFinal with an input of len bytes.
111 		 *
112 		 * Params:
113 		 * len = the length of the input.
114 		 * Returns: the space required to accommodate a call to processBytes and doFinal
115 		 * with len bytes of input.
116 		 */
117 		size_t getOutputSize(size_t len) nothrow;
118 
119 		/**
120 		 * Reset the cipher. After resetting the cipher is in the same state
121 		 * as it was after the last init (if there was one).
122 		 */
123 		void reset() nothrow;
124 	}
125 }
126 
127 //// TODO AEAD cipher wrapper
128 ///// Wrapper class for AEAD ciphers
129 //@safe
130 //public class AEADCipherWrapper(T) if(isAEADCipher!T): AEADCipher
131 //{
132 //
133 //	private T cipher = void;
134 //
135 //	public {
136 //
137 //		//		/// Params: c = underlying block cipher
138 //		//		this(BlockCipher c) {
139 //		//			cipher = T(c);
140 //		//		}
141 //
142 //		/**
143 //		 * initialize the underlying cipher. Parameter can either be an AEADParameters or a ParametersWithIV object.
144 //		 * Params:
145 //		 * forEncryption = true if we are setting up for encryption, false otherwise.
146 //		 * params = the necessary parameters for the underlying cipher to be initialised.
147 //		 * Throws: IllegalArgumentException if the params argument is inappropriate.
148 //		 */
149 //		void init(bool forEncryption, ParametersWithIV params) {
150 //			cipher.init(forEncryption, params);
151 //		}
152 //		
153 //		/**
154 //		 * Return the name of the algorithm.
155 //		 * 
156 //		 * Returns: the algorithm name.
157 //		 */
158 //		string getAlgorithmName() pure nothrow {
159 //			return cipher.getAlgorithmName();
160 //		}
161 //
162 //		/**
163 //		 * return the cipher this object wraps.
164 //		 *
165 //		 * Returns: the cipher this object wraps.
166 //		 */
167 //		BlockCipher getUnderlyingCipher() pure nothrow {
168 //			return cipher.getUnderlyingCipher();
169 //		}
170 //
171 //		
172 //		/**
173 //		 * Add a sequence of bytes to the associated data check.
174 //		 * If the implementation supports it, this will be an online operation and will not retain the associated data.
175 //		 *
176 //		 * Params: in = the input byte array.
177 //		 */
178 //		void processAADBytes(in ubyte[] aad) nothrow {
179 //			cipher.processAADBytes(aad);
180 //		}
181 //		
182 //		/**
183 //		 * process a block of bytes from in putting the result into out.
184 //		 * Params:
185 //		 * in = the input byte array.
186 //		 * out = the output buffer the processed bytes go into.
187 //		 * Returns: the number of bytes written to out.
188 //		 * Throws: Error if the output buffer is too small.
189 //		 */
190 //		size_t processBytes(in ubyte[] input, ubyte[] output) nothrow {
191 //			return cipher.processBytes(input, output);
192 //		}
193 //		
194 //		/**
195 //		 * Finish the operation either appending or verifying the MAC at the end of the data.
196 //		 *
197 //		 * Params: out = space for any resulting output data.
198 //		 * Returns: number of bytes written into out.
199 //		 * Throws: IllegalStateError = if the cipher is in an inappropriate state.
200 //		 * dcrypt.exceptions.InvalidCipherTextException =  if the MAC fails to match.
201 //		 */
202 //		size_t doFinal(ubyte[] output){
203 //			return cipher.doFinal(output);
204 //		}
205 //		
206 //		/**
207 //		 * Write the MAC of the processed data to buf
208 //		 * 
209 //		 * Params: buf  = output buffer
210 //		 */
211 //		void getMac(ubyte[] buf) nothrow {
212 //			cipher.getMac(buf);
213 //		}
214 //		
215 //		/**
216 //		 * return the size of the output buffer required for a processBytes
217 //		 * an input of len bytes.
218 //		 *
219 //		 * Params: len = the length of the input.
220 //		 * Returns: the space required to accommodate a call to processBytes
221 //		 * with len bytes of input.
222 //		 */
223 //		size_t getUpdateOutputSize(size_t len) nothrow {
224 //			return cipher.getUpdateOutputSize(len);
225 //		}
226 //		
227 //		/**
228 //		 * return the size of the output buffer required for a processBytes plus a
229 //		 * doFinal with an input of len bytes.
230 //		 *
231 //		 * Params:
232 //		 * len = the length of the input.
233 //		 * Returns: the space required to accommodate a call to processBytes and doFinal
234 //		 * with len bytes of input.
235 //		 */
236 //		size_t getOutputSize(size_t len) nothrow {
237 //			return cipher.getOutputSize(len);
238 //		}
239 //		
240 //		/**
241 //		 * Reset the cipher. After resetting the cipher is in the same state
242 //		 * as it was after the last init (if there was one).
243 //		 */
244 //		void reset() nothrow {
245 //			cipher.reset();
246 //		}
247 //	}
248 //}
249 
250 
251 
252 version(unittest) {
253 	
254 	// unittest helper functions
255 
256 	
257 	/// Runs decryption and encryption using AEADCipher cipher with given keys, plaintexts, and ciphertexts.
258 	///
259 	/// Params:
260 	/// hexKeys =	the keys encoded in hex
261 	/// hexIVs =	hex encoded nonces
262 	/// hexPlaintexts =	the plaintexts encoded in hex
263 	/// hexAAD = 	additional authenticated data
264 	/// hexCiphertexts =	the corresponding ciphertexts in hex
265 	/// macSize = MAC sizes in bits
266 	///
267 	/// Throws:
268 	/// AssertionError	if encryption or decryption failed
269 	@safe
270 	public void AEADCipherTest(
271 		AEADCipher cipher, 
272 		in string[] hexKeys, 
273 		in string[] hexIVs,
274 		in string[] hexPlaintexts,
275 		in string[] hexAAD, 
276 		in string[] hexCipherTexts,
277 		in uint[]	macSize
278 		) {
279 		
280 		import dcrypt.crypto.modes.aead;
281 		import dcrypt.util.encoders.hex;
282 		import std.conv: text;
283 		
284 		foreach (uint i, string test_key; hexKeys)
285 		{
286 			ubyte[] plain = hexDecode(hexPlaintexts[i]);
287 			ubyte[] aad = hexDecode(hexAAD[i]);
288 			ubyte[] ciphertext = hexDecode(hexCipherTexts[i]);
289 			
290 			ubyte[] output = new ubyte[0];
291 						
292 			// set to encryption mode
293 			cipher.start(true, hexDecode(test_key), hexDecode(hexIVs[i]), macSize[i]);
294 			
295 			// test reset()
296 			cipher.processAADBytes([0,1,2,3]);
297 			output.length = cipher.getOutputSize(plain.length);
298 			cipher.processBytes(plain, output);
299 			cipher.reset();
300 			
301 			output.length = cipher.getOutputSize(plain.length);
302 			
303 			// test encryption
304 			cipher.processAADBytes(aad);
305 			size_t offset = cipher.processBytes(plain, output);
306 			
307 			size_t len = offset+cipher.doFinal(output[offset..$]);
308 			//output = output[0..len];
309 			
310 			assert(output == ciphertext,
311 				text(cipher.name~" encrypt: (",hexEncode(output),") != ("~hexCipherTexts[i]~")"));
312 			
313 		}
314 	}
315 }