[PATCH] Added NIST CMAC to the CryptoAPI library

From: Tom St Denis
Date: Tue Dec 11 2012 - 14:00:05 EST


This patch adds NIST CMAC cipher based MAC as described in FIPS 800-38B to
the CryptoAPI stack. It also wires up the MAC to the IPsec stack as per
RFC 4494.

This patch was tested against 3.7 with third party hardware IPsec.

Signed-off-by: Tom St Denis <tstdenis@xxxxxxxxxxxxxxxx>
Acked-by: David S. Miller <davem@xxxxxxxxxxxxx>


---
crypto/Kconfig | 8 ++
crypto/Makefile | 1 +
crypto/cmac.c | 313 ++++++++++++++++++++++++++++++++++++++++++
crypto/testmgr.c | 9 ++
crypto/testmgr.h | 40 ++++++
include/uapi/linux/pfkeyv2.h | 1 +
net/xfrm/xfrm_algo.c | 17 +++
7 files changed, 389 insertions(+)
create mode 100644 crypto/cmac.c

diff --git a/crypto/Kconfig b/crypto/Kconfig
index 6563366..1c6a83e 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -301,6 +301,14 @@ config CRYPTO_XCBC
http://csrc.nist.gov/encryption/modes/proposedmodes/
xcbc-mac/xcbc-mac-spec.pdf

+config CRYPTO_CMAC
+ tristate "CMAC support"
+ depends on EXPERIMENTAL
+ select CRYPTO_HASH
+ select CRYPTO_MANAGER
+ help
+ NIST CMAC cipher based MAC
+
config CRYPTO_VMAC
tristate "VMAC support"
depends on EXPERIMENTAL
diff --git a/crypto/Makefile b/crypto/Makefile
index 8cf61ff..5989cbb 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_CRYPTO_USER) += crypto_user.o
obj-$(CONFIG_CRYPTO_HMAC) += hmac.o
obj-$(CONFIG_CRYPTO_VMAC) += vmac.o
obj-$(CONFIG_CRYPTO_XCBC) += xcbc.o
+obj-$(CONFIG_CRYPTO_CMAC) += cmac.o
obj-$(CONFIG_CRYPTO_NULL) += crypto_null.o
obj-$(CONFIG_CRYPTO_MD4) += md4.o
obj-$(CONFIG_CRYPTO_MD5) += md5.o
diff --git a/crypto/cmac.c b/crypto/cmac.c
new file mode 100644
index 0000000..ac81f52
--- /dev/null
+++ b/crypto/cmac.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C)2006 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author:
+ * Tom St Denis <tstdenis@xxxxxxxxxxxxxxxx>
+ *
+ * Derived from the XCBC code of:
+ * Kazunori Miyazawa <miyazawa@xxxxxxxxxxxxxx>
+ */
+
+#include <crypto/internal/hash.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+/*
+ * +------------------------
+ * | <parent tfm>
+ * +------------------------
+ * | cmac_tfm_ctx
+ * +------------------------
+ * | consts (block size * 2)
+ * +------------------------
+ */
+struct cmac_tfm_ctx {
+ struct crypto_cipher *child;
+ u8 ctx[];
+};
+
+/*
+ * +------------------------
+ * | <shash desc>
+ * +------------------------
+ * | cmac_desc_ctx
+ * +------------------------
+ * | odds (block size)
+ * +------------------------
+ * | prev (block size)
+ * +------------------------
+ */
+struct cmac_desc_ctx {
+ unsigned int len;
+ u8 ctx[];
+};
+
+static int crypto_cmac_digest_setkey(struct crypto_shash *parent,
+ const u8 *inkey, unsigned int keylen)
+{
+ unsigned long alignmask = crypto_shash_alignmask(parent);
+ struct cmac_tfm_ctx *ctx = crypto_shash_ctx(parent);
+ int bs = crypto_shash_blocksize(parent);
+ u8 *consts = PTR_ALIGN(&ctx->ctx[0], alignmask + 1);
+ int x, y, err = 0;
+ u8 msb, mask;
+
+ switch (bs) {
+ case 16:
+ mask = 0x87;
+ break;
+ case 8:
+ mask = 0x1B;
+ break;
+ default:
+ return -EINVAL; // only support 64/128 bit block ciphers
+ }
+
+ if ((err = crypto_cipher_setkey(ctx->child, inkey, keylen))) {
+ return err;
+ }
+
+ // encrypt the zero block
+ memset(consts, 0, bs);
+ crypto_cipher_encrypt_one(ctx->child, consts, consts);
+
+ for (x = 0; x < 2; x++) {
+ /* if msb(L * u^(x+1)) = 0 then just shift, otherwise shift and xor constant mask */
+ msb = consts[x*bs + 0] >> 7;
+
+ /* shift left */
+ for (y = 0; y < (bs - 1); y++) {
+ consts[x*bs + y] = ((consts[x*bs + y] << 1) | (consts[x*bs + y+1] >> 7)) & 255;
+ }
+ consts[x*bs + bs - 1] = (consts[x*bs + bs - 1] << 1) ^ (msb ? mask : 0) & 255;
+
+ /* copy up as require */
+ if (x == 0) {
+ memcpy(&consts[(x+1)*bs], &consts[x*bs], bs);
+ }
+ }
+ return 0;
+}
+
+static int crypto_cmac_digest_init(struct shash_desc *pdesc)
+{
+ unsigned long alignmask = crypto_shash_alignmask(pdesc->tfm);
+ struct cmac_desc_ctx *ctx = shash_desc_ctx(pdesc);
+ int bs = crypto_shash_blocksize(pdesc->tfm);
+ u8 *prev = PTR_ALIGN(&ctx->ctx[0], alignmask + 1) + bs;
+
+ ctx->len = 0;
+ memset(prev, 0, bs);
+
+ return 0;
+}
+
+static int crypto_cmac_digest_update(struct shash_desc *pdesc, const u8 *p,
+ unsigned int len)
+{
+ struct crypto_shash *parent = pdesc->tfm;
+ unsigned long alignmask = crypto_shash_alignmask(parent);
+ struct cmac_tfm_ctx *tctx = crypto_shash_ctx(parent);
+ struct cmac_desc_ctx *ctx = shash_desc_ctx(pdesc);
+ struct crypto_cipher *tfm = tctx->child;
+ int bs = crypto_shash_blocksize(parent);
+ u8 *odds = PTR_ALIGN(&ctx->ctx[0], alignmask + 1);
+ u8 *prev = odds + bs;
+
+ /* checking the data can fill the block */
+ if ((ctx->len + len) <= bs) {
+ memcpy(odds + ctx->len, p, len);
+ ctx->len += len;
+ return 0;
+ }
+
+ /* filling odds with new data and encrypting it */
+ memcpy(odds + ctx->len, p, bs - ctx->len);
+ len -= bs - ctx->len;
+ p += bs - ctx->len;
+
+ crypto_xor(prev, odds, bs);
+ crypto_cipher_encrypt_one(tfm, prev, prev);
+
+ /* clearing the length */
+ ctx->len = 0;
+
+ /* encrypting the rest of data */
+ while (len > bs) {
+ crypto_xor(prev, p, bs);
+ crypto_cipher_encrypt_one(tfm, prev, prev);
+ p += bs;
+ len -= bs;
+ }
+
+ /* keeping the surplus of blocksize */
+ if (len) {
+ memcpy(odds, p, len);
+ ctx->len = len;
+ }
+
+ return 0;
+}
+
+static int crypto_cmac_digest_final(struct shash_desc *pdesc, u8 *out)
+{
+ struct crypto_shash *parent = pdesc->tfm;
+ unsigned long alignmask = crypto_shash_alignmask(parent);
+ struct cmac_tfm_ctx *tctx = crypto_shash_ctx(parent);
+ struct cmac_desc_ctx *ctx = shash_desc_ctx(pdesc);
+ struct crypto_cipher *tfm = tctx->child;
+ int bs = crypto_shash_blocksize(parent);
+ u8 *consts = PTR_ALIGN(&tctx->ctx[0], alignmask + 1);
+ u8 *odds = PTR_ALIGN(&ctx->ctx[0], alignmask + 1);
+ u8 *prev = odds + bs;
+ unsigned int offset = 0;
+
+ if (ctx->len != bs) {
+ unsigned int rlen;
+ u8 *p = odds + ctx->len;
+
+ *p = 0x80;
+ p++;
+
+ rlen = bs - ctx->len -1;
+ if (rlen)
+ memset(p, 0, rlen);
+
+ offset += bs;
+ }
+
+ crypto_xor(prev, odds, bs);
+ crypto_xor(prev, consts + offset, bs);
+
+ crypto_cipher_encrypt_one(tfm, out, prev);
+
+ return 0;
+}
+
+static int cmac_init_tfm(struct crypto_tfm *tfm)
+{
+ struct crypto_cipher *cipher;
+ struct crypto_instance *inst = (void *)tfm->__crt_alg;
+ struct crypto_spawn *spawn = crypto_instance_ctx(inst);
+ struct cmac_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ cipher = crypto_spawn_cipher(spawn);
+ if (IS_ERR(cipher))
+ return PTR_ERR(cipher);
+
+ ctx->child = cipher;
+
+ return 0;
+};
+
+static void cmac_exit_tfm(struct crypto_tfm *tfm)
+{
+ struct cmac_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
+ crypto_free_cipher(ctx->child);
+}
+
+static int cmac_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+ struct shash_instance *inst;
+ struct crypto_alg *alg;
+ unsigned long alignmask;
+ int err;
+
+ err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_SHASH);
+ if (err)
+ return err;
+
+ alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER,
+ CRYPTO_ALG_TYPE_MASK);
+ if (IS_ERR(alg))
+ return PTR_ERR(alg);
+
+ switch(alg->cra_blocksize) {
+ case 16:
+ break;
+ default:
+ goto out_put_alg;
+ }
+
+ inst = shash_alloc_instance("cmac", alg);
+ err = PTR_ERR(inst);
+ if (IS_ERR(inst))
+ goto out_put_alg;
+
+ err = crypto_init_spawn(shash_instance_ctx(inst), alg,
+ shash_crypto_instance(inst),
+ CRYPTO_ALG_TYPE_MASK);
+ if (err)
+ goto out_free_inst;
+
+ alignmask = alg->cra_alignmask | 3;
+ inst->alg.base.cra_alignmask = alignmask;
+ inst->alg.base.cra_priority = alg->cra_priority;
+ inst->alg.base.cra_blocksize = alg->cra_blocksize;
+
+ inst->alg.digestsize = alg->cra_blocksize;
+ inst->alg.descsize = ALIGN(sizeof(struct cmac_desc_ctx),
+ crypto_tfm_ctx_alignment()) +
+ (alignmask &
+ ~(crypto_tfm_ctx_alignment() - 1)) +
+ alg->cra_blocksize * 2;
+
+ inst->alg.base.cra_ctxsize = ALIGN(sizeof(struct cmac_tfm_ctx),
+ alignmask + 1) +
+ alg->cra_blocksize * 2;
+ inst->alg.base.cra_init = cmac_init_tfm;
+ inst->alg.base.cra_exit = cmac_exit_tfm;
+
+ inst->alg.init = crypto_cmac_digest_init;
+ inst->alg.update = crypto_cmac_digest_update;
+ inst->alg.final = crypto_cmac_digest_final;
+ inst->alg.setkey = crypto_cmac_digest_setkey;
+
+ err = shash_register_instance(tmpl, inst);
+ if (err) {
+out_free_inst:
+ shash_free_instance(shash_crypto_instance(inst));
+ }
+
+out_put_alg:
+ crypto_mod_put(alg);
+ return err;
+}
+
+static struct crypto_template crypto_cmac_tmpl = {
+ .name = "cmac",
+ .create = cmac_create,
+ .free = shash_free_instance,
+ .module = THIS_MODULE,
+};
+
+static int __init crypto_cmac_module_init(void)
+{
+ return crypto_register_template(&crypto_cmac_tmpl);
+}
+
+static void __exit crypto_cmac_module_exit(void)
+{
+ crypto_unregister_template(&crypto_cmac_tmpl);
+}
+
+module_init(crypto_cmac_module_init);
+module_exit(crypto_cmac_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CMAC keyed hash algorithm");
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 941d75c..46f7463 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -3139,6 +3139,15 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}
}, {
+ .alg = "cmac(aes)",
+ .test = alg_test_hash,
+ .suite = {
+ .hash = {
+ .vecs = aes_cmac128_tv_template,
+ .count = CMAC_AES_TEST_VECTORS
+ }
+ }
+ }, {
.alg = "xcbc(aes)",
.test = alg_test_hash,
.suite = {
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index 76d7f6c..8af6cc9 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -1639,6 +1639,46 @@ static struct hash_testvec hmac_sha256_tv_template[] = {
},
};

+#define CMAC_AES_TEST_VECTORS 4
+
+static struct hash_testvec aes_cmac128_tv_template[] = {
+ {
+ .key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+ .plaintext = zeroed_string,
+ .digest = "\xbb\x1d\x69\x29\xe9\x59\x37\x28\x7f\xa3\x7d\x12\x9b\x75\x67\x46",
+ .psize = 0,
+ .ksize = 16,
+ },
+
+ {
+ .key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+ .plaintext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a",
+ .digest = "\x07\x0a\x16\xb4\x6b\x4d\x41\x44\xf7\x9b\xdd\x9d\xd0\x4a\x28\x7c"
+ .psize = 16,
+ .ksize = 16,
+ },
+ {
+ .key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+ .plaintext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17"
+ "\x2a\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf"
+ "\x8e\x51\x30\xc8\x1c\x46\xa3\x5c\xe4\x11",
+ .digest = "\xdf\xa6\x67\x47de\x9a\xe6\x30\x30\xca\x32\x61\x14\x97\xc8\x27",
+ .psize = 40,
+ .ksize = 16,
+ },
+ {
+ .key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+ .plaintext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17"
+ "\x2a\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf"
+ "\x8e\x51\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a"
+ "\x0a\x52\xef\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b"
+ "\xe6\x6c\x37\x10",
+ .digest = "\x51\xf0\xbe\xbf\x7e\x3b\x9d\x92\xfc\x49\x74\x17\x79\x36\x3c\xfe",
+ .psize = 64,
+ .ksize = 16,
+ },
+}
+
#define XCBC_AES_TEST_VECTORS 6

static struct hash_testvec aes_xcbc128_tv_template[] = {
diff --git a/include/uapi/linux/pfkeyv2.h b/include/uapi/linux/pfkeyv2.h
index 0b80c80..d61898e 100644
--- a/include/uapi/linux/pfkeyv2.h
+++ b/include/uapi/linux/pfkeyv2.h
@@ -296,6 +296,7 @@ struct sadb_x_kmaddress {
#define SADB_X_AALG_SHA2_512HMAC 7
#define SADB_X_AALG_RIPEMD160HMAC 8
#define SADB_X_AALG_AES_XCBC_MAC 9
+#define SADB_X_AALG_AES_CMAC_MAC 10
#define SADB_X_AALG_NULL 251 /* kame */
#define SADB_AALG_MAX 251

diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c
index 4ce2d93..bd6f227 100644
--- a/net/xfrm/xfrm_algo.c
+++ b/net/xfrm/xfrm_algo.c
@@ -265,6 +265,23 @@ static struct xfrm_algo_desc aalg_list[] = {
}
},
{
+ .name = "cmac(aes)",
+
+ .uinfo = {
+ .auth = {
+ .icv_truncbits = 96,
+ .icv_fullbits = 128,
+ }
+ },
+
+ .desc = {
+ .sadb_alg_id = SADB_X_AALG_AES_CMAC_MAC,
+ .sadb_alg_ivlen = 0,
+ .sadb_alg_minbits = 128,
+ .sadb_alg_maxbits = 128
+ }
+},
+{
.name = "xcbc(aes)",

.uinfo = {
--
1.7.10.4
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/