Last active
November 25, 2020 17:17
-
-
Save devendranaga/96e18e88f3d3742cdce00679ca4875ac to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* HMAC tutorial | |
* | |
* Written by Devendra Naga ([email protected]) | |
* | |
* License MIT | |
*/ | |
#include <iostream> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <openssl/rand.h> | |
#include <openssl/hmac.h> | |
#include <openssl/evp.h> | |
#include <openssl/conf.h> | |
#include <openssl/err.h> | |
#include <openssl/ssl.h> | |
class hmac { | |
public: | |
hmac() { // initialize openssl | |
ERR_load_ASN1_strings(); | |
OpenSSL_add_all_algorithms(); | |
#if OPENSSL_VERSION_NUMBER >= 0x10100003L | |
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL); | |
#endif | |
RAND_poll(); | |
} | |
~hmac() { // de initialize openssl | |
EVP_cleanup(); | |
CRYPTO_cleanup_all_ex_data(); | |
ERR_free_strings(); | |
} | |
// generate hmac key | |
int generate_key(std::string keyfile, int keylen) | |
{ | |
int ret; | |
ret = RAND_bytes(hmac_key, keylen); | |
if (ret != 1) { | |
return -1; | |
} | |
FILE *fp; | |
fp = fopen(keyfile.c_str(), "wb"); | |
if (!fp) { | |
return -1; | |
} | |
fwrite(hmac_key, keylen, 1, fp); | |
fflush(fp); | |
fclose(fp); | |
return 0; | |
} | |
// sign message with hmac sha256 | |
int sign_message(std::string message, uint8_t *hmac_hash) | |
{ | |
int ret; | |
EVP_PKEY *evp_key; | |
EVP_MD_CTX *md_ctx; | |
// load hmac key | |
evp_key = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, hmac_key, 16); | |
if (!evp_key) { | |
return -1; | |
} | |
// initialize md_ctx | |
md_ctx = EVP_MD_CTX_create(); | |
if (!md_ctx) { | |
return -1; | |
} | |
// get sha256 md context ptr | |
const EVP_MD *md = EVP_sha256(); | |
// initialize digest context ptr | |
ret = EVP_DigestInit_ex(md_ctx, md, NULL); | |
if (ret != 1) { | |
return -1; | |
} | |
// initilize signature context ptr | |
ret = EVP_DigestSignInit(md_ctx, NULL, md, NULL, evp_key); | |
if (ret != 1) { | |
return -1; | |
} | |
// update the message .. call it many times if there is more data | |
ret = EVP_DigestSignUpdate(md_ctx, message.c_str(), message.length()); | |
if (ret != 1) { | |
return -1; | |
} | |
size_t hmac_len = 0; | |
// calculate final signature length | |
ret = EVP_DigestSignFinal(md_ctx, NULL, &hmac_len); | |
if (ret != 1) { | |
return -1; | |
} | |
// copy the hash into the sign buffer | |
ret = EVP_DigestSignFinal(md_ctx, hmac_hash, &hmac_len); | |
if (ret != 1) { | |
return -1; | |
} | |
EVP_MD_CTX_destroy(md_ctx); | |
return hmac_len; | |
} | |
int verify_message(std::string message, uint8_t *hmac_hash, size_t hmac_hash_len) | |
{ | |
EVP_MD_CTX *md_ctx; | |
EVP_PKEY *evp_key; | |
int ret; | |
// initialize the hmac key | |
evp_key = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, hmac_key, 16); | |
if (!evp_key) { | |
return -1; | |
} | |
// create md context | |
md_ctx = EVP_MD_CTX_create(); | |
if (!md_ctx) { | |
return -1; | |
} | |
// get sha256 context | |
const EVP_MD *md = EVP_sha256(); | |
// initilize digest context ptr | |
ret = EVP_DigestSignInit(md_ctx, NULL, md, NULL, evp_key); | |
if (ret != 1) { | |
return -1; | |
} | |
uint8_t hmac_hash_local[128]; | |
size_t hash_len_local; | |
// update the input message | |
ret = EVP_DigestSignUpdate(md_ctx, message.c_str(), message.length()); | |
if (ret != 1) { | |
return -1; | |
} | |
// get local signatures | |
ret = EVP_DigestSignFinal(md_ctx, hmac_hash_local, &hash_len_local); | |
if (ret != 1) { | |
return -1; | |
} | |
size_t min = (hmac_hash_len < hash_len_local) ? hmac_hash_len : hash_len_local; | |
// compare the locally generated with whats received | |
ret = !!CRYPTO_memcmp(hmac_hash_local, hmac_hash, min); | |
EVP_MD_CTX_destroy(md_ctx); | |
if (ret != 0) { | |
return -1; | |
} | |
return 0; | |
} | |
void dump_signature(uint8_t *hmac, size_t hmac_len) | |
{ | |
printf("hmac: "); | |
for (auto i = 0; i < hmac_len; i ++) { | |
printf("%02x", hmac[i] & 0xff); | |
} | |
printf("\n"); | |
} | |
private: | |
uint8_t hmac_key[32]; | |
}; | |
int main() | |
{ | |
hmac test; | |
std::string hmac_str {"testing hmac with sha256"}; | |
uint8_t hmac_hash[32]; | |
size_t hash_len; | |
int ret; | |
ret = test.generate_key("./hmac_16.key", 16); | |
if (ret == 0) { | |
std::cout << "key generation ok " << std::endl; | |
} else { | |
std::cout << "key generation failed" << std::endl; | |
return -1; | |
} | |
hash_len = test.sign_message(hmac_str, hmac_hash); | |
if (hash_len == -1) { | |
std::cout << "couldn't sign message with hmac-sha256" << std::endl; | |
return -1; | |
} | |
test.dump_signature(hmac_hash, hash_len); | |
ret = test.verify_message(hmac_str, hmac_hash, hash_len); | |
if (ret != 0) { | |
std::cout << "couldn't verify signatures with hmac-sha256" << std::endl; | |
return -1; | |
} | |
std::cout << "signatures match.. message is authenticated" << std::endl; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment