1 module secured.mac; 2 3 import std.stdio; 4 import std.format; 5 6 import secured.openssl; 7 import deimos.openssl.evp; 8 import secured.hash; 9 import secured.util; 10 11 12 @safe public ubyte[] hmac(const ubyte[] key, const ubyte[] data) { 13 return hmac_ex(key, data, HashAlgorithm.SHA2_384); 14 } 15 16 @safe public bool hmac_verify(const ubyte[] test, const ubyte[] key, const ubyte[] data) { 17 ubyte[] hash = hmac_ex(key, data, HashAlgorithm.SHA2_384); 18 return constantTimeEquality(test, hash); 19 } 20 21 @trusted public ubyte[] hmac_ex(const ubyte[] key, const ubyte[] data, HashAlgorithm func) 22 { 23 if (key.length > getHashLength(func)) { 24 throw new CryptographicException(format("HMAC key must be less than or equal to %s bytes in length.", getHashLength(func))); 25 } 26 27 //Create the OpenSSL context 28 EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); 29 if (mdctx == null) { 30 throw new CryptographicException("Unable to create OpenSSL context."); 31 } 32 scope(exit) { 33 if(mdctx !is null) { 34 EVP_MD_CTX_free(mdctx); 35 } 36 } 37 38 //Initialize the hash algorithm 39 auto md = getOpenSSLHashAlgorithm(func); 40 if (EVP_DigestInit_ex(mdctx, md, null) != 1) { 41 throw new CryptographicException("Unable to create hash context."); 42 } 43 44 //Create the HMAC key context 45 auto pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, null, key.ptr, cast(int)key.length); 46 scope(exit) { 47 if(pkey !is null) { 48 EVP_PKEY_free(pkey); 49 } 50 } 51 if (EVP_DigestSignInit(mdctx, null, md, null, pkey) != 1) { 52 throw new CryptographicException("Unable to create HMAC key context."); 53 } 54 55 //Run the provided data through the digest algorithm 56 if (EVP_DigestSignUpdate(mdctx, data.ptr, data.length) != 1) { 57 throw new CryptographicException("Error while updating digest."); 58 } 59 60 //Copy the OpenSSL digest to our D buffer. 61 size_t digestlen; 62 ubyte[] digest = new ubyte[getHashLength(func)]; 63 if (EVP_DigestSignFinal(mdctx, digest.ptr, &digestlen) < 0) { 64 throw new CryptographicException("Error while retrieving the digest."); 65 } 66 67 return digest; 68 } 69 70 @safe public bool hmac_verify_ex(const ubyte[] test, const ubyte[] key, const ubyte[] data, HashAlgorithm func){ 71 ubyte[] hash = hmac_ex(key, data, func); 72 return constantTimeEquality(test, hash); 73 } 74 75 unittest { 76 import std.digest; 77 78 ubyte[48] key = [ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 79 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 80 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF ]; 81 82 writeln("Testing HMAC Basic:"); 83 84 ubyte[] verify_basic_hash = hmac(key, cast(ubyte[])""); 85 assert(hmac_verify(verify_basic_hash, key, cast(ubyte[])"")); 86 87 writeln("Testing HMAC Extended:"); 88 89 ubyte[] vec1 = hmac_ex(key, cast(ubyte[])"", HashAlgorithm.SHA2_384); 90 ubyte[] vec2 = hmac_ex(key, cast(ubyte[])"abc", HashAlgorithm.SHA2_384); 91 ubyte[] vec3 = hmac_ex(key, cast(ubyte[])"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", HashAlgorithm.SHA2_384); 92 93 writeln(toHexString!(LetterCase.lower)(vec1)); 94 writeln(toHexString!(LetterCase.lower)(vec2)); 95 writeln(toHexString!(LetterCase.lower)(vec3)); 96 97 assert(toHexString!(LetterCase.lower)(vec1) == "440b0d5f59c32cbee090c3d9f524b81a9b9708e9b65a46bbc189842b0ab0759d3bf118acca58eda0813fd346e8ccfde4"); 98 assert(toHexString!(LetterCase.lower)(vec2) == "cb5da1048feb76fd75752dc1b699caba124090feac21adb5b4c0f6600e7b626e08d7415660aa0ee79ca5b83e56669a60"); 99 assert(toHexString!(LetterCase.lower)(vec3) == "460b59c0bd8ae48133431185a4583376738be3116cafce47aff7696bd19501b0cf1f1850c3e5fa2992882997493d1c99"); 100 101 ubyte[32] keyshort = [ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 102 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF ]; 103 104 ubyte[] verify_hash = hmac_ex(keyshort, cast(ubyte[])"", HashAlgorithm.SHA2_256); 105 assert(hmac_verify_ex(verify_hash, keyshort, cast(ubyte[])"", HashAlgorithm.SHA2_256)); 106 }