/* $NetBSD$ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Christos Zoulas. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include <sys/cdefs.h> __RCSID("$NetBSD$"); #include <string.h> #include <errno.h> #include <stdlib.h> #include <hmac.h> #include <md2.h> #include <md4.h> #include <md5.h> #include <rmd160.h> #include <sha1.h> #include <sha2.h> #define HMAC_PAD_LEN 64 #define HMAC_IPAD 0x36 #define HMAC_OPAD 0x5C static const struct hmac { const char *name; size_t ctxsize; size_t digsize; size_t blocksize; void (*init)(void *); void (*update)(void *, const uint8_t *, unsigned int); void (*final)(uint8_t *, void *); } hmacs[] = { { "md2", sizeof(MD2_CTX), MD2_DIGEST_LENGTH, MD2_BLOCK_LENGTH, (void *)MD2Init, (void *)MD2Update, (void *)MD2Final, }, { "md4", sizeof(MD4_CTX), MD4_DIGEST_LENGTH, MD4_BLOCK_LENGTH, (void *)MD4Init, (void *)MD4Update, (void *)MD4Final, }, { "md5", sizeof(MD5_CTX), MD5_DIGEST_LENGTH, MD5_BLOCK_LENGTH, (void *)MD5Init, (void *)MD5Update, (void *)MD5Final, }, { "rmd160", sizeof(RMD160_CTX), RMD160_DIGEST_LENGTH, RMD160_BLOCK_LENGTH, (void *)RMD160Init, (void *)RMD160Update, (void *)RMD160Final, }, { "sha1", sizeof(SHA1_CTX), SHA1_DIGEST_LENGTH, SHA1_BLOCK_LENGTH, (void *)SHA1Init, (void *)SHA1Update, (void *)SHA1Final, }, { "sha224", sizeof(SHA224_CTX), SHA224_DIGEST_LENGTH, SHA224_BLOCK_LENGTH, (void *)SHA224_Init, (void *)SHA224_Update, (void *)SHA224_Final, }, { "sha256", sizeof(SHA256_CTX), SHA256_DIGEST_LENGTH, SHA256_BLOCK_LENGTH, (void *)SHA256_Init, (void *)SHA256_Update, (void *)SHA256_Final, }, { "sha384", sizeof(SHA384_CTX), SHA384_DIGEST_LENGTH, SHA384_BLOCK_LENGTH, (void *)SHA384_Init, (void *)SHA384_Update, (void *)SHA384_Final, }, { "sha512", sizeof(SHA512_CTX), SHA512_DIGEST_LENGTH, SHA512_BLOCK_LENGTH, (void *)SHA512_Init, (void *)SHA512_Update, (void *)SHA512_Final, }, }; struct hmac_context { const struct hmac *hmac; void *context; void *digest; }; static void hmac_free(struct hmac_context *hm) { if (hm == NULL) return; free(hm->digest); free(hm->context); free(hm); } static struct hmac_context * hmac_init(const char *name) { struct hmac_context *hm; for (size_t i = 0; i < __arraycount(hmacs); i++) { if (strcmp(hmacs[i].name, name) != 0) continue; if ((hm = calloc(1, sizeof(*hm))) == NULL) return NULL; hm->hmac = &hmacs[i]; hm->context = calloc(1, hm->hmac->ctxsize); if (hm->context == NULL) goto free; hm->digest = calloc(1, hm->hmac->digsize); if (hm->digest) return hm; free: hmac_free(hm); return NULL; } errno = EINVAL; return NULL; } int hmac(const char *name, const uint8_t *key, size_t klen, const uint8_t *text, size_t tlen, uint8_t *digest, size_t *dlen) { uint8_t ipad[HMAC_PAD_LEN * 2], opad[HMAC_PAD_LEN * 2]; size_t i; struct hmac_context *ctx; const struct hmac *h; void *c; ctx = hmac_init(name); if (ctx == NULL) return -1; *dlen = ctx->hmac->digsize; h = ctx->hmac; c = ctx->context; if (klen > h->blocksize) { (*h->init)(c); (*h->update)(c, key, (unsigned int)klen); (*h->final)(ctx->digest, c); key = ctx->digest; klen = *dlen; } /* Form input and output pads for the digests */ for (size_t i = 0; i < sizeof(ipad); i++) { ipad[i] = (i < klen ? key[i] : 0) ^ HMAC_IPAD; opad[i] = (i < klen ? key[i] : 0) ^ HMAC_OPAD; } (*h->init)(c); (*h->update)(c, ipad, h->blocksize); (*h->update)(c, text, (unsigned int)tlen); (*h->final)(digest, c); (*h->init)(c); (*h->update)(c, opad, h->blocksize); (*h->update)(c, digest, (unsigned int)*dlen); (*h->final)(digest, c); hmac_free(ctx); return 0; }