1 module dcrypt.aead.gcm.ghash;
2 
3 import dcrypt.aead.gcm.galoisfield;
4 import dcrypt.aead.gcm.multiplier;
5 
6 
7 // BUG: llvm crashes when using GCMMultiplier64kTable
8 alias GHashGeneral!GCMMultiplier8kTable GHash;
9 
10 /// Params:
11 /// Multiplier = a GCM Multiplier like GCMMultiplier8kTable
12 @safe
13 public struct GHashGeneral(Multiplier) if(isGCMMultiplier!Multiplier)
14 {
15 	enum BLOCKSIZE = 16;				/// block size in bytes
16 
17 	alias ubyte T;
18 	alias T[BLOCKSIZE/(T.sizeof)] block;
19 
20 	private {
21 
22 		block stateCipher;	/// state for cipher data hashing
23 		block H;
24 		block stateAAD;		/// state for AAD hashing
25 		block stateAADPre; 	/// stateAAD before first cipher byte is processed
26 	
27 		ubyte stateAADOff = 0; 			/// offset in stateAAD buffer
28 		ubyte stateCipherOff = 0; 		/// offset in stateCipher buffer
29 
30 		ulong lenAAD = 0;				/// length of additional authenticated data (AAD) in bits
31 		ulong lenAADPre = 0;			/// length of AAD before first cipher byte is processed
32 		ulong lenCipher = 0;			/// length of authenticated cipher data in bits
33 
34 		bool aadInput = true; 			/// AAD or cipher input
35 
36 		Multiplier gcmMult;
37 	}
38 
39 	invariant {
40 		// offsets should never exceed their boundaries
41 		assert(stateAADOff <= BLOCKSIZE);
42 		assert(stateCipherOff <= BLOCKSIZE);
43 	}
44 
45 	/// Params:
46 	/// H = element of GF(2^128)
47 	this(in ubyte[] H) nothrow @nogc
48 	in {
49 		assert(H.length == BLOCKSIZE, "H must be 16 bytes");
50 	}
51 	body {
52 		init(H);
53 	}
54 
55 	/// initialize the hash
56 	/// Params:
57 	/// H = the factor used for multiplication.
58 	public void init(in ubyte[] H) nothrow @nogc
59 	in {
60 		assert(H.length == BLOCKSIZE, "H must be 16 bytes");
61 	}
62 	body {
63 		this.H[] = H[];
64 		
65 		// init the multiplier
66 		gcmMult.init(H);
67 	}
68 
69 	
70 	/// add data to the AAD stream
71 	/// Params:
72 	/// aad = data to be authenticated only
73 	public void updateAAD(in ubyte[] aad...) nothrow @nogc
74 	{
75 		update(stateAAD, stateAADOff, aad);
76 		lenAAD += aad.length*8;
77 	}
78 
79 	/// Call this before processing any cipher data for better performance.
80 	private void finalizeAAD() nothrow @nogc {
81 
82 		stateCipher[] = stateAAD[];
83 
84 		if(lenAAD > 0) {
85 			stateAADPre[] = stateAAD[];
86 			lenAADPre = lenAAD;
87 		}
88 
89 		if(stateAADOff > 0) {
90 			// process partial block
91 			multiplyH(stateCipher);
92 			stateAADOff = 0;
93 		}
94 		aadInput = false;
95 	}
96 
97 	/**
98 	 * Params:
99 	 * input = encrypted data
100 	 */
101 	public void updateCipherData(in ubyte[] input...) nothrow @nogc 
102 	{
103 		if(aadInput) {
104 			finalizeAAD(); // sets aadInput = false
105 		}
106 		update(stateCipher, stateCipherOff, input);
107 		lenCipher += input.length*8;
108 	}
109 
110 	/// do final hash round and copy hash to buf
111 	/// resets GHASH
112 	/// Params: buf = output buffer for hash value
113 	public void doFinal(ubyte[] buf) nothrow @nogc
114 	in {
115 		assert(buf.length >= BLOCKSIZE, "output buffer too short");	
116 	}
117 	body {
118 
119 		if(stateAADOff > 0) {
120 			// process last partial AAD block
121 			multiplyH(stateAAD);
122 			stateAADOff = 0;
123 		}
124 
125 		// process last incomplete block
126 		if(stateCipherOff > 0) {
127 			multiplyH(stateCipher);
128 			stateCipherOff = 0;
129 		}
130 
131 		if(lenAAD > lenAADPre) {
132 			// some AAD has been processed after first cipher bytes arrived
133 			// need to adjust the MAC state
134 
135 			// caluculate the difference
136 			stateAADPre[] ^= stateAAD[];
137 
138 			ulong blockDiff = (lenCipher + 127) / 128;	// number of cipher data blocks. 
139 			// + 127 added for rounding up.
140 
141 			// calculate H^blockDiff
142 			ubyte[BLOCKSIZE] expH;
143 			expH[] = H[];
144 			GF128.power(expH, blockDiff);
145 
146 			// propagate the difference to the current block
147 			GF128.multiply(stateAADPre, expH);
148 
149 			// add the difference to the current block
150 			stateCipher[] ^= stateAADPre[];
151 		}
152 
153 		// Add a block containing the length of both streams:	X ^ (len(A)||len(C)) * H
154 		foreach(i;0..8) {
155 			stateCipher[i] ^= lenAAD >> (56-8*i);
156 			stateCipher[i+8] ^= lenCipher >> (56-8*i);
157 		}
158 
159 		multiplyH(stateCipher);
160 
161 		buf[0..BLOCKSIZE] = stateCipher[];
162 
163 		reset();
164 	}
165 
166 	/// Reset the internal state.
167 	public void reset() nothrow @nogc {
168 		stateAAD[] = 0;
169 		stateAADOff = 0;	
170 		stateCipher[] = 0;
171 		stateCipherOff = 0;
172 		lenCipher = 0;
173 		lenAAD = 0;
174 
175 		lenAADPre = 0;
176 		stateAADPre[] = 0;
177 
178 		aadInput = true;
179 	}
180 
181 	/// xor X with input bytes and do GF multiplication by H if buffer is full
182 	/// Params:
183 	/// input = incoming data
184 	/// state = update this state
185 	/// statePos = pointer to the location where the next byte gets written
186 	private void update(ubyte[] state, ref ubyte statePos, in ubyte[] input...) nothrow @nogc
187 	in {
188 		assert(state.length == 16);
189 	}
190 	body {
191 		import std.algorithm: min;
192 		
193 		const(ubyte)[] iBuf = input;
194 
195 		if(statePos == BLOCKSIZE) {
196 			multiplyH(state);
197 			statePos = 0;
198 		}
199 		
200 		while(iBuf.length > 0) {
201 			
202 			size_t procLen = min(iBuf.length, BLOCKSIZE-statePos);
203 
204 			state[statePos..statePos+procLen] ^= iBuf[0..procLen];
205 			
206 			statePos += procLen;
207 			iBuf = iBuf[procLen..$];
208 
209 			if(statePos == BLOCKSIZE) {
210 				multiplyH(state);
211 				statePos = 0;
212 			}
213 		}
214 	}
215 
216 	/// Multiply x by H, store result in x.
217 	private void multiplyH(ubyte[] x) nothrow @nogc {
218 		gcmMult.multiply(x);
219 	}
220 
221 	// unittests
222 
223 	unittest {
224 		GHash gHash = GHash(cast(const(ubyte)[])x"66e94bd4ef8a2c3b884cfa59ca342b2e");
225 		
226 		ubyte[16] token;
227 		gHash.doFinal(token);
228 		
229 		const(ubyte)[] EK0 = cast(const(ubyte)[])x"58e2fccefa7e3061367f1d57a4e7455a";
230 		token[] ^= EK0[];
231 
232 		assert(token == x"58e2fccefa7e3061367f1d57a4e7455a");
233 	}
234 
235 	unittest {
236 
237 		GHash gHash = GHash(cast(const(ubyte)[])x"66e94bd4ef8a2c3b884cfa59ca342b2e");
238 
239 		gHash.updateCipherData(cast(const(ubyte)[])x"0388dace60b6a392f328c2b971b2fe78");
240 
241 		// check X1
242 		assert(gHash.stateCipher == cast(const(ubyte)[])x"5e2ec746917062882c85b0685353deb7");
243 
244 		ubyte[16] hash;
245 		gHash.doFinal(hash);
246 
247 		assert(hash == x"f38cbb1ad69223dcc3457ae5b6b0f885");
248 	}
249 
250 	// test vectors from
251 	// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
252 	// section 2.1.1
253 	unittest {
254 
255 		GHash gHash = GHash(cast(const(ubyte)[])x"73A23D80121DE2D5A850253FCF43120E");
256 
257 		gHash.updateAAD(cast(const(ubyte)[])x"D609B1F056637A0D46DF998D88E5222A");
258 		gHash.updateAAD(cast(const(ubyte)[])x"B2C2846512153524C0895E8108000F10");
259 		gHash.updateAAD(cast(const(ubyte)[])x"1112131415161718191A1B1C1D1E1F20");
260 		gHash.updateAAD(cast(const(ubyte)[])x"2122232425262728292A2B2C2D2E2F30");
261 		gHash.updateAAD(cast(const(ubyte)[])x"313233340001");
262 
263 		ubyte[16] token;
264 		gHash.doFinal(token);
265 
266 		assert(token == x"1BDA7DB505D8A165264986A703A6920D");
267 	}
268 
269 	// test vectors from
270 	// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
271 	// section 2.4.1
272 	unittest {
273 
274 		GHash gHash = GHash(cast(const(ubyte)[])x"E4E01725D724C1215C7309AD34539257");
275 		
276 		gHash.updateAAD(cast(const(ubyte)[])x"E20106D7CD0DF0761E8DCD3D88E54C2A");
277 		gHash.updateAAD(cast(const(ubyte)[])x"76D457ED");
278 
279 		gHash.updateCipherData(cast(const(ubyte)[])x"13B4C72B389DC5018E72A171DD85A5D3");
280 		gHash.updateCipherData(cast(const(ubyte)[])x"752274D3A019FBCAED09A425CD9B2E1C");
281 		gHash.updateCipherData(cast(const(ubyte)[])x"9B72EEE7C9DE7D52B3F3");
282 
283 		ubyte[16] ghash;
284 		gHash.doFinal(ghash);
285 		
286 		assert(ghash == x"2A807BDE4AF8A462D467D2FFA3E1D868");
287 	}
288 
289 	// test vectors from
290 	// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
291 	// section 2.8.1
292 	unittest {
293 		
294 		GHash gHash = GHash(cast(const(ubyte)[])x"AE19118C3B704FCE42AE0D15D2C15C7A");
295 		
296 		gHash.updateAAD(cast(const(ubyte)[])x"68F2E77696CE7AE8E2CA4EC588E54D00");
297 		gHash.updateAAD(cast(const(ubyte)[])x"2E58495C");
298 
299 		gHash.updateCipherData(cast(const(ubyte)[])x"C31F53D99E5687F7365119B832D2AAE7");
300 		gHash.updateCipherData(cast(const(ubyte)[])x"0741D593F1F9E2AB3455779B078EB8FE");
301 		gHash.updateCipherData(cast(const(ubyte)[])x"ACDFEC1F8E3E5277F8180B43361F6512");
302 		gHash.updateCipherData(cast(const(ubyte)[])x"ADB16D2E38548A2C719DBA7228D840");
303 		
304 		ubyte[16] ghash;
305 		gHash.doFinal(ghash);
306 		
307 		assert(ghash == x"5AAA6FD11F06A18BE6E77EF2BC18AF93");
308 	}
309 
310 	/// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
311 	/// test case 16
312 	unittest {
313 
314 		// init gHash with H = acbef...
315 		GHash gHash = GHash(cast(const(ubyte)[])x"acbef20579b4b8ebce889bac8732dad7");
316 
317 		// process AAD
318 		gHash.updateAAD(cast(const(ubyte)[])x"feedfacedeadbeeffeedfacedeadbeef");
319 		gHash.updateAAD(cast(const(ubyte)[])x"abaddad2");
320 
321 		// process cipher data
322 		gHash.updateCipherData(cast(const(ubyte)[])x"522dc1f099567d07f47f37a32a84427d");
323 		gHash.updateCipherData(cast(const(ubyte)[])x"643a8cdcbfe5c0c97598a2bd2555d1aa");
324 		gHash.updateCipherData(cast(const(ubyte)[])x"8cb08e48590dbb3da7b08b1056828838");
325 		gHash.updateCipherData(cast(const(ubyte)[])x"c5f61e6393ba7a0abcc9f662");
326 
327 		// get the final hash value
328 		ubyte[16] ghash;
329 		gHash.doFinal(ghash);
330 		
331 		assert(ghash == x"8bd0c4d8aacd391e67cca447e8c38f65");
332 	}
333 
334 	// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
335 	// test case 16
336 	// AAD and cipher data come out of order
337 	unittest {
338 
339 		GHash gHash = GHash(cast(const(ubyte)[])x"acbef20579b4b8ebce889bac8732dad7");
340 
341 		gHash.updateAAD(cast(const(ubyte)[])x"feedfacedeadbeeffeedfacedeadbeef");
342 
343 		gHash.updateCipherData(cast(const(ubyte)[])x"522dc1f099567d07f47f37a32a84427d");
344 		gHash.updateCipherData(cast(const(ubyte)[])x"643a8cdcbfe5c0c97598a2bd2555d1aa");
345 		gHash.updateCipherData(cast(const(ubyte)[])x"8cb08e48590dbb3da7b08b1056828838");
346 		gHash.updateCipherData(cast(const(ubyte)[])x"c5f61e6393ba7a0abcc9f662");
347 
348 		gHash.updateAAD(cast(const(ubyte)[])x"abaddad2");
349 
350 		
351 		ubyte[16] ghash;
352 		gHash.doFinal(ghash);
353 
354 		assert(ghash == cast(const(ubyte)[])x"8bd0c4d8aacd391e67cca447e8c38f65");
355 
356 		// gHash should now be resetted, so do the same thing again
357 
358 		gHash.updateAAD(cast(const(ubyte)[])x"feedfacedeadbeeffeedfacedeadbeef");
359 		
360 		gHash.updateCipherData(cast(const(ubyte)[])x"522dc1f099567d07f47f37a32a84427d");
361 		gHash.updateCipherData(cast(const(ubyte)[])x"643a8cdcbfe5c0c97598a2bd2555d1aa");
362 		gHash.updateCipherData(cast(const(ubyte)[])x"8cb08e48590dbb3da7b08b1056828838");
363 		
364 		gHash.updateAAD(cast(const(ubyte)[])x"abaddad2");
365 		
366 		gHash.updateCipherData(cast(const(ubyte)[])x"c5f61e6393ba7a0abcc9f662");
367 
368 		gHash.doFinal(ghash);
369 		
370 		assert(ghash == x"8bd0c4d8aacd391e67cca447e8c38f65");
371 	}
372 
373 }
374