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