1 module dcrypt.macs.gmac;
2 
3 public import dcrypt.macs.mac;
4 import dcrypt.aead.gcm.gcm;
5 
6 
7 /// Special case of GCMEngine where no data gets encrypted
8 /// but all processed as AAD.
9 /// 
10 /// Standards: NIST Special Publication 800-38D
11 @safe
12 public struct GMac(T) if(isBlockCipher!T)
13 {
14 	enum macSize = 16;
15 
16 	private {
17 		GCM!T gcm;
18 		bool initialized = false;
19 	}
20 
21 	public {
22 
23 		enum name = "GMAC-"~T.name;
24 		
25 		void start(in ubyte[] key, in ubyte[] nonce) {
26 			gcm.start(true, key, nonce);
27 			initialized = true;
28 		}
29 
30 		/// Update the MAC with a block of bytes.
31 		///
32 		/// Params:
33 		/// input = The ubyte slice containing the data.
34 		void put(in ubyte[] input...) nothrow @nogc
35 		in {
36 			assert(initialized, "GMac not initialized. call init() first.");
37 		}
38 		body {
39 			gcm.processAADBytes(input);
40 		}
41 		
42 		/// Close the MAC, producing the final MAC value.
43 		/// Leaves the MAC reset.
44 		/// 
45 		/// Params:
46 		/// output	=	Output buffer for MAC tag.
47 		/// 
48 		/// Returns: Returns a slice pointing to the MAC tag in the output buffer.
49 		void finish(ubyte[] output) nothrow
50 		in {
51 			assert(initialized, "GMac not initialized. call init() first.");
52 			assert(output.length >= gcm.macSize, "output buffer too short for MAC");
53 		}
54 		body {
55 
56 			scope(exit) {
57 				reset();
58 			}
59 
60 			gcm.finish(output[0..macSize], output[0..0]);
61 		}
62 
63 		/// Reset the digest back to it's initial state.
64 		void reset() nothrow 
65 		in {
66 			assert(initialized, "GMac not initialized. call init() first.");
67 		}
68 		body {
69 			gcm.reset();
70 		}
71 	}
72 }
73 
74 /// simple usage of GMac
75 /// with test vectors from
76 /// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf, section 2.1.1
77 unittest {
78 
79 	import dcrypt.blockcipher.aes;
80 	
81 	alias const(ubyte)[] octets;
82 	
83 	octets key = cast(octets)x"AD7A2BD03EAC835A6F620FDCB506B345";
84 	octets iv = cast(octets)x"12153524C0895E81B2C28465"; 
85 
86 	auto gmac = new GMac!AES();
87 	gmac.start(key, iv);
88 	
89 	
90 	octets aad = cast(octets)(
91 		x"D609B1F056637A0D46DF998D88E5222A
92           B2C2846512153524C0895E8108000F10
93           1112131415161718191A1B1C1D1E1F20
94           2122232425262728292A2B2C2D2E2F30
95           313233340001"
96 		);
97 
98 	gmac.put(aad);
99 	
100 	ubyte[] outbuf = new ubyte[gmac.macSize];
101 
102 	gmac.finish(outbuf);
103 	
104 	octets expectedMac = cast(octets) (x"F09478A9B09007D06F46E9B6A1DA25DD");
105 
106 	assert(outbuf == expectedMac);
107 }
108