9
0
Fork 0
barebox/crypto/hmac.c

176 lines
3.6 KiB
C
Raw Normal View History

/*
* (C) Copyright 2015 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
*
* GPL v2 only
*/
#include <common.h>
#include <digest.h>
#include <malloc.h>
#include "internal.h"
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;
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 = {
.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),
};
int digest_hmac_register(struct digest_algo *algo, unsigned int pad_length)
{
struct digest_hmac *dh;
if (!algo || !pad_length)
return -EINVAL;
dh = xzalloc(sizeof(*dh));
dh->name = xstrdup(algo->name);
dh->pad_length = pad_length;
dh->algo = hmac_algo;
dh->algo.length = algo->length;
dh->algo.name = asprintf("hmac(%s)", algo->name);
return digest_algo_register(&dh->algo);
}