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