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