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 }