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 }