As we will use the best sha algo at runtime Add a new init level crypto_initcall to ensure that all the sha present before hmac Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
200 lines
4.2 KiB
C
200 lines
4.2 KiB
C
/*
|
|
* (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>
|
|
|
|
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);
|