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