1 module dcrypt.aead.aead;
2 
3 public import dcrypt.blockcipher.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 
26 						ubyte[] slice = bc.processBytes(cast(const ubyte[]) [0], cast(ubyte[]) [0]);
27 						//ubyte[] mac = bc.finish(block);
28 
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 					}));
33 }
34 
35 @safe
36 public interface IAEADEngine
37 {
38 
39 	public {
40 
41 		 /// Initialize the underlying cipher.
42 		 /// Params:
43 		 /// forEncryption = true if we are setting up for encryption, false otherwise.
44 		 /// key	= Secret key.
45 		 /// nonce	= Number used only once.
46 		void start(bool forEncryption, in ubyte[] key, in ubyte[] nonce) nothrow @nogc;
47 
48 		/// Returns: Returns the name of the algorithm.
49 		@property
50 		string name() pure nothrow;
51 		
52 
53 		/// Process additional authenticated data.
54 		void processAADBytes(in ubyte[] aad) nothrow;
55 
56 		/// Encrypt or decrypt a block of bytes.
57 		/// 
58 		/// Params:
59 		/// input	= Input buffer.
60 		/// output	= Output buffer.
61 		/// 
62 		/// Returns: A slice pointing to the output data.
63 		ubyte[] processBytes(in ubyte[] input, ubyte[] output) nothrow;
64 
65 		///	Close the AEAD cipher by producing the remaining output and a authentication tag.
66 		/// 
67 		/// Params:
68 		/// macBuf	= Buffer for the MAC tag.
69 		/// output	= Buffer for remaining output data.
70 		/// 
71 		/// Note: In decryption mode this does not verify the integrity of the data. Verification has to be done by the programmer!
72 		///
73 		size_t finish(ubyte[] macBuf, ubyte[] output);
74 
75 		/// Returns: Return the size of the output buffer required for a processBytes an input of len bytes.
76 		size_t getUpdateOutputSize(size_t len) nothrow const;
77 	
78 		/// Returns: Return the size of the output buffer required for a processBytes plus a finish with an input of len bytes.
79 		size_t getOutputSize(size_t len) nothrow const;
80 
81 	}
82 }
83 
84 // TODO AEAD cipher wrapper
85 /// Wrapper class for AEAD ciphers
86 @safe
87 public class AEADCipherWrapper(T) if(isAEADCipher!T): IAEADEngine
88 {
89 
90 	private T cipher;
91 
92 	public {
93 
94 		void start(bool forEncryption, in ubyte[] key, in ubyte[] iv) {
95 			cipher.start(forEncryption, key, iv);
96 		}
97 	
98 		@property
99 		string name() pure nothrow {
100 			return cipher.name;
101 		}
102 
103 		void processAADBytes(in ubyte[] aad) nothrow {
104 			cipher.processAADBytes(aad);
105 		}
106 		
107 	
108 		ubyte[] processBytes(in ubyte[] input, ubyte[] output) nothrow {
109 			return cipher.processBytes(input, output);
110 		}
111 
112 		size_t finish(ubyte[] macBuf, ubyte[] output){
113 			return cipher.finish(macBuf, output);
114 		}
115 
116 		size_t getUpdateOutputSize(size_t len) nothrow const {
117 			return cipher.getUpdateOutputSize(len);
118 		}
119 
120 		size_t getOutputSize(size_t len) nothrow const {
121 			return cipher.getOutputSize(len);
122 		}
123 	}
124 }
125 
126 
127 
128 version(unittest) {
129 	
130 	// unittest helper functions
131 
132 	
133 	/// Runs decryption and encryption using AEADCipher cipher with given keys, plaintexts, and ciphertexts.
134 	///
135 	/// Params:
136 	/// hexKeys =	the keys encoded in hex
137 	/// hexIVs =	hex encoded nonces
138 	/// hexPlaintexts =	the plaintexts encoded in hex
139 	/// hexAAD = 	additional authenticated data
140 	/// hexCiphertexts =	the corresponding ciphertexts in hex
141 	/// macSize = MAC sizes in bits
142 	///
143 	/// Throws:
144 	/// AssertionError	if encryption or decryption failed
145 	@safe
146 	public void AEADCipherTest(
147 		IAEADEngine cipher, 
148 		in string[] keys, 
149 		in string[] ivs,
150 		in string[] plaintexts,
151 		in string[] aads, 
152 		in string[] ciphertexts,
153 		in uint[]	macSize
154 		) {
155 		
156 		import dcrypt.aead.aead;
157 		import std.format: format;
158 
159 		alias const (ubyte)[] octets;
160 		
161 		foreach (uint i, string test_key; keys)
162 		{
163 			octets plain = cast(octets) plaintexts[i];
164 			octets aad = cast(octets) aads[i];
165 			octets ciphertext = cast(octets) ciphertexts[i];
166 			
167 			ubyte[] output = new ubyte[plain.length];
168 						
169 			// set to encryption mode
170 			cipher.start(true, cast(octets) test_key, cast(octets) ivs[i]);
171 
172 			output.length = cipher.getOutputSize(plain.length);
173 
174 			immutable size_t taglen = macSize[i]/8;
175 			octets expectedMac = ciphertext[$-taglen..$];
176 			ciphertext = ciphertext[0..$-taglen];
177 
178 //			assert(cipher.getUpdateOutputSize(plain.length) == plain.length);
179 			assert(output.length >= cipher.getUpdateOutputSize(plain.length));
180 
181 
182 			assert(output.length >= cipher.getUpdateOutputSize(plain.length));
183 
184 			// test encryption
185 			cipher.processAADBytes(aad);
186 			ubyte[] out_slice = cipher.processBytes(plain, output);
187 
188 			ubyte[16] mac;
189 			size_t len = out_slice.length+cipher.finish(mac, output[out_slice.length..$]);
190 
191 			assert(output == ciphertext,
192 				format("%s encrypt: %(%.2x%) != %(%.2x%)", cipher.name, output, ciphertexts[i]));
193 				
194 			assert(mac[0..taglen] == expectedMac);
195 			
196 		}
197 	}
198 }