/* * HMAC implementation based on OpenBSD hmac.c * * Copyright (c) 2012 Daniel Farina * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include struct HMAC { struct DigestContext *hash; CxMem *cx; uint8_t *ipad; uint8_t *opad; }; struct HMAC *hmac_new(const struct DigestInfo *impl, const void *key, unsigned int key_len, CxMem *cx) { struct DigestContext *hash; struct HMAC *hmac; unsigned bs = impl->block_len; unsigned i; /* load hash */ hash = digest_new(impl, cx); if (!hash) return NULL; /* struct setup */ hmac = cx_alloc0(cx, sizeof(struct HMAC) + 2*bs); if (!hmac) { digest_free(hash); return NULL; } hmac->hash = hash; hmac->cx = cx; hmac->ipad = (uint8_t *)(hmac + 1); hmac->opad = hmac->ipad + bs; /* copy key to pads */ if (key_len > bs) { digest_update(hash, key, key_len); digest_final(hash, hmac->ipad); digest_reset(hash); memcpy(hmac->opad, hmac->ipad, digest_result_len(hash)); } else { memcpy(hmac->ipad, key, key_len); memcpy(hmac->opad, key, key_len); } /* calculate pads */ for (i = 0; i < bs; i++) { hmac->ipad[i] ^= 0x36; hmac->opad[i] ^= 0x5c; } /* prepare for user data */ digest_update(hmac->hash, hmac->ipad, bs); return hmac; } /* Clean HMAC state */ void hmac_reset(struct HMAC *ctx) { unsigned bs = digest_block_len(ctx->hash); digest_reset(ctx->hash); digest_update(ctx->hash, ctx->ipad, bs); } /* Update HMAC state with more data */ void hmac_update(struct HMAC *ctx, const void *data, unsigned int len) { digest_update(ctx->hash, data, len); } /* Get final HMAC result */ void hmac_final(struct HMAC *ctx, uint8_t *dst) { unsigned bs = digest_block_len(ctx->hash); unsigned rs = digest_result_len(ctx->hash); digest_final(ctx->hash, dst); digest_reset(ctx->hash); digest_update(ctx->hash, ctx->opad, bs); digest_update(ctx->hash, dst, rs); digest_final(ctx->hash, dst); } unsigned hmac_block_len(struct HMAC *ctx) { return digest_block_len(ctx->hash); } unsigned hmac_result_len(struct HMAC *ctx) { return digest_result_len(ctx->hash); }