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 class GMac(T) if(isBlockCipher!T): Mac
16 {
17 
18 	private {
19 		GCM!T gcm;
20 		uint macSizeBits;
21 		bool initialized = false;
22 	}
23 
24 	public {
25 	
26 		void start(in ubyte[] key, in ubyte[] nonce, in uint macSize = 128) {
27 			gcm.start(true, key, nonce, macSize);
28 			macSizeBits = macSize;
29 			initialized = true;
30 		}
31 	}
32 
33 	public override {
34 		
35 		@property
36 		string name() pure nothrow {
37 			static if(is(T == void)) {
38 				return gcm.getUnderlyingCipher.getAlgorithmName()~"-GMAC";
39 			} else {
40 				return T.name~"/GMAC";
41 			}
42 		}
43 		
44 		/**
45 		 * Returns: the size, in bytes, of the MAC.
46 		 */
47 		@property
48 		uint macSize() pure nothrow {
49 			return macSizeBits / 8;
50 		}
51 
52 		/**
53 		 * update the MAC with a block of bytes.
54 		 *
55 		 * Params:
56 		 * input the ubyte slice containing the data.
57 		 */
58 		void put(in ubyte[] input...) nothrow @nogc
59 		in {
60 			assert(initialized, "GMac not initialized. call init() first.");
61 		}
62 		body {
63 			gcm.processAADBytes(input);
64 		}
65 		
66 		/**
67 		 * close the MAC, producing the final MAC value. The doFinal
68 		 * call leaves the MAC reset(). 
69 		 */
70 		size_t doFinal(ubyte[] output) nothrow
71 		in {
72 			assert(initialized, "GMac not initialized. call init() first.");
73 			assert(output.length >= getMacSize(), "output buffer too short for MAC");
74 		}
75 		body {
76 
77 			scope(exit) {
78 				reset();
79 			}
80 
81 			try {
82 				// get the MAC
83 				return gcm.doFinal(output);
84 			} catch(InvalidCipherTextException ex) {
85 				// should not happen in encryption mode
86 				assert(false, "unexpected InvalidCipherTextException");
87 			} catch (Exception e) {
88 				assert(false, "unexpected Exception");
89 			}
90 		}
91 
92 		/**
93 		 * reset the digest back to it's initial state.
94 		 */
95 		void reset() nothrow 
96 		in {
97 			assert(initialized, "GMac not initialized. call init() first.");
98 		}
99 		body {
100 			gcm.reset();
101 		}
102 	}
103 }
104 
105 /// simple usage of GMac
106 /// with test vectors from
107 /// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf, section 2.1.1
108 unittest {
109 
110 	import dcrypt.crypto.engines.aes;
111 	
112 	alias const(ubyte)[] octets;
113 	
114 	octets key = cast(octets)x"AD7A2BD03EAC835A6F620FDCB506B345";
115 	octets iv = cast(octets)x"12153524C0895E81B2C28465"; 
116 
117 	auto gmac = new GMac!AES();
118 	gmac.start(key, iv);
119 	
120 	
121 	octets aad = cast(octets)(
122 		x"D609B1F056637A0D46DF998D88E5222A
123           B2C2846512153524C0895E8108000F10
124           1112131415161718191A1B1C1D1E1F20
125           2122232425262728292A2B2C2D2E2F30
126           313233340001"
127 		);
128 
129 	gmac.put(aad);
130 	
131 	ubyte[] outbuf = new ubyte[gmac.macSize];
132 
133 	gmac.doFinal(outbuf);
134 	
135 	octets expectedMac = cast(octets) (x"F09478A9B09007D06F46E9B6A1DA25DD");
136 
137 	assert(outbuf == expectedMac);
138 }
139