1 // Written in the D programming language.
2 /**
3 HMAC implemented by D.
4 
5 Author: Kazuki Komatsu
6 Licence: NYSL
7 */
8 module graphite.twitter.hmac;
9 
10 import std.digest.sha;
11 import std.digest.md;
12 
13 
14 /**
15 
16 */
17 struct HMAC(Hash)if(isDigest!Hash)
18 {
19     this(const(ubyte)[] key) pure nothrow @safe
20     {
21         _ipad.length = blockSize;
22         _opad.length = blockSize;
23 
24         if(key.length > blockSize){
25             _hash.start();
26             _hash.put(key);
27             _key = _hash.finish()[];
28         }else
29             _key = key;
30 
31         if(_key.length < blockSize)
32             _key.length = blockSize;
33 
34         foreach(i; 0 .. blockSize){
35             _ipad[i] = _key[i] ^ 0x36;
36             _opad[i] = _key[i] ^ 0x5c;
37         }
38 
39         this.start();
40     }
41 
42 
43     void start() pure nothrow @safe
44     {
45         _hash.start();
46         _hash.put(_ipad);
47     }
48 
49 
50     void put(scope const(ubyte)[] input...) pure nothrow @safe
51     {
52         _hash.put(input);
53     }
54 
55 
56     auto finish() pure nothrow @safe
57     {
58         auto inner = _hash.finish();
59 
60         _hash.put(_opad);
61         _hash.put(inner[]);
62         auto result = _hash.finish();
63         
64         _hash.put(_ipad);   // this.start();
65 
66         return result;
67     }
68 
69 
70   private:
71     Hash _hash;
72     const(ubyte)[] _key;
73     ubyte[] _ipad;
74     ubyte[] _opad;
75 
76     static if(is(Hash == std.digest.sha.SHA1) || is(Hash == std.digest.md.MD5))
77         enum blockSize = 64;
78     else 
79         enum blockSize = Hash.blockSize;
80 }
81 
82 unittest{
83     // HMAC-MD5 test case : http://www.ipa.go.jp/security/rfc/RFC2202JA.html
84     import std.algorithm, std.range, std.array, std.digest.digest;
85 
86     auto hmac_md5 = HMAC!(MD5)(array(take(repeat(cast(ubyte)0x0b), 16)));
87     put(hmac_md5, cast(ubyte[])"Hi There");
88     assert(toHexString(hmac_md5.finish()) == "9294727A3638BB1C13F48EF8158BFC9D");
89 
90     hmac_md5 = HMAC!(MD5)(cast(ubyte[])"Jefe");
91     put(hmac_md5, cast(ubyte[])"what do ya want for nothing?");
92     assert(toHexString(hmac_md5.finish()) == "750C783E6AB0B503EAA86E310A5DB738");
93 
94     hmac_md5 = HMAC!(MD5)(array(take(repeat(cast(ubyte)0xaa), 16)));
95     put(hmac_md5, array(take(repeat(cast(ubyte)0xdd), 50)));
96     assert(toHexString(hmac_md5.finish()) == "56BE34521D144C88DBB8C733F0E8B3F6");
97 
98     hmac_md5 = HMAC!(MD5)(array(map!"cast(ubyte)a"(iota(1, 26))));
99     put(hmac_md5, array(take(repeat(cast(ubyte)0xcd), 50)));
100     assert(toHexString(hmac_md5.finish()) == "697EAF0ACA3A3AEA3A75164746FFAA79");
101 
102     hmac_md5 = HMAC!(MD5)(array(take(repeat(cast(ubyte)0x0c), 16)));
103     put(hmac_md5, cast(ubyte[])"Test With Truncation");
104     assert(toHexString(hmac_md5.finish()) == "56461EF2342EDC00F9BAB995690EFD4C");
105 
106     hmac_md5 = HMAC!(MD5)(array(take(repeat(cast(ubyte)0xaa), 80)));
107     put(hmac_md5, cast(ubyte[])"Test Using Larger Than Block-Size Key - Hash Key First");
108     assert(toHexString(hmac_md5.finish()) == "6B1AB7FE4BD7BF8F0B62E6CE61B9D0CD");
109 
110     hmac_md5 = HMAC!(MD5)(array(take(repeat(cast(ubyte)0xaa), 80)));
111     put(hmac_md5, cast(ubyte[])"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data");
112     assert(toHexString(hmac_md5.finish()) == "6F630FAD67CDA0EE1FB1F562DB3AA53E");
113 }
114 
115 unittest{
116     // HMAC-SHA1 test case : http://www.ipa.go.jp/security/rfc/RFC2202JA.html
117     import std.algorithm, std.range, std.array, std.digest.digest;
118 
119     auto hmac_sha1 = HMAC!(SHA1)(array(take(repeat(cast(ubyte)0x0b), 20)));
120     put(hmac_sha1, cast(ubyte[])"Hi There");
121     assert(toHexString(hmac_sha1.finish()) == "B617318655057264E28BC0B6FB378C8EF146BE00");
122 
123     hmac_sha1 = HMAC!(SHA1)(cast(ubyte[])"Jefe");
124     put(hmac_sha1, cast(ubyte[])"what do ya want for nothing?");
125     assert(toHexString(hmac_sha1.finish()) == "EFFCDF6AE5EB2FA2D27416D5F184DF9C259A7C79");
126 
127     hmac_sha1 = HMAC!(SHA1)(array(take(repeat(cast(ubyte)0xaa), 20)));
128     put(hmac_sha1, array(take(repeat(cast(ubyte)0xdd), 50)));
129     assert(toHexString(hmac_sha1.finish()) == "125D7342B9AC11CD91A39AF48AA17B4F63F175D3");
130 
131     hmac_sha1 = HMAC!(SHA1)(array(map!"cast(ubyte)a"(iota(1, 26))));
132     put(hmac_sha1, array(take(repeat(cast(ubyte)0xcd), 50)));
133     assert(toHexString(hmac_sha1.finish()) == "4C9007F4026250C6BC8414F9BF50C86C2D7235DA");
134 
135     hmac_sha1 = HMAC!(SHA1)(array(take(repeat(cast(ubyte)0x0c), 20)));
136     put(hmac_sha1, cast(ubyte[])"Test With Truncation");
137     assert(toHexString(hmac_sha1.finish()) == "4C1A03424B55E07FE7F27BE1D58BB9324A9A5A04");
138 
139     hmac_sha1 = HMAC!(SHA1)(array(take(repeat(cast(ubyte)0xaa), 80)));
140     put(hmac_sha1, cast(ubyte[])"Test Using Larger Than Block-Size Key - Hash Key First");
141     assert(toHexString(hmac_sha1.finish()) == "AA4AE5E15272D00E95705637CE8A3B55ED402112");
142 
143     hmac_sha1 = HMAC!(SHA1)(array(take(repeat(cast(ubyte)0xaa), 80)));
144     put(hmac_sha1, cast(ubyte[])"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data");
145     assert(toHexString(hmac_sha1.finish()) == "E8E99D0F45237D786D6BBAA7965C7808BBFF1A91");
146 }
147 
148 
149 
150 auto hmacOf(Hash)(in void[] key, in void[] input)
151 {
152     auto hash = HMAC!Hash(cast(const(ubyte)[])key);
153     hash.put(cast(const(ubyte)[])input);
154     return hash.finish;
155 }