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 }