linux/crypto/poly1305_generic.c
<<
>>
Prefs
   1/*
   2 * Poly1305 authenticator algorithm, RFC7539
   3 *
   4 * Copyright (C) 2015 Martin Willi
   5 *
   6 * Based on public domain code by Andrew Moon and Daniel J. Bernstein.
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <crypto/algapi.h>
  15#include <crypto/internal/hash.h>
  16#include <crypto/internal/poly1305.h>
  17#include <linux/crypto.h>
  18#include <linux/kernel.h>
  19#include <linux/module.h>
  20#include <asm/unaligned.h>
  21
  22static int crypto_poly1305_init(struct shash_desc *desc)
  23{
  24        struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
  25
  26        poly1305_core_init(&dctx->h);
  27        dctx->buflen = 0;
  28        dctx->rset = 0;
  29        dctx->sset = false;
  30
  31        return 0;
  32}
  33
  34static unsigned int crypto_poly1305_setdesckey(struct poly1305_desc_ctx *dctx,
  35                                               const u8 *src, unsigned int srclen)
  36{
  37        if (!dctx->sset) {
  38                if (!dctx->rset && srclen >= POLY1305_BLOCK_SIZE) {
  39                        poly1305_core_setkey(&dctx->core_r, src);
  40                        src += POLY1305_BLOCK_SIZE;
  41                        srclen -= POLY1305_BLOCK_SIZE;
  42                        dctx->rset = 2;
  43                }
  44                if (srclen >= POLY1305_BLOCK_SIZE) {
  45                        dctx->s[0] = get_unaligned_le32(src +  0);
  46                        dctx->s[1] = get_unaligned_le32(src +  4);
  47                        dctx->s[2] = get_unaligned_le32(src +  8);
  48                        dctx->s[3] = get_unaligned_le32(src + 12);
  49                        src += POLY1305_BLOCK_SIZE;
  50                        srclen -= POLY1305_BLOCK_SIZE;
  51                        dctx->sset = true;
  52                }
  53        }
  54        return srclen;
  55}
  56
  57static void poly1305_blocks(struct poly1305_desc_ctx *dctx, const u8 *src,
  58                            unsigned int srclen)
  59{
  60        unsigned int datalen;
  61
  62        if (unlikely(!dctx->sset)) {
  63                datalen = crypto_poly1305_setdesckey(dctx, src, srclen);
  64                src += srclen - datalen;
  65                srclen = datalen;
  66        }
  67
  68        poly1305_core_blocks(&dctx->h, &dctx->core_r, src,
  69                             srclen / POLY1305_BLOCK_SIZE, 1);
  70}
  71
  72static int crypto_poly1305_update(struct shash_desc *desc,
  73                                  const u8 *src, unsigned int srclen)
  74{
  75        struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
  76        unsigned int bytes;
  77
  78        if (unlikely(dctx->buflen)) {
  79                bytes = min(srclen, POLY1305_BLOCK_SIZE - dctx->buflen);
  80                memcpy(dctx->buf + dctx->buflen, src, bytes);
  81                src += bytes;
  82                srclen -= bytes;
  83                dctx->buflen += bytes;
  84
  85                if (dctx->buflen == POLY1305_BLOCK_SIZE) {
  86                        poly1305_blocks(dctx, dctx->buf,
  87                                        POLY1305_BLOCK_SIZE);
  88                        dctx->buflen = 0;
  89                }
  90        }
  91
  92        if (likely(srclen >= POLY1305_BLOCK_SIZE)) {
  93                poly1305_blocks(dctx, src, srclen);
  94                src += srclen - (srclen % POLY1305_BLOCK_SIZE);
  95                srclen %= POLY1305_BLOCK_SIZE;
  96        }
  97
  98        if (unlikely(srclen)) {
  99                dctx->buflen = srclen;
 100                memcpy(dctx->buf, src, srclen);
 101        }
 102
 103        return 0;
 104}
 105
 106static int crypto_poly1305_final(struct shash_desc *desc, u8 *dst)
 107{
 108        struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
 109
 110        if (unlikely(!dctx->sset))
 111                return -ENOKEY;
 112
 113        poly1305_final_generic(dctx, dst);
 114        return 0;
 115}
 116
 117static struct shash_alg poly1305_alg = {
 118        .digestsize     = POLY1305_DIGEST_SIZE,
 119        .init           = crypto_poly1305_init,
 120        .update         = crypto_poly1305_update,
 121        .final          = crypto_poly1305_final,
 122        .descsize       = sizeof(struct poly1305_desc_ctx),
 123        .base           = {
 124                .cra_name               = "poly1305",
 125                .cra_driver_name        = "poly1305-generic",
 126                .cra_priority           = 100,
 127                .cra_blocksize          = POLY1305_BLOCK_SIZE,
 128                .cra_module             = THIS_MODULE,
 129        },
 130};
 131
 132static int __init poly1305_mod_init(void)
 133{
 134        return crypto_register_shash(&poly1305_alg);
 135}
 136
 137static void __exit poly1305_mod_exit(void)
 138{
 139        crypto_unregister_shash(&poly1305_alg);
 140}
 141
 142subsys_initcall(poly1305_mod_init);
 143module_exit(poly1305_mod_exit);
 144
 145MODULE_LICENSE("GPL");
 146MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
 147MODULE_DESCRIPTION("Poly1305 authenticator");
 148MODULE_ALIAS_CRYPTO("poly1305");
 149MODULE_ALIAS_CRYPTO("poly1305-generic");
 150