1 module dcrypt.macs.hmac; 2 3 public import dcrypt.macs.mac; 4 import dcrypt.digest; 5 6 import dcrypt.util: wipe; 7 8 // TODO optimize reset() 9 10 static { 11 import dcrypt.digests.sha2: SHA256; 12 13 static assert(isMAC!(HMac!SHA256), "HMac is not a valid MAC"); 14 } 15 16 @safe 17 public struct HMac(D, uint blockSize = D.blockSize) if(isDigest!D) { 18 19 20 public: 21 22 public enum name = "HMAC-"~D.name; 23 public enum macSize = digestLength!D; 24 25 /// Params: keyParam = The HMac key. 26 @safe @nogc 27 void start(in ubyte[] macKey = null) 28 in { 29 if(!initialized) { 30 assert(macKey !is null, "No mac key!"); 31 } 32 } 33 body { 34 if(macKey !is null) { 35 iKey[] = ipadByte; 36 oKey[] = opadByte; 37 // replace key by hash(key) if key length > block length of hash function 38 if(macKey.length > blockSize) { 39 ubyte[blockSize] key; 40 digest.start(); 41 digest.put(macKey); 42 key[0..digestLength!D] = digest.finish(); 43 iKey[] ^= key[]; 44 oKey[] ^= key[]; 45 } else { 46 iKey[0..macKey.length] ^= macKey[]; 47 oKey[0..macKey.length] ^= macKey[]; 48 } 49 } 50 51 if(initialized) { 52 digest.start(); 53 } 54 55 digest.put(iKey); 56 57 initialized = true; 58 } 59 60 61 /// Update the MAC with a block of bytes. 62 /// 63 /// Params: 64 /// input = The ubyte slice containing the data. 65 @safe 66 void put(in ubyte[] input...) nothrow @nogc 67 in { 68 assert(initialized, "HMac not initialized! Call init() first"); 69 } 70 body{ 71 digest.put(input); 72 } 73 74 /// Close the MAC, producing the final MAC value. 75 /// Leaves the MAC reset. 76 /// 77 /// Params: 78 /// output = Output buffer for MAC tag. 79 /// 80 /// Returns: Returns a slice pointing to the MAC tag in the output buffer. 81 @safe 82 ubyte[] finish(ubyte[] output) nothrow @nogc { 83 iHash = digest.finish(); 84 digest.put(oKey); 85 86 digest.put(iHash); 87 88 output[0..macSize] = digest.finish(); 89 90 digest.put(iKey); 91 92 start(); 93 94 return output[0..macSize]; 95 } 96 97 @safe @nogc nothrow 98 ubyte[macSize] finish() { 99 ubyte[macSize] buf; 100 finish(buf); 101 return buf; 102 } 103 104 /// Reset the digest back to it's initial state. 105 @safe 106 public void reset() nothrow @nogc 107 in{ 108 assert(initialized, "HMac not initialized!"); 109 } 110 body { 111 start(); 112 } 113 114 ~this () nothrow { 115 wipe(iKey); 116 wipe(oKey); 117 wipe(iHash); 118 } 119 120 private: 121 D digest; 122 private ubyte[D.digestLength] iHash; 123 // Digest iPaddedDigest, oPaddedDigest; 124 ubyte[blockSize] iKey, oKey; 125 bool initialized = false; 126 127 128 enum ubyte opadByte = 0x5c; 129 enum ubyte ipadByte = 0x36; 130 131 } 132 133 134 /// test vectors from http://tools.ietf.org/html/rfc4231 135 /// 136 /// test case: 1 2 3 4 6 7 (without 5) 137 unittest { 138 import dcrypt.digests.sha2; 139 import dcrypt.digests.sha2; 140 import std.stdio; 141 142 // test vectors from http://tools.ietf.org/html/rfc4231 143 144 // test case: 1 2 3 4 6 7 (without 5) 145 146 string[] keys = [ 147 x"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", 148 x"4a656665", 149 x"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 150 x"0102030405060708090a0b0c0d0e0f10111213141516171819", 151 x"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 152 x"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 153 ]; 154 155 string[] data = [ 156 x"4869205468657265", 157 x"7768617420646f2079612077616e7420666f72206e6f7468696e673f", 158 x"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", 159 x"cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", 160 x"54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a65204b6579202d2048617368204b6579204669727374", 161 x"5468697320697320612074657374207573696e672061206c6172676572207468616e20626c6f636b2d73697a65206b657920616e642061206c6172676572207468616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565647320746f20626520686173686564206265666f7265206265696e6720757365642062792074686520484d414320616c676f726974686d2e", 162 ]; 163 164 string[] macsSHA256 = [ 165 x"b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7", 166 x"5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843", 167 x"773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe", 168 x"82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b", 169 x"60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54", 170 x"9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2" 171 ]; 172 173 string[] macsSHA512 = [ 174 x"87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854", 175 x"164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737", 176 x"fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb", 177 x"b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd", 178 x"80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598", 179 x"e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58" 180 ]; 181 182 183 testHMac!(SHA256)(keys, data, macsSHA256); 184 testHMac!(SHA512)(keys, data, macsSHA512); 185 } 186 187 version(unittest) { 188 189 // unittest helper functions 190 191 import std.conv: text; 192 193 alias const(ubyte)[] octets; 194 195 /// Tests Digest d with given input data and reference hashes. 196 /// 197 /// Params: 198 /// keys = MAC keys. 199 /// datas = Test vector input data. 200 /// hashes = Expected hashes. 201 /// 202 @safe 203 public void testHMac(Digest)(string[] keys, string[] datas, string[] hashes) 204 if(isStdDigest!Digest) { 205 foreach (i; 0 .. datas.length) 206 { 207 HMac!Digest mac; 208 209 octets key = cast(octets) keys[i]; 210 octets data = cast(octets) datas[i]; 211 octets expectedHash = cast(octets) hashes[i]; 212 213 mac.start(key); 214 215 mac.put(data); 216 217 auto hash = mac.finish(); 218 219 assert(hash == expectedHash, text(mac.name, " failed.")); 220 } 221 } 222 }