[email protected] (Niels Möller) writes:

> The current C implementation uses radix 26, and 25 multiplies (32x32
> --> 64) per block. And quite a lot of shifts. A radix 32 variant
> analogous to the above would need 16 long multiplies and 4 short. I'd
> expect that to be faster on most machines, but I'd have to try that out.

I've tried this out, see attached file. It has an #if 0/1 to choose
between radix 64 (depending on the non-standard __int128 type for
accumulated products) and radix 32 (portable C).

This is the speed I get for C implementations of poly1305_update on my
x86_64 laptop:

* Radix 26: 1.2 GByte/s (old code)

* Radix 32: 1.3 GByte/s

* Radix 64: 2.2 GByte/s

It would be interesting with benchmarks on actual 32-bit hardware,
32-bit ARM likely being the most relevant arch.

For comparison, the current x86_64 asm version: 2.5 GByte/s.

If I understood correctly, the suggestion to use radix 26 in djb's
original paper was motivated by a high-speed implementation using
floating point arithmetic (possibly in combination with SIMD), where the
product of two 26-bit integers can be represented exactly in an IEEE
double (but it gets a bit subtle if we want to accumulate several
products), I haven't really looked into implementing poly1305 with
either floating point or SIMD.

To improve test coverage, I've also extended poly1305 tests with tests
on random inputs, with results compared to a reference implementation
based on gmp/mini-gmp. I intend to merge those testing changes soon.
See
https://gitlab.com/gnutls/nettle/-/commit/b48217c8058676c8cd2fd12cdeba457755ace309.

Unfortunately, the http interface of the main git repo at Lysator is
inaccessible at the moment due to an expired certificate; should be
fixed in a day or two.

Regards,
/Niels

/* poly1305-internal.c

   Copyright: 2013 Nikos Mavrogiannopoulos
   Copyright: 2013, 2022 Niels Möller

   This file is part of GNU Nettle.

   GNU Nettle is free software: you can redistribute it and/or
   modify it under the terms of either:

     * the GNU Lesser General Public License as published by the Free
       Software Foundation; either version 3 of the License, or (at your
       option) any later version.

   or

     * 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.

   or both in parallel, as here.

   GNU Nettle 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 copies of the GNU General Public License and
   the GNU Lesser General Public License along with this program.  If
   not, see http://www.gnu.org/licenses/.
*/

#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <assert.h>
#include <string.h>

#include "poly1305.h"
#include "poly1305-internal.h"

#include "macros.h"

#if 1
typedef unsigned __int128 nettle_uint128_t;

#define M64(a,b) ((nettle_uint128_t)(a) * (b))

#define r0 r.r64[0]
#define r1 r.r64[1]
#define s1 r.r64[2]
#define h0 h.h64[0]
#define h1 h.h64[1]
#define h2 hh

void
_nettle_poly1305_set_key(struct poly1305_ctx *ctx, const uint8_t key[16])
{
  uint64_t t0, t1;
  t0 = LE_READ_UINT64(key);
  t1 = LE_READ_UINT64(key + 8);

  ctx->r0 = t0 & UINT64_C(0x0ffffffc0fffffff);
  ctx->r1 = t1 & UINT64_C(0x0ffffffc0ffffffc);
  ctx->s1 = 5*(ctx->r1 >> 2);

  ctx->h0 = 0;
  ctx->h1 = 0;
  ctx->h2 = 0;
}

void
_nettle_poly1305_block (struct poly1305_ctx *ctx, const uint8_t *m, unsigned t2)
{
  uint64_t t0, t1;
  nettle_uint128_t s, f0, f1;

  /* Add in message block */
  t0 = ctx->h0 + LE_READ_UINT64(m);
  s = (nettle_uint128_t) (t0 < ctx->h0) + ctx->h1 + LE_READ_UINT64(m+8);
  t1 = s;
  t2 += (s >> 64) + ctx->h2;

  /* Key constants are bounded by rk < 2^60, sk < 5*2^58, therefore
     all the fk sums fit in 128 bits without overflow, with at least
     one bit margin. */
  f0 = M64(t0, ctx->r0) + M64(t1, ctx->s1);
  f1 = M64(t0, ctx->r1) + M64(t1, ctx->r0) + t2 * ctx->s1
    + ((nettle_uint128_t)(t2 * ctx->r0) << 64);

  /* Fold high part of f1. */
  f0 += 5*(f1 >> 66);
  f1 &= ((nettle_uint128_t) 1 << 66) - 1;
  ctx->h0 = f0;
  f1 += f0 >> 64;
  ctx->h1 = f1;
  ctx->h2 = f1 >> 64;
  assert (ctx->h2 <= 4);
}

/* Adds digest to the nonce */
void
_nettle_poly1305_digest (struct poly1305_ctx *ctx, union nettle_block16 *s)
{
  uint64_t t0, t1, t2, c1, mask, s0;

  t0 = ctx->h0;
  t1 = ctx->h1;
  t2 = ctx->h2;

  /* Compute resulting carries when adding 5. */
  c1 = t0 > -(UINT64_C(5));
  t2 += (t1 + c1 < c1);

  /* Set if H >= 2^130 - 5 */
  mask = - (t2 >> 2);

  t0 += mask & 5;
  t1 += mask & c1;

  /* FIXME: Take advantage of s being aligned as an unsigned long. */
  s0 = t0 + LE_READ_UINT64(s->b);
  t1 += (s0 < t0) + LE_READ_UINT64(s->b+8);

  LE_WRITE_UINT64(s->b, s0);
  LE_WRITE_UINT64(s->b+8, t1);

  ctx->h0 = 0;
  ctx->h1 = 0;
  ctx->h2 = 0;
}
#else
#define M32(a,b) ((uint64_t)(a) * (b))

#define r0 r.r32[0]
#define r1 r.r32[1]
#define r2 r.r32[2]
#define r3 r.r32[3]
#define r4 r.r32[4]
#define s1 r.r32[5]
#define s2 s32[0]
#define s3 s32[1]
#define s4 s32[2]

#define h0 h.h32[0]
#define h1 h.h32[1]
#define h2 h.h32[2]
#define h3 h.h32[3]
#define h4 hh

void
_nettle_poly1305_set_key(struct poly1305_ctx *ctx, const uint8_t key[16])
{
  uint32_t t0,t1,t2,t3;
  t0 = LE_READ_UINT32(key);
  t1 = LE_READ_UINT32(key+4);
  t2 = LE_READ_UINT32(key+8);
  t3 = LE_READ_UINT32(key+12);

  ctx->r0 = t0 & 0x0fffffff;
  ctx->r1 = t1 & 0x0ffffffc;
  ctx->r2 = t2 & 0x0ffffffc;
  ctx->r3 = t3 & 0x0ffffffc;

  ctx->s1 = 5*(ctx->r1 >> 2);
  ctx->s2 = 5*(ctx->r2 >> 2);
  ctx->s3 = 5*(ctx->r3 >> 2);

  ctx->h0 = 0;
  ctx->h1 = 0;
  ctx->h2 = 0;
  ctx->h3 = 0;
  ctx->h4 = 0;
}

void
_nettle_poly1305_block (struct poly1305_ctx *ctx, const uint8_t *m, unsigned t4)
{
  uint32_t t0, t1, t2, t3;
  uint64_t s, f0, f1, f2, f3;

  /* Add in message block */
  t0 = ctx->h0 + LE_READ_UINT32(m);
  t0 = s;
  s =  (uint64_t) (t0 < ctx->h0) + ctx->h1 + LE_READ_UINT32(m+4);
  t1 = s;
  s =  (s >> 32) + ctx->h2 + LE_READ_UINT32(m+8);
  t2 = s;
  s =  (s >> 32) + ctx->h3 + LE_READ_UINT32(m+12);
  t3 = s;
  t4 += (s >> 32) + ctx->h4;

  /* Key constants are bounded by rk < 2^28, sk < 5*2^26, therefore
     all the fk sums fit in 64 bits without overflow, with at least
     one bit margin. */
  f0 = M32(t0, ctx->r0) + M32(t1, ctx->s3) + M32(t2, ctx->s2) + M32(t3, ctx->s1);
  f1 = M32(t0, ctx->r1) + M32(t1, ctx->r0) + M32(t2, ctx->s3) + M32(t3, ctx->s2) + t4 * ctx->s1;
  f2 = M32(t0, ctx->r2) + M32(t1, ctx->r1) + M32(t2, ctx->r0) + M32(t3, ctx->s3) + t4 * ctx->s2;
  f3 = M32(t0, ctx->r3) + M32(t1, ctx->r2) + M32(t2, ctx->r1) + M32(t3, ctx->r0) + t4 * ctx->s3
    + ((uint64_t)(t4*ctx->r0) << 32);

  /* Fold high part of f3. */
  f0 += 5*(f3 >> 34);
  f3 &= UINT64_C(0x3ffffffff);
  ctx->h0 = f0;
  f1 += (f0 >> 32);
  ctx->h1 = f1;
  f2 += (f1 >> 32);
  ctx->h2 = f2;
  f3 += (f2 >> 32);
  ctx->h3 = f3;
  ctx->h4 = f3 >> 32;
  assert (ctx->h4 <= 4);
}

/* Adds digest to the nonce */
void
_nettle_poly1305_digest (struct poly1305_ctx *ctx, union nettle_block16 *s)
{
  uint32_t t0, t1, t2, t3, t4, c1, c2, c3, mask;
  uint64_t f0, f1, f2;

  t0 = ctx->h0;
  t1 = ctx->h1;
  t2 = ctx->h2;
  t3 = ctx->h3;
  t4 = ctx->h4;

  /* Compute resulting carries when adding 5. */
  c1 = (t0 >= 0xfffffffb);
  c2 = (t1 + c1 < c1);
  c3 = (t2 + c2 < t2);
  t4 += (t3 + c3 < t3);

  /* Set if H >= 2^130 - 5 */
  mask = - (t4 >> 2);

  t0 += mask & 5;
  t1 += mask & c1;
  t2 += mask & c2;
  t3 += mask & c3;

  /* FIXME: Take advantage of s being aligned as an unsigned long. */
  f0 = (uint64_t) t0 + LE_READ_UINT32(s->b);
  f1 = t1 + (f0 >> 32) + LE_READ_UINT32(s->b+4);
  f2 = t2 + (f1 >> 32) + LE_READ_UINT32(s->b+8);
  t3 += (f2 >> 32) + LE_READ_UINT32(s->b+12);

  LE_WRITE_UINT32(s->b, f0);
  LE_WRITE_UINT32(s->b+4, f1);
  LE_WRITE_UINT32(s->b+8, f2);
  LE_WRITE_UINT32(s->b+12, t3);

  ctx->h0 = 0;
  ctx->h1 = 0;
  ctx->h2 = 0;
  ctx->h3 = 0;
  ctx->h4 = 0;
}
#endif
-- 
Niels Möller. PGP key CB4962D070D77D7FCB8BA36271D8F1FF368C6677.
Internet email is subject to wholesale government surveillance.
_______________________________________________
nettle-bugs mailing list
[email protected]
http://lists.lysator.liu.se/mailman/listinfo/nettle-bugs

Reply via email to