162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Poly1305 authenticator algorithm, RFC7539
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2015 Martin Willi
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Based on public domain code by Andrew Moon and Daniel J. Bernstein.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
962306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by
1062306a36Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or
1162306a36Sopenharmony_ci * (at your option) any later version.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <crypto/algapi.h>
1562306a36Sopenharmony_ci#include <crypto/internal/hash.h>
1662306a36Sopenharmony_ci#include <crypto/internal/poly1305.h>
1762306a36Sopenharmony_ci#include <linux/crypto.h>
1862306a36Sopenharmony_ci#include <linux/kernel.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <asm/unaligned.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic int crypto_poly1305_init(struct shash_desc *desc)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	poly1305_core_init(&dctx->h);
2762306a36Sopenharmony_ci	dctx->buflen = 0;
2862306a36Sopenharmony_ci	dctx->rset = 0;
2962306a36Sopenharmony_ci	dctx->sset = false;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	return 0;
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic unsigned int crypto_poly1305_setdesckey(struct poly1305_desc_ctx *dctx,
3562306a36Sopenharmony_ci					       const u8 *src, unsigned int srclen)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	if (!dctx->sset) {
3862306a36Sopenharmony_ci		if (!dctx->rset && srclen >= POLY1305_BLOCK_SIZE) {
3962306a36Sopenharmony_ci			poly1305_core_setkey(&dctx->core_r, src);
4062306a36Sopenharmony_ci			src += POLY1305_BLOCK_SIZE;
4162306a36Sopenharmony_ci			srclen -= POLY1305_BLOCK_SIZE;
4262306a36Sopenharmony_ci			dctx->rset = 2;
4362306a36Sopenharmony_ci		}
4462306a36Sopenharmony_ci		if (srclen >= POLY1305_BLOCK_SIZE) {
4562306a36Sopenharmony_ci			dctx->s[0] = get_unaligned_le32(src +  0);
4662306a36Sopenharmony_ci			dctx->s[1] = get_unaligned_le32(src +  4);
4762306a36Sopenharmony_ci			dctx->s[2] = get_unaligned_le32(src +  8);
4862306a36Sopenharmony_ci			dctx->s[3] = get_unaligned_le32(src + 12);
4962306a36Sopenharmony_ci			src += POLY1305_BLOCK_SIZE;
5062306a36Sopenharmony_ci			srclen -= POLY1305_BLOCK_SIZE;
5162306a36Sopenharmony_ci			dctx->sset = true;
5262306a36Sopenharmony_ci		}
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci	return srclen;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void poly1305_blocks(struct poly1305_desc_ctx *dctx, const u8 *src,
5862306a36Sopenharmony_ci			    unsigned int srclen)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	unsigned int datalen;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (unlikely(!dctx->sset)) {
6362306a36Sopenharmony_ci		datalen = crypto_poly1305_setdesckey(dctx, src, srclen);
6462306a36Sopenharmony_ci		src += srclen - datalen;
6562306a36Sopenharmony_ci		srclen = datalen;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	poly1305_core_blocks(&dctx->h, &dctx->core_r, src,
6962306a36Sopenharmony_ci			     srclen / POLY1305_BLOCK_SIZE, 1);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int crypto_poly1305_update(struct shash_desc *desc,
7362306a36Sopenharmony_ci				  const u8 *src, unsigned int srclen)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
7662306a36Sopenharmony_ci	unsigned int bytes;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (unlikely(dctx->buflen)) {
7962306a36Sopenharmony_ci		bytes = min(srclen, POLY1305_BLOCK_SIZE - dctx->buflen);
8062306a36Sopenharmony_ci		memcpy(dctx->buf + dctx->buflen, src, bytes);
8162306a36Sopenharmony_ci		src += bytes;
8262306a36Sopenharmony_ci		srclen -= bytes;
8362306a36Sopenharmony_ci		dctx->buflen += bytes;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		if (dctx->buflen == POLY1305_BLOCK_SIZE) {
8662306a36Sopenharmony_ci			poly1305_blocks(dctx, dctx->buf,
8762306a36Sopenharmony_ci					POLY1305_BLOCK_SIZE);
8862306a36Sopenharmony_ci			dctx->buflen = 0;
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (likely(srclen >= POLY1305_BLOCK_SIZE)) {
9362306a36Sopenharmony_ci		poly1305_blocks(dctx, src, srclen);
9462306a36Sopenharmony_ci		src += srclen - (srclen % POLY1305_BLOCK_SIZE);
9562306a36Sopenharmony_ci		srclen %= POLY1305_BLOCK_SIZE;
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (unlikely(srclen)) {
9962306a36Sopenharmony_ci		dctx->buflen = srclen;
10062306a36Sopenharmony_ci		memcpy(dctx->buf, src, srclen);
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return 0;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int crypto_poly1305_final(struct shash_desc *desc, u8 *dst)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (unlikely(!dctx->sset))
11162306a36Sopenharmony_ci		return -ENOKEY;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	poly1305_final_generic(dctx, dst);
11462306a36Sopenharmony_ci	return 0;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic struct shash_alg poly1305_alg = {
11862306a36Sopenharmony_ci	.digestsize	= POLY1305_DIGEST_SIZE,
11962306a36Sopenharmony_ci	.init		= crypto_poly1305_init,
12062306a36Sopenharmony_ci	.update		= crypto_poly1305_update,
12162306a36Sopenharmony_ci	.final		= crypto_poly1305_final,
12262306a36Sopenharmony_ci	.descsize	= sizeof(struct poly1305_desc_ctx),
12362306a36Sopenharmony_ci	.base		= {
12462306a36Sopenharmony_ci		.cra_name		= "poly1305",
12562306a36Sopenharmony_ci		.cra_driver_name	= "poly1305-generic",
12662306a36Sopenharmony_ci		.cra_priority		= 100,
12762306a36Sopenharmony_ci		.cra_blocksize		= POLY1305_BLOCK_SIZE,
12862306a36Sopenharmony_ci		.cra_module		= THIS_MODULE,
12962306a36Sopenharmony_ci	},
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic int __init poly1305_mod_init(void)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	return crypto_register_shash(&poly1305_alg);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic void __exit poly1305_mod_exit(void)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	crypto_unregister_shash(&poly1305_alg);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cisubsys_initcall(poly1305_mod_init);
14362306a36Sopenharmony_cimodule_exit(poly1305_mod_exit);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
14662306a36Sopenharmony_ciMODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
14762306a36Sopenharmony_ciMODULE_DESCRIPTION("Poly1305 authenticator");
14862306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("poly1305");
14962306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("poly1305-generic");
150