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