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 }