]> git.ipfire.org Git - thirdparty/gcc.git/blame - libphobos/src/std/digest/hmac.d
d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)
[thirdparty/gcc.git] / libphobos / src / std / digest / hmac.d
CommitLineData
b4c522fa
IB
1// Written in the D programming language.
2
3/**
4This package implements the hash-based message authentication code (_HMAC)
5algorithm as defined in $(HTTP tools.ietf.org/html/rfc2104, RFC2104). See also
6the corresponding $(HTTP en.wikipedia.org/wiki/Hash-based_message_authentication_code, Wikipedia article).
7
8$(SCRIPT inhibitQuickIndex = 1;)
9
10Macros:
11
12License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
13
5fee5ec3 14Source: $(PHOBOSSRC std/digest/hmac.d)
b4c522fa
IB
15 */
16
17module std.digest.hmac;
18
19import std.digest : isDigest, hasBlockSize, isDigestibleRange, DigestType;
20import std.meta : allSatisfy;
21
22@safe:
23
24/**
25 * Template API HMAC implementation.
26 *
27 * This implements an _HMAC over the digest H. If H doesn't provide
28 * information about the block size, it can be supplied explicitly using
29 * the second overload.
30 *
31 * This type conforms to $(REF isDigest, std,digest).
32 */
33
34/// Compute HMAC over an input string
35@safe unittest
36{
37 import std.ascii : LetterCase;
38 import std.digest : toHexString;
39 import std.digest.sha : SHA1;
40 import std.string : representation;
41
42 auto secret = "secret".representation;
43 assert("The quick brown fox jumps over the lazy dog"
44 .representation
45 .hmac!SHA1(secret)
46 .toHexString!(LetterCase.lower) == "198ea1ea04c435c1246b586a06d5cf11c3ffcda6");
47}
48
49template HMAC(H)
50if (isDigest!H && hasBlockSize!H)
51{
52 alias HMAC = HMAC!(H, H.blockSize);
53}
54
55/**
56 * Overload of HMAC to be used if H doesn't provide information about its
57 * block size.
58 */
59
60struct HMAC(H, size_t hashBlockSize)
61if (hashBlockSize % 8 == 0)
62{
63 enum blockSize = hashBlockSize;
64
65 private H digest;
66 private ubyte[blockSize / 8] key;
67
68 /**
69 * Constructs the HMAC digest using the specified secret.
70 */
71
72 this(scope const(ubyte)[] secret)
73 {
74 // if secret is too long, shorten it by computing its hash
75 typeof(digest.finish()) buffer = void;
5fee5ec3
IB
76 typeof(secret) secretBytes = secret;
77
b4c522fa
IB
78 if (secret.length > blockSize / 8)
79 {
80 digest.start();
81 digest.put(secret);
82 buffer = digest.finish();
5fee5ec3 83 secretBytes = buffer[];
b4c522fa
IB
84 }
85
86 // if secret is too short, it will be padded with zeroes
87 // (the key buffer is already zero-initialized)
88 import std.algorithm.mutation : copy;
5fee5ec3 89 secretBytes.copy(key[]);
b4c522fa
IB
90
91 start();
92 }
93
94 ///
95 @safe pure nothrow @nogc unittest
96 {
5fee5ec3 97 import std.digest.sha : SHA1;
b4c522fa
IB
98 import std.string : representation;
99 auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
100 hmac.put("Hello, world".representation);
101 static immutable expected = [
102 130, 32, 235, 44, 208, 141,
103 150, 232, 211, 214, 162, 195,
104 188, 127, 52, 89, 100, 68, 90, 216];
105 assert(hmac.finish() == expected);
106 }
107
108 /**
109 * Reinitializes the digest, making it ready for reuse.
110 *
111 * Note:
112 * The constructor leaves the digest in an initialized state, so that this
113 * method only needs to be called if an unfinished digest is to be reused.
114 *
115 * Returns:
116 * A reference to the digest for convenient chaining.
117 */
118
119 ref HMAC!(H, blockSize) start() return
120 {
121 ubyte[blockSize / 8] ipad = void;
122 foreach (immutable i; 0 .. blockSize / 8)
123 ipad[i] = key[i] ^ 0x36;
124
125 digest.start();
126 digest.put(ipad[]);
127
128 return this;
129 }
130
131 ///
132 @safe pure nothrow @nogc unittest
133 {
5fee5ec3 134 import std.digest.sha : SHA1;
b4c522fa
IB
135 import std.string : representation;
136 string data1 = "Hello, world", data2 = "Hola mundo";
137 auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
138 hmac.put(data1.representation);
139 hmac.start(); // reset digest
140 hmac.put(data2.representation); // start over
141 static immutable expected = [
142 122, 151, 232, 240, 249, 80,
143 19, 178, 186, 77, 110, 23, 208,
144 52, 11, 88, 34, 151, 192, 255];
145 assert(hmac.finish() == expected);
146 }
147
148 /**
149 * Feeds a piece of data into the hash computation. This method allows the
150 * type to be used as an $(REF OutputRange, std,range).
151 *
152 * Returns:
153 * A reference to the digest for convenient chaining.
154 */
155
156 ref HMAC!(H, blockSize) put(in ubyte[] data...) return
157 {
158 digest.put(data);
159 return this;
160 }
161
162 ///
163 @safe pure nothrow @nogc unittest
164 {
165 import std.digest.hmac, std.digest.sha;
166 import std.string : representation;
167 string data1 = "Hello, world", data2 = "Hola mundo";
168 auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
169 hmac.put(data1.representation)
170 .put(data2.representation);
171 static immutable expected = [
172 197, 57, 52, 3, 13, 194, 13,
173 36, 117, 228, 8, 11, 111, 51,
174 165, 3, 123, 31, 251, 113];
175 assert(hmac.finish() == expected);
176 }
177
178 /**
179 * Resets the digest and returns the finished hash.
180 */
181
182 DigestType!H finish()
183 {
184 ubyte[blockSize / 8] opad = void;
185 foreach (immutable i; 0 .. blockSize / 8)
186 opad[i] = key[i] ^ 0x5c;
187
188 auto tmp = digest.finish();
189
190 digest.start();
191 digest.put(opad[]);
192 digest.put(tmp);
193 auto result = digest.finish();
194 start(); // reset the digest
195 return result;
196 }
197
198 ///
199 @safe pure nothrow @nogc unittest
200 {
5fee5ec3 201 import std.digest.sha : SHA1;
b4c522fa
IB
202 import std.string : representation;
203 string data1 = "Hello, world", data2 = "Hola mundo";
204 auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
205 auto digest = hmac.put(data1.representation)
206 .put(data2.representation)
207 .finish();
208 static immutable expected = [
209 197, 57, 52, 3, 13, 194, 13,
210 36, 117, 228, 8, 11, 111, 51,
211 165, 3, 123, 31, 251, 113];
212 assert(digest == expected);
213 }
214}
215
5fee5ec3 216/// ditto
b4c522fa
IB
217template hmac(H)
218if (isDigest!H && hasBlockSize!H)
219{
220 alias hmac = hmac!(H, H.blockSize);
221}
222
223/// ditto
224template hmac(H, size_t blockSize)
225if (isDigest!H)
226{
227 /**
228 * Constructs an HMAC digest with the specified secret.
229 *
230 * Returns:
231 * An instance of HMAC that can be fed data as desired, and finished
232 * to compute the final hash when done.
233 */
234 auto hmac(scope const(ubyte)[] secret)
235 {
236 return HMAC!(H, blockSize)(secret);
237 }
238
239 ///
240 @safe pure nothrow @nogc unittest
241 {
5fee5ec3 242 import std.digest.sha : SHA1;
b4c522fa
IB
243 import std.string : representation;
244 string data1 = "Hello, world", data2 = "Hola mundo";
245 auto digest = hmac!SHA1("My s3cR3T keY".representation)
246 .put(data1.representation)
247 .put(data2.representation)
248 .finish();
249 static immutable expected = [
250 197, 57, 52, 3, 13, 194, 13, 36,
251 117, 228, 8, 11, 111, 51, 165,
252 3, 123, 31, 251, 113];
253 assert(digest == expected);
254 }
255
256 /**
257 * Computes an _HMAC digest over the given range of data with the
258 * specified secret.
259 *
260 * Returns:
261 * The final _HMAC hash.
262 */
263 DigestType!H hmac(T...)(scope T data, scope const(ubyte)[] secret)
264 if (allSatisfy!(isDigestibleRange, typeof(data)))
265 {
266 import std.range.primitives : put;
267 auto hash = HMAC!(H, blockSize)(secret);
268 foreach (datum; data)
269 put(hash, datum);
270 return hash.finish();
271 }
272
273 ///
274 @safe pure nothrow @nogc unittest
275 {
276 import std.algorithm.iteration : map;
5fee5ec3 277 import std.digest.sha : SHA1;
b4c522fa
IB
278 import std.string : representation;
279 string data = "Hello, world";
280 auto digest = data.representation
281 .map!(a => cast(ubyte)(a+1))
282 .hmac!SHA1("My s3cR3T keY".representation);
283 static assert(is(typeof(digest) == ubyte[20]));
284 static immutable expected = [
285 163, 208, 118, 179, 216, 93,
286 17, 10, 84, 200, 87, 104, 244,
287 111, 136, 214, 167, 210, 58, 10];
288 assert(digest == expected);
289 }
290}
291
5fee5ec3
IB
292///
293@safe pure nothrow @nogc unittest
b4c522fa 294{
5fee5ec3
IB
295 import std.digest.sha : SHA1;
296 import std.string : representation;
297 string data1 = "Hello, world", data2 = "Hola mundo";
298 auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
299 auto digest = hmac.put(data1.representation)
300 .put(data2.representation)
301 .finish();
302 static immutable expected = [
303 197, 57, 52, 3, 13, 194, 13,
304 36, 117, 228, 8, 11, 111, 51,
305 165, 3, 123, 31, 251, 113];
306 assert(digest == expected);
b4c522fa
IB
307}
308
309@safe pure nothrow @nogc
310unittest
311{
312 import std.digest.md : MD5;
313 import std.range : isOutputRange;
314 static assert(isOutputRange!(HMAC!MD5, ubyte));
315 static assert(isDigest!(HMAC!MD5));
316 static assert(hasBlockSize!(HMAC!MD5) && HMAC!MD5.blockSize == MD5.blockSize);
317}
318
319@safe pure nothrow
320unittest
321{
322 import std.digest.md : MD5;
323 import std.digest.sha : SHA1, SHA256;
324
5fee5ec3
IB
325 // Note, can't be UFCS because we don't want to import inside
326 // version (StdUnittest).
327 import std.digest : toHexString, LetterCase;
328 alias hex = toHexString!(LetterCase.lower);
329
b4c522fa 330 ubyte[] nada;
5fee5ec3
IB
331 assert(hex(hmac!MD5 (nada, nada)) == "74e6f7298a9c2d168935f58c001bad88");
332 assert(hex(hmac!SHA1 (nada, nada)) == "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d");
333 assert(hex(hmac!SHA256(nada, nada)) == "b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad");
b4c522fa
IB
334
335 import std.string : representation;
336 auto key = "key".representation,
337 long_key = ("012345678901234567890123456789012345678901"
338 ~"234567890123456789012345678901234567890123456789").representation,
339 data1 = "The quick brown fox ".representation,
340 data2 = "jumps over the lazy dog".representation,
341 data = data1 ~ data2;
342
5fee5ec3
IB
343 assert(hex(data.hmac!MD5 (key)) == "80070713463e7749b90c2dc24911e275");
344 assert(hex(data.hmac!SHA1 (key)) == "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9");
345 assert(hex(data.hmac!SHA256(key)) == "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8");
b4c522fa 346
5fee5ec3
IB
347 assert(hex(data.hmac!MD5 (long_key)) == "e1728d68e05beae186ea768561963778");
348 assert(hex(data.hmac!SHA1 (long_key)) == "560d3cd77316e57ab4bba0c186966200d2b37ba3");
349 assert(hex(data.hmac!SHA256(long_key)) == "a1b0065a5d1edd93152c677e1bc1b1e3bc70d3a76619842e7f733f02b8135c04");
b4c522fa
IB
350
351 assert(hmac!MD5 (key).put(data1).put(data2).finish == data.hmac!MD5 (key));
352 assert(hmac!SHA1 (key).put(data1).put(data2).finish == data.hmac!SHA1 (key));
353 assert(hmac!SHA256(key).put(data1).put(data2).finish == data.hmac!SHA256(key));
354}