18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Poly1305 authenticator algorithm, RFC7539
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2015 Martin Willi
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Based on public domain code by Andrew Moon and Daniel J. Bernstein.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
98c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by
108c2ecf20Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or
118c2ecf20Sopenharmony_ci * (at your option) any later version.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <crypto/algapi.h>
158c2ecf20Sopenharmony_ci#include <crypto/internal/hash.h>
168c2ecf20Sopenharmony_ci#include <crypto/internal/poly1305.h>
178c2ecf20Sopenharmony_ci#include <linux/crypto.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic int crypto_poly1305_init(struct shash_desc *desc)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	poly1305_core_init(&dctx->h);
278c2ecf20Sopenharmony_ci	dctx->buflen = 0;
288c2ecf20Sopenharmony_ci	dctx->rset = 0;
298c2ecf20Sopenharmony_ci	dctx->sset = false;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	return 0;
328c2ecf20Sopenharmony_ci}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic unsigned int crypto_poly1305_setdesckey(struct poly1305_desc_ctx *dctx,
358c2ecf20Sopenharmony_ci					       const u8 *src, unsigned int srclen)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	if (!dctx->sset) {
388c2ecf20Sopenharmony_ci		if (!dctx->rset && srclen >= POLY1305_BLOCK_SIZE) {
398c2ecf20Sopenharmony_ci			poly1305_core_setkey(&dctx->core_r, src);
408c2ecf20Sopenharmony_ci			src += POLY1305_BLOCK_SIZE;
418c2ecf20Sopenharmony_ci			srclen -= POLY1305_BLOCK_SIZE;
428c2ecf20Sopenharmony_ci			dctx->rset = 2;
438c2ecf20Sopenharmony_ci		}
448c2ecf20Sopenharmony_ci		if (srclen >= POLY1305_BLOCK_SIZE) {
458c2ecf20Sopenharmony_ci			dctx->s[0] = get_unaligned_le32(src +  0);
468c2ecf20Sopenharmony_ci			dctx->s[1] = get_unaligned_le32(src +  4);
478c2ecf20Sopenharmony_ci			dctx->s[2] = get_unaligned_le32(src +  8);
488c2ecf20Sopenharmony_ci			dctx->s[3] = get_unaligned_le32(src + 12);
498c2ecf20Sopenharmony_ci			src += POLY1305_BLOCK_SIZE;
508c2ecf20Sopenharmony_ci			srclen -= POLY1305_BLOCK_SIZE;
518c2ecf20Sopenharmony_ci			dctx->sset = true;
528c2ecf20Sopenharmony_ci		}
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci	return srclen;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic void poly1305_blocks(struct poly1305_desc_ctx *dctx, const u8 *src,
588c2ecf20Sopenharmony_ci			    unsigned int srclen)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	unsigned int datalen;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (unlikely(!dctx->sset)) {
638c2ecf20Sopenharmony_ci		datalen = crypto_poly1305_setdesckey(dctx, src, srclen);
648c2ecf20Sopenharmony_ci		src += srclen - datalen;
658c2ecf20Sopenharmony_ci		srclen = datalen;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	poly1305_core_blocks(&dctx->h, &dctx->core_r, src,
698c2ecf20Sopenharmony_ci			     srclen / POLY1305_BLOCK_SIZE, 1);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int crypto_poly1305_update(struct shash_desc *desc,
738c2ecf20Sopenharmony_ci				  const u8 *src, unsigned int srclen)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
768c2ecf20Sopenharmony_ci	unsigned int bytes;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (unlikely(dctx->buflen)) {
798c2ecf20Sopenharmony_ci		bytes = min(srclen, POLY1305_BLOCK_SIZE - dctx->buflen);
808c2ecf20Sopenharmony_ci		memcpy(dctx->buf + dctx->buflen, src, bytes);
818c2ecf20Sopenharmony_ci		src += bytes;
828c2ecf20Sopenharmony_ci		srclen -= bytes;
838c2ecf20Sopenharmony_ci		dctx->buflen += bytes;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		if (dctx->buflen == POLY1305_BLOCK_SIZE) {
868c2ecf20Sopenharmony_ci			poly1305_blocks(dctx, dctx->buf,
878c2ecf20Sopenharmony_ci					POLY1305_BLOCK_SIZE);
888c2ecf20Sopenharmony_ci			dctx->buflen = 0;
898c2ecf20Sopenharmony_ci		}
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (likely(srclen >= POLY1305_BLOCK_SIZE)) {
938c2ecf20Sopenharmony_ci		poly1305_blocks(dctx, src, srclen);
948c2ecf20Sopenharmony_ci		src += srclen - (srclen % POLY1305_BLOCK_SIZE);
958c2ecf20Sopenharmony_ci		srclen %= POLY1305_BLOCK_SIZE;
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (unlikely(srclen)) {
998c2ecf20Sopenharmony_ci		dctx->buflen = srclen;
1008c2ecf20Sopenharmony_ci		memcpy(dctx->buf, src, srclen);
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return 0;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int crypto_poly1305_final(struct shash_desc *desc, u8 *dst)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (unlikely(!dctx->sset))
1118c2ecf20Sopenharmony_ci		return -ENOKEY;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	poly1305_final_generic(dctx, dst);
1148c2ecf20Sopenharmony_ci	return 0;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic struct shash_alg poly1305_alg = {
1188c2ecf20Sopenharmony_ci	.digestsize	= POLY1305_DIGEST_SIZE,
1198c2ecf20Sopenharmony_ci	.init		= crypto_poly1305_init,
1208c2ecf20Sopenharmony_ci	.update		= crypto_poly1305_update,
1218c2ecf20Sopenharmony_ci	.final		= crypto_poly1305_final,
1228c2ecf20Sopenharmony_ci	.descsize	= sizeof(struct poly1305_desc_ctx),
1238c2ecf20Sopenharmony_ci	.base		= {
1248c2ecf20Sopenharmony_ci		.cra_name		= "poly1305",
1258c2ecf20Sopenharmony_ci		.cra_driver_name	= "poly1305-generic",
1268c2ecf20Sopenharmony_ci		.cra_priority		= 100,
1278c2ecf20Sopenharmony_ci		.cra_blocksize		= POLY1305_BLOCK_SIZE,
1288c2ecf20Sopenharmony_ci		.cra_module		= THIS_MODULE,
1298c2ecf20Sopenharmony_ci	},
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic int __init poly1305_mod_init(void)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	return crypto_register_shash(&poly1305_alg);
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic void __exit poly1305_mod_exit(void)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	crypto_unregister_shash(&poly1305_alg);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cisubsys_initcall(poly1305_mod_init);
1438c2ecf20Sopenharmony_cimodule_exit(poly1305_mod_exit);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
1478c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Poly1305 authenticator");
1488c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("poly1305");
1498c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("poly1305-generic");
150