diff options
Diffstat (limited to 'src/crypto/hmac.c')
-rw-r--r-- | src/crypto/hmac.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/src/crypto/hmac.c b/src/crypto/hmac.c new file mode 100644 index 0000000..ab1b24d --- /dev/null +++ b/src/crypto/hmac.c @@ -0,0 +1,201 @@ +/* + * (C) Copyright 2015 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> + * + * GPL v2 only + */ + +#include <common.h> +#include <digest.h> +#include <malloc.h> +#include <init.h> +#include <crypto/internal.h> + +#define asprintf(fmt, arg...) barebox_asprintf(fmt, ##arg) + +struct digest_hmac { + char *name; + unsigned int pad_length; + struct digest_algo algo; +}; + +struct digest_hmac_ctx { + struct digest *d; + + unsigned char *ipad; + unsigned char *opad; + + unsigned char *key; + unsigned int keylen; +}; + +static inline struct digest_hmac *to_digest_hmac(struct digest_algo *algo) +{ + return container_of(algo, struct digest_hmac, algo); +} + +static int digest_hmac_alloc(struct digest *d) +{ + struct digest_hmac_ctx *dh = d->ctx; + struct digest_hmac *hmac = to_digest_hmac(d->algo); + + dh->d = digest_alloc(hmac->name); + if (!dh->d) + return -EINVAL; + + d->length = dh->d->algo->length; + + dh->ipad = xmalloc(hmac->pad_length); + dh->opad = xmalloc(hmac->pad_length); + + return 0; +} + +static void digest_hmac_free(struct digest *d) +{ + struct digest_hmac_ctx *dh = d->ctx; + + free(dh->ipad); + free(dh->opad); + free(dh->key); + + digest_free(dh->d); +} + +static int digest_hmac_set_key(struct digest *d, const unsigned char *key, + unsigned int len) +{ + struct digest_hmac_ctx *dh = d->ctx; + struct digest_hmac *hmac = to_digest_hmac(d->algo); + unsigned char *sum = NULL; + int ret; + + free(dh->key); + if (len > hmac->pad_length) { + sum = xmalloc(digest_length(dh->d)); + ret = digest_digest(dh->d, dh->key, dh->keylen, sum); + if (ret) + goto err; + dh->keylen = digest_length(dh->d); + dh->key = sum; + } else { + dh->key = xmalloc(len); + memcpy(dh->key, key, len); + dh->keylen = len; + } + + ret = 0; +err: + free(sum); + return ret; +} + +static int digest_hmac_init(struct digest *d) +{ + struct digest_hmac_ctx *dh = d->ctx; + struct digest_hmac *hmac = to_digest_hmac(d->algo); + int i, ret; + unsigned char *key = dh->key; + unsigned int keylen = dh->keylen; + + memset(dh->ipad, 0x36, hmac->pad_length); + memset(dh->opad, 0x5C, hmac->pad_length); + + for (i = 0; i < keylen; i++) { + dh->ipad[i] = (unsigned char)(dh->ipad[i] ^ key[i]); + dh->opad[i] = (unsigned char)(dh->opad[i] ^ key[i]); + } + + ret = digest_init(dh->d); + if (ret) + return ret; + return digest_update(dh->d, dh->ipad, hmac->pad_length); +} + +static int digest_hmac_update(struct digest *d, const void *data, + unsigned long len) +{ + struct digest_hmac_ctx *dh = d->ctx; + + return digest_update(dh->d, data, len); +} + +static int digest_hmac_final(struct digest *d, unsigned char *md) +{ + struct digest_hmac_ctx *dh = d->ctx; + struct digest_hmac *hmac = to_digest_hmac(d->algo); + unsigned char *tmp = NULL; + int ret; + + tmp = xmalloc(digest_length(d)); + + ret = digest_final(dh->d, tmp); + if (ret) + goto err; + ret = digest_init(dh->d); + if (ret) + goto err; + ret = digest_update(dh->d, dh->opad, hmac->pad_length); + if (ret) + goto err; + ret = digest_update(dh->d, tmp, digest_length(d)); + if (ret) + goto err; + ret = digest_final(dh->d, md); + +err: + free(tmp); + + return ret; +} + +struct digest_algo hmac_algo = { + .base = { + .priority = 0, + .flags = DIGEST_ALGO_NEED_KEY, + }, + .alloc = digest_hmac_alloc, + .init = digest_hmac_init, + .update = digest_hmac_update, + .final = digest_hmac_final, + .digest = digest_generic_digest, + .verify = digest_generic_verify, + .set_key = digest_hmac_set_key, + .free = digest_hmac_free, + .ctx_length = sizeof(struct digest_hmac), +}; + +static int digest_hmac_register(char *name, unsigned int pad_length) +{ + struct digest_hmac *dh; + + if (!name || !pad_length) + return -EINVAL; + + dh = xzalloc(sizeof(*dh)); + dh->name = xstrdup(name); + dh->pad_length = pad_length; + dh->algo = hmac_algo; + dh->algo.base.name = asprintf("hmac(%s)", name); + dh->algo.base.driver_name = asprintf("hmac(%s)-generic", name); + + return digest_algo_register(&dh->algo); +} + +static int digest_hmac_initcall(void) +{ + if (IS_ENABLED(CONFIG_MD5)) + digest_hmac_register("md5", 64); + if (IS_ENABLED(CONFIG_SHA1)) + digest_hmac_register("sha1", 64); + if (IS_ENABLED(CONFIG_SHA224)) + digest_hmac_register("sha224", 64); + if (IS_ENABLED(CONFIG_SHA256)) + digest_hmac_register("sha256", 64); + if (IS_ENABLED(CONFIG_SHA384)) + digest_hmac_register("sha384", 128); + if (IS_ENABLED(CONFIG_SHA512)) + digest_hmac_register("sha512", 128); + + return 0; +} +crypto_initcall(digest_hmac_initcall); |