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