summaryrefslogtreecommitdiffstats
path: root/crypto/hmac.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/hmac.c')
-rw-r--r--crypto/hmac.c159
1 files changed, 159 insertions, 0 deletions
diff --git a/crypto/hmac.c b/crypto/hmac.c
new file mode 100644
index 0000000000..4e6bfa3f09
--- /dev/null
+++ b/crypto/hmac.c
@@ -0,0 +1,159 @@
+/*
+ * (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);
+
+ free(dh->key);
+ if (len > hmac->pad_length) {
+ unsigned char *sum;
+
+ sum = xmalloc(digest_length(dh->d));
+ digest_init(dh->d);
+ digest_update(dh->d, dh->key, dh->keylen);
+ digest_final(dh->d, sum);
+ dh->keylen = digest_length(dh->d);
+ dh->key = sum;
+ } else {
+ dh->key = xmalloc(len);
+ memcpy(dh->key, key, len);
+ dh->keylen = len;
+ }
+
+ return 0;
+}
+
+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;
+ 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]);
+ }
+
+ digest_init(dh->d);
+ digest_update(dh->d, dh->ipad, hmac->pad_length);
+
+ return 0;
+}
+
+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;
+
+ tmp = xmalloc(digest_length(d));
+
+ digest_final(dh->d, tmp);
+ digest_init(dh->d);
+ digest_update(dh->d, dh->opad, hmac->pad_length);
+ digest_update(dh->d, tmp, digest_length(d));
+ digest_final(dh->d, md);
+
+ free(tmp);
+
+ return 0;
+}
+
+struct digest_algo hmac_algo = {
+ .alloc = digest_hmac_alloc,
+ .init = digest_hmac_init,
+ .update = digest_hmac_update,
+ .final = digest_hmac_final,
+ .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);
+}