162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Poly1305 authenticator algorithm, RFC7539
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Martin Willi
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Based on public domain code by Andrew Moon and Daniel J. Bernstein.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <crypto/internal/poly1305.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <asm/unaligned.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_civoid poly1305_init_generic(struct poly1305_desc_ctx *desc,
1662306a36Sopenharmony_ci			   const u8 key[POLY1305_KEY_SIZE])
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	poly1305_core_setkey(&desc->core_r, key);
1962306a36Sopenharmony_ci	desc->s[0] = get_unaligned_le32(key + 16);
2062306a36Sopenharmony_ci	desc->s[1] = get_unaligned_le32(key + 20);
2162306a36Sopenharmony_ci	desc->s[2] = get_unaligned_le32(key + 24);
2262306a36Sopenharmony_ci	desc->s[3] = get_unaligned_le32(key + 28);
2362306a36Sopenharmony_ci	poly1305_core_init(&desc->h);
2462306a36Sopenharmony_ci	desc->buflen = 0;
2562306a36Sopenharmony_ci	desc->sset = true;
2662306a36Sopenharmony_ci	desc->rset = 2;
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(poly1305_init_generic);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_civoid poly1305_update_generic(struct poly1305_desc_ctx *desc, const u8 *src,
3162306a36Sopenharmony_ci			     unsigned int nbytes)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	unsigned int bytes;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	if (unlikely(desc->buflen)) {
3662306a36Sopenharmony_ci		bytes = min(nbytes, POLY1305_BLOCK_SIZE - desc->buflen);
3762306a36Sopenharmony_ci		memcpy(desc->buf + desc->buflen, src, bytes);
3862306a36Sopenharmony_ci		src += bytes;
3962306a36Sopenharmony_ci		nbytes -= bytes;
4062306a36Sopenharmony_ci		desc->buflen += bytes;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci		if (desc->buflen == POLY1305_BLOCK_SIZE) {
4362306a36Sopenharmony_ci			poly1305_core_blocks(&desc->h, &desc->core_r, desc->buf,
4462306a36Sopenharmony_ci					     1, 1);
4562306a36Sopenharmony_ci			desc->buflen = 0;
4662306a36Sopenharmony_ci		}
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (likely(nbytes >= POLY1305_BLOCK_SIZE)) {
5062306a36Sopenharmony_ci		poly1305_core_blocks(&desc->h, &desc->core_r, src,
5162306a36Sopenharmony_ci				     nbytes / POLY1305_BLOCK_SIZE, 1);
5262306a36Sopenharmony_ci		src += nbytes - (nbytes % POLY1305_BLOCK_SIZE);
5362306a36Sopenharmony_ci		nbytes %= POLY1305_BLOCK_SIZE;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (unlikely(nbytes)) {
5762306a36Sopenharmony_ci		desc->buflen = nbytes;
5862306a36Sopenharmony_ci		memcpy(desc->buf, src, nbytes);
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(poly1305_update_generic);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_civoid poly1305_final_generic(struct poly1305_desc_ctx *desc, u8 *dst)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	if (unlikely(desc->buflen)) {
6662306a36Sopenharmony_ci		desc->buf[desc->buflen++] = 1;
6762306a36Sopenharmony_ci		memset(desc->buf + desc->buflen, 0,
6862306a36Sopenharmony_ci		       POLY1305_BLOCK_SIZE - desc->buflen);
6962306a36Sopenharmony_ci		poly1305_core_blocks(&desc->h, &desc->core_r, desc->buf, 1, 0);
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	poly1305_core_emit(&desc->h, desc->s, dst);
7362306a36Sopenharmony_ci	*desc = (struct poly1305_desc_ctx){};
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(poly1305_final_generic);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
7862306a36Sopenharmony_ciMODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
79