Skip to content

Instantly share code, notes, and snippets.

@devendranaga
Last active November 25, 2020 17:17
Show Gist options
  • Save devendranaga/96e18e88f3d3742cdce00679ca4875ac to your computer and use it in GitHub Desktop.
Save devendranaga/96e18e88f3d3742cdce00679ca4875ac to your computer and use it in GitHub Desktop.
/**
*
* 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