]> git.ipfire.org Git - thirdparty/nettle.git/commitdiff
Add Streamlined NTRU Prime sntrup761.
authorNiels Möller <nisse@lysator.liu.se>
Thu, 7 May 2026 17:32:03 +0000 (19:32 +0200)
committerNiels Möller <nisse@lysator.liu.se>
Thu, 7 May 2026 17:32:57 +0000 (19:32 +0200)
ChangeLog
Makefile.in
sntrup-internal.h [new file with mode: 0644]
sntrup.h [new file with mode: 0644]
sntrup761-decap.c [new file with mode: 0644]
sntrup761-encap.c [new file with mode: 0644]
sntrup761-keygen.c [new file with mode: 0644]
sntrup761.c [new file with mode: 0644]
sntrupdata.c [new file with mode: 0644]

index 3750e0e7e383ce89c57db3d7baf2c5171d446cf6..e5f64f3c000bb56bd7a11673fcd385c638571dde 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2026-05-07  Niels Möller  <nisse@lysator.liu.se>
+
+       Add support for sntrup761.
+       * sntrup.h: Declare public sntrup761 functions.
+       (SNTRUP761_PRIVATE_KEY_SIZE, SNTRUP761_PUBLIC_KEY_SIZE)
+       (SNTRUP761_CIPHER_SIZE, SNTRUP_SESSION_KEY_SIZE): New constants.
+       * sntrup761-decap.c (sntrup761_decap): New function.
+       * sntrup761-encap.c (sntrup761_encap): New function.
+       * sntrup761-keygen.c (sntrup761_generate_keypair): New function.
+       * sntrup-internal.h: New header with internal declarations.
+       * sntrup761.c: New file, with shared sntrup internals.
+       * sntrupdata.c: New program, for generating sntrup encoding tables.
+       * Makefile.in (nettle_SOURCES): Add sntrup761.c,
+       sntrup761-keygen.c, sntrup761-decap.c, and sntrup761-encap.c.
+       (HEADERS): Add sntrup.h.
+       (SOURCES): Add sntrupdata.c.
+       (DISTFILES): Add sntrup-internal.h and sntrup761-encoding.h.
+       (sntrup761-encoding.h): Add make targets to generate this file by
+       compiling and running sntrupdata.c.
+
 2026-05-03  Niels Möller  <nisse@lysator.liu.se>
 
        * testsuite/testutils.c (test_random_seed, test_get_seed): New functions.
index 9399985010d3f7c1b606f2702ff85cf338dd90a6..ac7a36a52d5e941abe47b3c6ed053ccb197c7702 100644 (file)
@@ -181,7 +181,8 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt-table.c \
                 slh-shake.c slh-sha256.c \
                 slh-fors.c slh-merkle.c slh-wots.c slh-xmss.c \
                 slh-dsa.c slh-dsa-128s.c slh-dsa-128f.c \
-                slh-dsa-shake-128s.c slh-dsa-shake-128f.c slh-dsa-sha2-128s.c slh-dsa-sha2-128f.c
+                slh-dsa-shake-128s.c slh-dsa-shake-128f.c slh-dsa-sha2-128s.c slh-dsa-sha2-128f.c \
+                sntrup761.c sntrup761-keygen.c sntrup761-decap.c sntrup761-encap.c
 
 hogweed_SOURCES = sexp.c sexp-format.c \
                  sexp-transport.c sexp-transport-format.c \
@@ -259,14 +260,14 @@ HEADERS = aes.h arcfour.h arctwo.h asn1.h blowfish.h balloon.h \
          salsa20.h sexp.h serpent.h \
          sha1.h sha2.h sha3.h slh-dsa.h sm3.h sm4.h streebog.h twofish.h \
          umac.h yarrow.h xts.h poly1305.h nist-keywrap.h \
-         drbg-ctr.h
+         drbg-ctr.h sntrup.h
 
 INSTALL_HEADERS = $(HEADERS) version.h @IF_MINI_GMP@ mini-gmp.h
 
 SOURCES = $(nettle_SOURCES) $(hogweed_SOURCES) \
          $(getopt_SOURCES) $(internal_SOURCES) \
          $(OPT_SOURCES) \
-         aesdata.c desdata.c twofishdata.c shadata.c eccdata.c
+         aesdata.c desdata.c twofishdata.c shadata.c eccdata.c sntrupdata.c
 
 # NOTE: This list must include all source files, with no duplicates,
 # independently of which source files are included in the build.
@@ -290,7 +291,7 @@ DISTFILES = $(SOURCES) $(HEADERS) getopt.h \
        ctr-internal.h chacha-internal.h hmac-internal.h sha3-internal.h \
        salsa20-internal.h umac-internal.h hogweed-internal.h \
        rsa-internal.h pkcs1-internal.h dsa-internal.h eddsa-internal.h \
-       slh-dsa-internal.h \
+       slh-dsa-internal.h sntrup-internal.h sntrup761-encoding.h \
        gmp-glue.h ecc-internal.h fat-setup.h oaep.h \
        mini-gmp.h asm.m4 m4-utils.m4 \
        nettle.texinfo nettle.info nettle.html nettle.pdf sha-example.c
@@ -452,6 +453,12 @@ ecc-secp256r1.$(OBJEXT): ecc-secp256r1.h
 ecc-secp384r1.$(OBJEXT): ecc-secp384r1.h
 ecc-secp521r1.$(OBJEXT): ecc-secp521r1.h
 
+sntrup761-encoding.h: sntrupdata.c
+       $(MAKE) sntrupdata$(EXEEXT_FOR_BUILD)
+       ./sntrupdata$(EXEEXT_FOR_BUILD) > $(srcdir)/$(@F)T && mv $(srcdir)/$(@F)T $(srcdir)/$(@F)
+
+sntrup761.$(OBJEXT): sntrup761-encoding.h
+
 # Texinfo rules
 %.info: %.texinfo
        cd $(srcdir) && $(MAKEINFO) --output $@ `basename "$<"`
diff --git a/sntrup-internal.h b/sntrup-internal.h
new file mode 100644 (file)
index 0000000..f44e075
--- /dev/null
@@ -0,0 +1,170 @@
+/* sntrup-internal.h
+
+   Copyright (C) 2023 Simon Josefsson
+   Copyright (C) 2026 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/.
+*/
+
+/*
+ * Derived from public domain source, written by (in alphabetical order):
+ * - Daniel J. Bernstein
+ * - Chitchanok Chuengsatiansup
+ * - Tanja Lange
+ * - Christine van Vredendaal
+ */
+
+#ifndef NETTLE_SNTRUP_INTERNAL_H
+#define NETTLE_SNTRUP_INTERNAL_H
+
+/* Name mangling */
+#define _sntrup_hash_prefix _nettle_sntrup_hash_prefix
+#define _sntrup_hash_session _nettle_sntrup_hash_session
+#define _sntrup_hash_confirm _nettle_sntrup_hash_confirm
+#define _sntrup_urandom32 _nettle_sntrup_urandom32
+#define _sntrup_encode _nettle_sntrup_encode
+#define _sntrup_decode _nettle_sntrup_decode
+#define _sntrup_mod_3 _nettle_sntrup_mod_3
+#define _sntrup761_mod_q _nettle_sntrup761_mod_q
+#define _sntrup761_short_random _nettle__sntrup761_short_random
+#define _sntrup761_small_encode _nettle_sntrup761_small_encode
+#define _sntrup761_Rq_mult_small _nettle_sntrup761_Rq_mult_small
+#define _sntrup761_encap_internal _nettle_sntrup761_encap_internal
+#define _sntrup761_encoding_Rq _nettle_sntrup761_encoding_Rq
+#define _sntrup761_encoding_rounded _nettle_sntrup761_encoding_rounded
+
+/* Defines the coefficient field Z/q, with q = 1 (mod 6). */
+#define SNTRUP761_Q 4591
+/* Elements canonicalized to range |x| <= (q-1)/2 */
+#define SNTRUP761_Q12 ((SNTRUP761_Q-1)/2)
+
+/* Defines polynomial x^p - x - 1, irreducible over Z/q. */
+#define SNTRUP761_P 761
+
+/* Target polynomial weight. */
+#define SNTRUP761_W 286
+
+#define SNTRUP_HASH_SIZE 32
+
+/* Octet size of an encoded R3 polynomial, 191. */
+#define SNTRUP761_R3_SIZE ((SNTRUP761_P+3)/4)
+
+/* Octet size of an encoded rounded polynomial, 1007. */
+#define SNTRUP761_ROUNDED_SIZE (SNTRUP761_CIPHER_SIZE - SNTRUP_HASH_SIZE)
+
+#if WITH_EXTRA_ASSERTS
+# define assert_maybe(x) assert(x)
+#else
+# define assert_maybe(x) ((void)(x))
+#endif
+
+/* Return -1 if high bit set, otherwise zero. */
+static inline int
+uint16_highbit_mask (uint16_t x)
+{
+  return -(x >> 15);
+}
+
+/* Return -1 if non-zero, otherwise 0. */
+static inline int
+uint16_nonzero_mask (uint16_t x)
+{
+  return uint16_highbit_mask (x | -x);
+}
+
+void
+_sntrup_hash_prefix (uint8_t *out, uint8_t b, size_t size, const uint8_t *in);
+
+/* k = HashSession(b,y,z) */
+void
+_sntrup_hash_session (uint8_t *k, uint8_t b,
+                     const uint8_t y[SNTRUP761_R3_SIZE],
+                     const uint8_t z[SNTRUP761_CIPHER_SIZE]);
+
+/* h = HashConfirm(r,pk,cache); cache is Hash4(pk) */
+void
+_sntrup_hash_confirm (uint8_t *h, const uint8_t r[SNTRUP761_R3_SIZE],
+                     const uint8_t cache[SNTRUP_HASH_SIZE]);
+
+uint32_t
+_sntrup_urandom32 (void *random_ctx, nettle_random_func * random);
+
+struct sntrup_encoding_step
+{
+  size_t len;
+  uint16_t M0;
+  uint16_t M1;
+  uint32_t M0_inv;
+  uint32_t M1_inv;
+  unsigned char M0_count;
+  unsigned char M1_count;
+};
+
+#define SNTRUP761_ENCODING_STEPS 10
+
+extern struct sntrup_encoding_step
+_sntrup761_encoding_Rq[SNTRUP761_ENCODING_STEPS];
+extern struct sntrup_encoding_step
+_sntrup761_encoding_rounded[SNTRUP761_ENCODING_STEPS];
+
+void
+_sntrup_encode (const struct sntrup_encoding_step *step, uint8_t *out, uint16_t * R);
+
+void
+_sntrup_decode (unsigned n, const struct sntrup_encoding_step *step,
+               uint16_t *R, const uint8_t *S /* Must point at *end* of input. */);
+
+int8_t
+_sntrup_mod_3 (int16_t x);
+
+int16_t
+_sntrup761_mod_q (int32_t x);
+
+/* Polynomial typedefs, passed by reference. */
+/* Coefficients mod 3, canonically represented as -1, 0, 1. But this
+   type may is also used with values outside of this range during
+   decapsulation. */
+typedef int8_t sntrup761_R3_t[SNTRUP761_P];
+
+/* Coefficients mod q, represented as -(q-1)/2, ... , (q-1)/2. */
+typedef int16_t sntrup761_Rq_t[SNTRUP761_P];
+
+void
+_sntrup761_short_random (sntrup761_R3_t out, void *random_ctx, nettle_random_func * random);
+
+void
+_sntrup761_small_encode (uint8_t *s, const sntrup761_R3_t f);
+
+void
+_sntrup761_Rq_mult_small (sntrup761_Rq_t h, const sntrup761_Rq_t f, const sntrup761_R3_t g);
+
+void
+_sntrup761_encap_internal (const uint8_t *pk, const uint8_t *cache,
+                          uint8_t *r_enc, uint8_t *c,
+                          const sntrup761_R3_t r);
+
+#endif /* NETTLE_SNTRUP_INTERNAL_H */
diff --git a/sntrup.h b/sntrup.h
new file mode 100644 (file)
index 0000000..7fa9434
--- /dev/null
+++ b/sntrup.h
@@ -0,0 +1,76 @@
+/* sntrup.h
+
+   Copyright (C) 2023 Simon Josefsson
+   Copyright (C) 2026 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/.
+*/
+
+/*
+ * Derived from public domain source, written by (in alphabetical order):
+ * - Daniel J. Bernstein
+ * - Chitchanok Chuengsatiansup
+ * - Tanja Lange
+ * - Christine van Vredendaal
+ */
+
+#ifndef NETTLE_SNTRUP_H
+#define NETTLE_SNTRUP_H
+
+#include "nettle-types.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Name mangling */
+#define sntrup761_generate_keypair nettle_sntrup761_generate_keypair
+#define sntrup761_encap nettle_sntrup761_encap
+#define sntrup761_decap nettle_sntrup761_decap
+
+#define SNTRUP761_PRIVATE_KEY_SIZE 1763
+#define SNTRUP761_PUBLIC_KEY_SIZE 1158
+#define SNTRUP761_CIPHER_SIZE 1039
+#define SNTRUP_SESSION_KEY_SIZE 32
+
+void
+sntrup761_generate_keypair (uint8_t *pk, uint8_t *sk, void *random_ctx,
+                           nettle_random_func *random);
+
+void
+sntrup761_encap (const uint8_t *pk, uint8_t *k, uint8_t *c,
+                void *random_ctx, nettle_random_func *random);
+
+void
+sntrup761_decap (const uint8_t *sk, uint8_t *k, const uint8_t *c);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NETTLE_SNTRUP_H */
diff --git a/sntrup761-decap.c b/sntrup761-decap.c
new file mode 100644 (file)
index 0000000..0fa657a
--- /dev/null
@@ -0,0 +1,195 @@
+/* sntrup761-decap.c
+
+   Copyright (C) 2023 Simon Josefsson
+   Copyright (C) 2026 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/.
+*/
+
+/*
+ * Derived from public domain source, written by (in alphabetical order):
+ * - Daniel J. Bernstein
+ * - Chitchanok Chuengsatiansup
+ * - Tanja Lange
+ * - Christine van Vredendaal
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sntrup.h"
+#include "sntrup-internal.h"
+
+#include "memops.h"
+
+/* 0 if weight(r) == w, else -1 */
+static int
+Weightw_mask (sntrup761_R3_t r)
+{
+  int weight = 0;
+  int i;
+
+  for (i = 0; i < SNTRUP761_P; i++)
+    weight += r[i] & 1;
+  return uint16_nonzero_mask (weight - SNTRUP761_W);
+}
+
+/* R3_fromR(R_fromRq(r)) */
+static void
+R3_fromRq (sntrup761_R3_t out, const sntrup761_Rq_t r)
+{
+  int i;
+  for (i = 0; i < SNTRUP761_P; i++)
+    out[i] = _sntrup_mod_3 (r[i]);
+}
+
+/* h = f*g in the ring R3. */
+static void
+R3_mult (sntrup761_R3_t h, const sntrup761_R3_t f, const sntrup761_R3_t g)
+{
+  int16_t fg[SNTRUP761_P + SNTRUP761_P - 1];
+  int i, j;
+
+  for (i = 0; i < SNTRUP761_P; i++)
+    {
+      int16_t result;
+      for (result = 0, j = 0; j <= i; j++)
+       result += f[j] * g[i - j];
+      fg[i] = result;
+    }
+  for (i = SNTRUP761_P; i < SNTRUP761_P + SNTRUP761_P - 1; i++)
+    {
+      int16_t result;
+      for (result = 0, j = i - SNTRUP761_P + 1; j < SNTRUP761_P; j++)
+       result += f[j] * g[i - j];
+      fg[i] = result;
+    }
+
+  for (i = SNTRUP761_P + SNTRUP761_P - 2; i >= SNTRUP761_P; --i)
+    {
+      fg[i - SNTRUP761_P] += + fg[i];
+      fg[i - SNTRUP761_P + 1] += fg[i];
+    }
+
+  for (i = 0; i < SNTRUP761_P; i++)
+    h[i] = _sntrup_mod_3 (fg[i]);
+}
+
+/* Decodes a polynomial with coefficients supposedly all being in {-1,
+   0, 1}. Invalid inputs, corresponding to the value 2, are
+   canonicalized to -1. */
+static void
+Small_decode (sntrup761_R3_t f, const uint8_t *s)
+{
+  int8_t *p;
+  int i;
+
+  for (i = 0, p = f; i < SNTRUP761_P / 4; i++)
+    {
+      uint8_t x = *s++;
+      /* All bit pairs should be one of 00, 01, 10. If invalid value
+        11 occurs, replace with 00 (reduction mod 3). */
+      uint8_t invalid = x & (x >> 1) & 0x55;
+      x &= ~(3*invalid);
+
+      *p++ = ((int8_t) (x & 3)) - 1;
+      x >>= 2;
+      *p++ = ((int8_t) (x & 3)) - 1;
+      x >>= 2;
+      *p++ = ((int8_t) (x & 3)) - 1;
+      x >>= 2;
+      *p++ = ((int8_t) (x & 3)) - 1;
+    }
+  {
+    uint8_t x = *s;
+    uint8_t valid = (x & (x >> 1) & 1) ^ 1;
+    *p = ((int8_t) (x & (3*valid))) - 1;
+  }
+}
+
+static void
+Rounded_decode (sntrup761_Rq_t r, const uint8_t *s)
+{
+  uint16_t R[SNTRUP761_P];
+  int i;
+
+  _sntrup_decode (SNTRUP761_ENCODING_STEPS, _sntrup761_encoding_rounded, R,
+                 s + SNTRUP761_ROUNDED_SIZE);
+  for (i = 0; i < SNTRUP761_P; i++)
+    r[i] = R[i] * 3 - SNTRUP761_Q12;
+}
+
+/* r = ZDecrypt(C,sk) */
+static void
+ZDecrypt (const uint8_t *sk, sntrup761_R3_t r, const uint8_t *c_enc)
+{
+  sntrup761_R3_t f, ginv;
+  sntrup761_Rq_t c;
+  int mask;
+  int i;
+
+  Small_decode (f, sk);
+  Small_decode (ginv, sk + SNTRUP761_R3_SIZE);
+  Rounded_decode (c, c_enc);
+
+  /* Premultiply by 3; the result is interpreted as a polynomial over
+     Rq, not the zero element of R3. */
+  for (i = 0; i < SNTRUP761_P; i++)
+    f[i] *= 3;
+
+  _sntrup761_Rq_mult_small (c, c, f);
+  R3_fromRq (r, c);
+  R3_mult (r, r, ginv);
+
+  mask = Weightw_mask (r);     /* 0 if weight SNTRUP761_W, else -1 */
+  for (i = 0; i < SNTRUP761_W; i++)
+    r[i] = ((r[i] ^ 1) & ~mask) ^ 1;
+  for (; i < SNTRUP761_P; i++)
+    r[i] &= ~mask;
+}
+
+/* k = Decap(c,sk) */
+void
+sntrup761_decap (const uint8_t *sk, uint8_t *k, const uint8_t *c)
+{
+  const uint8_t *pk = sk + 2*SNTRUP761_R3_SIZE;
+  const uint8_t *rho = pk + SNTRUP761_PUBLIC_KEY_SIZE;
+  const uint8_t *cache = rho + SNTRUP761_R3_SIZE;
+  sntrup761_R3_t r;
+  uint8_t r_enc[SNTRUP761_R3_SIZE];
+  uint8_t cnew[SNTRUP761_CIPHER_SIZE];
+  int mask;
+  int i;
+
+  ZDecrypt (sk, r, c);
+  _sntrup761_encap_internal (pk, cache, r_enc, cnew, r);
+  mask = memeql_sec(c, cnew, sizeof (cnew)) - 1;
+  for (i = 0; i < SNTRUP761_R3_SIZE; i++)
+    r_enc[i] ^= mask & (r_enc[i] ^ rho[i]);
+  _sntrup_hash_session (k, 1 + mask, r_enc, c);
+}
diff --git a/sntrup761-encap.c b/sntrup761-encap.c
new file mode 100644 (file)
index 0000000..064948a
--- /dev/null
@@ -0,0 +1,122 @@
+/* sntrup761-encap.c
+
+   Copyright (C) 2023 Simon Josefsson
+   Copyright (C) 2026 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/.
+*/
+
+/*
+ * Derived from public domain source, written by (in alphabetical order):
+ * - Daniel J. Bernstein
+ * - Chitchanok Chuengsatiansup
+ * - Tanja Lange
+ * - Christine van Vredendaal
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sntrup.h"
+#include "sntrup-internal.h"
+
+static void
+Rq_decode (sntrup761_Rq_t r, const uint8_t *s)
+{
+  uint16_t R[SNTRUP761_P];
+  int i;
+
+  _sntrup_decode (SNTRUP761_ENCODING_STEPS,_sntrup761_encoding_Rq, R,
+                 s + SNTRUP761_PUBLIC_KEY_SIZE);
+  for (i = 0; i < SNTRUP761_P; i++)
+    r[i] = ((int16_t) R[i]) - SNTRUP761_Q12;
+}
+
+static uint16_t
+div_3 (uint16_t x)
+{
+  uint16_t q, r;
+  /* Magic constant is ceil (2^16 / 3). */
+  q = ((uint32_t) 21846 * x) >> 16;
+  r = x - 3 * q;
+  return q - (r >> 15);
+}
+
+/* Short-circuit rounding followed by exact division by 3. Rounding
+   means computing
+
+     u = x + (q-1)/2 + 1 (note (q-1)/2 = 0 (mod 3))
+     a = floor (u/3)
+     r = u - 3*a - 1
+     x' = x - r
+
+   For encoding this rounded value, we compute
+
+     (x' + (q-1)/2)3,
+
+   but this is the same as the quotient a, so let us just compute
+   that.
+*/
+static void
+Round_and_encode (uint8_t *s, const sntrup761_Rq_t r)
+{
+  uint16_t R[SNTRUP761_P];
+  int i;
+
+  for (i = 0; i < SNTRUP761_P; i++)
+    R[i] = div_3 (r[i] + SNTRUP761_Q12 + 1);
+  _sntrup_encode (_sntrup761_encoding_rounded, s, R);
+}
+
+/* cache is Hash4(pk) */
+void
+_sntrup761_encap_internal (const uint8_t *pk, const uint8_t *cache,
+                          uint8_t *r_enc, uint8_t *c,
+                          const sntrup761_R3_t r)
+{
+  sntrup761_Rq_t t;
+  _sntrup761_small_encode (r_enc, r);
+  Rq_decode (t, pk);
+  _sntrup761_Rq_mult_small (t, t, r);
+  Round_and_encode (c, t);
+  _sntrup_hash_confirm (c + SNTRUP761_ROUNDED_SIZE, r_enc, cache);
+}
+
+void
+sntrup761_encap (const uint8_t *pk, uint8_t *k, uint8_t *c,
+                void *random_ctx, nettle_random_func * random)
+{
+  sntrup761_R3_t r;
+  uint8_t r_enc[SNTRUP761_R3_SIZE];
+  uint8_t cache[SNTRUP_HASH_SIZE];
+
+  _sntrup_hash_prefix (cache, 4, SNTRUP761_PUBLIC_KEY_SIZE, pk);
+  _sntrup761_short_random (r, random_ctx, random);
+  _sntrup761_encap_internal (pk, cache, r_enc, c, r);
+  _sntrup_hash_session (k, 1, r_enc, c);
+}
diff --git a/sntrup761-keygen.c b/sntrup761-keygen.c
new file mode 100644 (file)
index 0000000..00e0990
--- /dev/null
@@ -0,0 +1,295 @@
+/* sntrup761-keygen.c
+
+   Copyright (C) 2023 Simon Josefsson
+   Copyright (C) 2026 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/.
+*/
+
+/*
+ * Derived from public domain source, written by (in alphabetical order):
+ * - Daniel J. Bernstein
+ * - Chitchanok Chuengsatiansup
+ * - Tanja Lange
+ * - Christine van Vredendaal
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+#include "sntrup.h"
+#include "sntrup-internal.h"
+
+static void
+R3_random (sntrup761_R3_t out, void *random_ctx, nettle_random_func * random)
+{
+  int i;
+
+  for (i = 0; i < SNTRUP761_P; ++i)
+    out[i] = (((_sntrup_urandom32 (random_ctx, random) & 0x3fffffff) * 3) >> 30) - 1;
+}
+
+/* Returns non-canonical representation, 0 <= r < 2q */
+static uint16_t
+mod_q_appr (uint32_t u)
+{
+  uint32_t a, r, p;
+  /* Magic constant is ceil (2^32 / q) */
+  a = ((uint64_t) 935519 * u) >> 32;
+  p = a * SNTRUP761_Q;
+  r = SNTRUP761_Q + u - p;
+  assert_maybe (r < 2*SNTRUP761_Q);
+  return r;
+}
+
+static int16_t
+Fq_recip (int16_t a)
+{
+  int16_t x;
+  uint16_t ua, ux;
+  ua = a + SNTRUP761_Q;
+  ux = mod_q_appr ((uint32_t) ua * ua); /* a^2 */
+  ux = mod_q_appr ((uint32_t) ux * ux); /* a^4 */
+  ux = mod_q_appr ((uint32_t) ux * ux); /* a^8 */
+  ux = mod_q_appr ((uint32_t) ux * ux); /* a^16 */
+  ux = mod_q_appr ((uint32_t) ux * ua); /* a^17 */
+  ux = mod_q_appr ((uint32_t) ux * ux); /* a^34 */
+  ux = mod_q_appr ((uint32_t) ux * ua); /* a^35 */
+  ux = mod_q_appr ((uint32_t) ux * ux); /* a^70 */
+  ux = mod_q_appr ((uint32_t) ux * ua); /* a^71 */
+  ux = mod_q_appr ((uint32_t) ux * ux); /* a^142 */
+  ux = mod_q_appr ((uint32_t) ux * ua); /* a^143 */
+  ux = mod_q_appr ((uint32_t) ux * ux); /* a^286 */
+  ux = mod_q_appr ((uint32_t) ux * ux); /* a^572 */
+  ux = mod_q_appr ((uint32_t) ux * ua); /* a^573 */
+  ux = mod_q_appr ((uint32_t) ux * ux); /* a^1146 */
+  ux = mod_q_appr ((uint32_t) ux * ua); /* a^1147 */
+  ux = mod_q_appr ((uint32_t) ux * ux); /* a^2294 */
+  ux = mod_q_appr ((uint32_t) ux * ux); /* a^4588 */
+  x = _sntrup761_mod_q ((int32_t) ux * a); /* a^4589 = a^(q-2) */
+  assert_maybe (_sntrup761_mod_q ((int32_t) x * a) == 1);
+  return x;
+}
+
+/* returns 1 if recip succeeded; else 0 */
+static int
+R3_recip (sntrup761_R3_t out, const sntrup761_R3_t in)
+{
+  /* Working polynomials have non-canonical coefficients. After 8
+     iterations, coefficients may grow up to size fibonacci (11) ==
+     89, which still fit in int8_t. */
+  int8_t f[SNTRUP761_P + 1], g[SNTRUP761_P + 1], v[SNTRUP761_P + 1], r[SNTRUP761_P + 1];
+  int i, loop, delta;
+  int sign, swap, t;
+
+  v[0] = 0;
+  r[0] = 1;
+  for (i = 1; i < SNTRUP761_P + 1; ++i)
+    v[i] = r[i] = 0;
+
+  f[0] = 1;
+  for (i = 1; i < SNTRUP761_P - 1; ++i)
+    f[i] = 0;
+  f[SNTRUP761_P - 1] = f[SNTRUP761_P] = -1;
+
+  for (i = 0; i < SNTRUP761_P; ++i)
+    g[SNTRUP761_P - 1 - i] = in[i];
+  g[SNTRUP761_P] = 0;
+
+  delta = 1;
+
+  for (loop = 0; loop < 2 * SNTRUP761_P - 1; ++loop)
+    {
+      for (i = SNTRUP761_P; i > 0; --i)
+       v[i] = v[i - 1];
+      v[0] = 0;
+
+      /* Always canonicalize the values controlling the iteration
+        update. */
+      g[0] = _sntrup_mod_3 (g[0]);
+      f[0] = _sntrup_mod_3 (f[0]);
+
+      if (loop && !(loop & 7))
+       {
+         /* Re-canonicalize everything. */
+         r[0] = _sntrup_mod_3 (r[0]);
+         for (i = 1; i < SNTRUP761_P + 1; ++i)
+           {
+             g[i] = _sntrup_mod_3 (g[i]);
+             f[i] = _sntrup_mod_3 (f[i]);
+             v[i] = _sntrup_mod_3 (v[i]);
+             r[i] = _sntrup_mod_3 (r[i]);
+           }
+       }
+      sign = -g[0] * f[0];
+      swap = uint16_highbit_mask (-delta) & uint16_nonzero_mask (g[0]);
+      delta ^= swap & (delta ^ -delta);
+      delta += 1;
+
+      for (i = 0; i < SNTRUP761_P + 1; ++i)
+       {
+         t = swap & (f[i] ^ g[i]);
+         f[i] ^= t;
+         g[i] ^= t;
+         t = swap & (v[i] ^ r[i]);
+         v[i] ^= t;
+         r[i] ^= t;
+       }
+
+      for (i = 0; i < SNTRUP761_P; ++i)
+       g[i] = g[i+1] + sign * f[i+1];
+      g[SNTRUP761_P] = 0;
+
+      for (i = 0; i < SNTRUP761_P + 1; ++i)
+       r[i] += sign * v[i];
+    }
+
+  sign = _sntrup_mod_3 (f[0]);
+  for (i = 0; i < SNTRUP761_P; ++i)
+    out[i] = _sntrup_mod_3 (sign * v[SNTRUP761_P - 1 - i]);
+
+  return delta == 0;
+}
+
+/* out = 1/(3*in) in Rq */
+static void
+Rq_recip3 (sntrup761_Rq_t out, const sntrup761_R3_t in)
+{
+  int16_t f[SNTRUP761_P + 1], g[SNTRUP761_P + 1], v[SNTRUP761_P + 1], r[SNTRUP761_P + 1];
+  int i, loop, delta;
+  int swap, t;
+  int32_t f0, g0;
+  int16_t scale;
+
+  v[0] = 0;
+  r[0] = -1530; /* 3^-1 (mod q) */
+  for (i = 1; i < SNTRUP761_P + 1; ++i)
+    v[i] = r[i] = 0;
+
+  f[0] = 1;
+  for (i = 1; i < SNTRUP761_P - 1; ++i)
+    f[i] = 0;
+  f[SNTRUP761_P - 1] = f[SNTRUP761_P] = -1;
+
+  for (i = 0; i < SNTRUP761_P; ++i)
+    g[SNTRUP761_P - 1 - i] = in[i];
+  g[SNTRUP761_P] = 0;
+
+  delta = 1;
+
+  for (loop = 0; loop < 2 * SNTRUP761_P - 1; ++loop)
+    {
+      for (i = SNTRUP761_P; i > 0; --i)
+       v[i] = v[i - 1];
+      v[0] = 0;
+
+      swap = uint16_highbit_mask (-delta) & uint16_nonzero_mask (g[0]);
+      delta ^= swap & (delta ^ -delta);
+      delta += 1;
+
+      for (i = 0; i < SNTRUP761_P + 1; ++i)
+       {
+         t = swap & (f[i] ^ g[i]);
+         f[i] ^= t;
+         g[i] ^= t;
+         t = swap & (v[i] ^ r[i]);
+         v[i] ^= t;
+         r[i] ^= t;
+       }
+
+      f0 = f[0];
+      g0 = g[0];
+      for (i = 0; i < SNTRUP761_P; ++i)
+       g[i] = _sntrup761_mod_q (f0 * g[i+1] - g0 * f[i+1]);
+      g[SNTRUP761_P] = 0;
+
+      for (i = 0; i < SNTRUP761_P + 1; ++i)
+       r[i] = _sntrup761_mod_q (f0 * r[i] - g0 * v[i]);
+    }
+
+  scale = Fq_recip (f[0]);
+  for (i = 0; i < SNTRUP761_P; ++i)
+    out[i] = _sntrup761_mod_q (scale * (int32_t) v[SNTRUP761_P - 1 - i]);
+
+  /* Since R/q is a field and input is always non-zero. */
+  assert_maybe (delta == 0);
+}
+
+static void
+Rq_encode (uint8_t *s, const sntrup761_Rq_t r)
+{
+  uint16_t R[SNTRUP761_P];
+  int i;
+
+  for (i = 0; i < SNTRUP761_P; ++i)
+    R[i] = r[i] + SNTRUP761_Q12;
+  _sntrup_encode (_sntrup761_encoding_Rq, s, R);
+}
+
+/* Returns public polynomial h, private polynomials f, ginv. */
+static void
+KeyGen (sntrup761_Rq_t h, sntrup761_R3_t f, sntrup761_R3_t ginv,
+       void *random_ctx, nettle_random_func * random)
+{
+  sntrup761_R3_t g;
+  sntrup761_Rq_t finv;
+
+  for (;;)
+    {
+      R3_random (g, random_ctx, random);
+      if (R3_recip (ginv, g))
+       break;
+    }
+  _sntrup761_short_random (f, random_ctx, random);
+  Rq_recip3 (finv, f);
+  _sntrup761_Rq_mult_small (h, finv, g);
+}
+
+void
+sntrup761_generate_keypair (uint8_t *pk, uint8_t *sk, void *random_ctx,
+                           nettle_random_func * random)
+{
+  sntrup761_Rq_t h;
+  sntrup761_R3_t f, v;
+
+  KeyGen (h, f, v, random_ctx, random);
+  Rq_encode (pk, h);
+  _sntrup761_small_encode (sk, f);
+  _sntrup761_small_encode (sk + SNTRUP761_R3_SIZE, v);
+
+  /* Auxiliary data appended to the secret key: public key, random
+     rho, and hash of the public key. */
+  memcpy (sk + 2*SNTRUP761_R3_SIZE, pk, SNTRUP761_PUBLIC_KEY_SIZE);
+  random (random_ctx, SNTRUP761_R3_SIZE,
+         sk + 2*SNTRUP761_R3_SIZE + SNTRUP761_PUBLIC_KEY_SIZE);
+  _sntrup_hash_prefix (sk + 3*SNTRUP761_R3_SIZE + SNTRUP761_PUBLIC_KEY_SIZE,
+                      4, SNTRUP761_PUBLIC_KEY_SIZE, pk);
+}
diff --git a/sntrup761.c b/sntrup761.c
new file mode 100644 (file)
index 0000000..389ff47
--- /dev/null
@@ -0,0 +1,447 @@
+/* sntrup761.c
+
+   Copyright (C) 2023 Simon Josefsson
+   Copyright (C) 2026 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/.
+*/
+
+/*
+ * Derived from public domain source, written by (in alphabetical order):
+ * - Daniel J. Bernstein
+ * - Chitchanok Chuengsatiansup
+ * - Tanja Lange
+ * - Christine van Vredendaal
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+#include "sntrup.h"
+#include "sntrup-internal.h"
+#include "sntrup761-encoding.h"
+
+#include "bswap-internal.h"
+#include "sha2.h"
+
+#define uint32_MINMAX(a,b) \
+do { \
+  uint64_t d = (uint64_t)b - (uint64_t)a; \
+  uint32_t masked_d = (d >> 32) & d; \
+  a += masked_d; \
+  b -= masked_d; \
+} while(0)
+
+/* Based on supercop-20201130/crypto_sort/int32/portable4/sort.c, but
+   using uint32_t rather than int32_t. */
+static void
+crypto_sort_uint32 (uint32_t *x, size_t n)
+{
+  size_t top, p, q, r, i, j;
+
+  if (n < 2)
+    return;
+  top = 1;
+  while (top < n - top)
+    top += top;
+
+  for (p = top; p >= 1; p >>= 1)
+    {
+      i = 0;
+      while (i + 2 * p <= n)
+       {
+         for (j = i; j < i + p; j++)
+           uint32_MINMAX (x[j], x[j + p]);
+         i += 2 * p;
+       }
+      for (j = i; j < n - p; j++)
+       uint32_MINMAX (x[j], x[j + p]);
+
+      i = 0;
+      j = 0;
+      for (q = top; q > p; q >>= 1)
+       {
+         if (j != i)
+           for (;;)
+             {
+               if (j == n - q)
+                 goto done;
+               uint32_t a = x[j + p];
+               for (r = q; r > p; r >>= 1)
+                 uint32_MINMAX (a, x[j + r]);
+               x[j + p] = a;
+               j++;
+               if (j == i + p)
+                 {
+                   i += 2 * p;
+                   break;
+                 }
+             }
+         while (i + p <= n - q)
+           {
+             for (j = i; j < i + p; j++)
+               {
+                 uint32_t a = x[j + p];
+                 for (r = q; r > p; r >>= 1)
+                   uint32_MINMAX (a, x[j + r]);
+                 x[j + p] = a;
+               }
+             i += 2 * p;
+           }
+         /* now i + p > n - q */
+         j = i;
+         while (j < n - q)
+           {
+             uint32_t a = x[j + p];
+             for (r = q; r > p; r >>= 1)
+               uint32_MINMAX (a, x[j + r]);
+             x[j + p] = a;
+             j++;
+           }
+
+       done:;
+       }
+    }
+}
+
+static uint32_t
+uint32_16_divmod (uint16_t *rp, uint32_t u, uint16_t d, uint32_t dinv)
+{
+  uint32_t q, r, p, mask;
+  q = ((uint64_t) dinv * u) >> 32;
+  p = q * d;
+  r = u - p; /* Interpreted as two's complement, |r| < d */
+  mask = - (r >> 31);
+  *rp = r + (mask & d);
+  assert_maybe (*rp < d);
+  return q + mask;
+}
+
+static uint16_t
+uint32_16_mod (uint32_t u, uint16_t d, uint32_t dinv)
+{
+  uint32_t q, r, p;
+  q = ((uint64_t) dinv * u) >> 32;
+  p = q * d;
+  r = u - p; /* Interpreted as two's complement, |r| < d */
+  r += ((r >> 16) & d);
+  assert_maybe ((uint16_t) r < d);
+  return r;
+}
+
+void
+_sntrup_decode (unsigned n, const struct sntrup_encoding_step *step,
+               uint16_t *R, const uint8_t *S /* Must point at *end* of input. */)
+{
+  step += --n;
+  assert (step->len == 2);
+  {
+    /* Decode first pair using M0, M1 */
+    uint32_t r;
+    unsigned j;
+
+    for (r = j = 0; j < step->M1_count; j++)
+      r = (r << 8) | *--S;
+
+    r = uint32_16_divmod (&R[0], r, step->M0, step->M0_inv);
+    R[1] = uint32_16_mod (r, step->M1, step->M1_inv);
+  }
+  while (n-- > 0)
+    {
+      size_t i;
+      step--;
+      i = step->len;
+      if (i & 1)
+       {
+         i--;
+         /* Copy last element */
+         R[i] = R[i / 2];
+       }
+      else
+       {
+         /* Decode a pair using M0, M1 */
+         uint32_t r;
+         unsigned j;
+         i -= 2;
+         for (j = 0, r = R[i/2]; j < step->M1_count; j++)
+           r = (r << 8) | *--S;
+         r = uint32_16_divmod (&R[i], r, step->M0, step->M0_inv);
+         R[i+1] = uint32_16_mod (r, step->M1, step->M1_inv);
+       }
+      while (i > 0)
+       {
+         /* Decode a pair using M0, M0 */
+         uint32_t r;
+         unsigned j;
+         i -= 2;
+         for (j = 0, r = R[i/2]; j < step->M0_count; j++)
+           r = (r << 8) | *--S;
+         r = uint32_16_divmod (&R[i], r, step->M0, step->M0_inv);
+         R[i+1] = uint32_16_mod (r, step->M0, step->M0_inv);
+       }
+    }
+}
+
+/* Clobbers R during encoding. */
+void
+_sntrup_encode (const struct sntrup_encoding_step *step,
+               uint8_t *out, uint16_t *R)
+{
+  for (;; step++)
+    {
+      size_t len = step->len;
+      size_t i;
+      uint32_t r;
+
+      /* Process all but the last one or two elements, based on M0, M0. */
+      for (i = 0; i < len - 2; i += 2)
+       {
+         unsigned j;
+         r = R[i] + R[i + 1] * step->M0;
+         for (j = 0; j < step->M0_count; j++, r >>= 8)
+           *out++ = r;
+         R[i / 2] = r;
+       }
+
+      r = R[i];
+      if (i == len - 2)
+       {
+         unsigned j;
+         /* Process last two elements, based on M0, M1. */
+         r += R[i + 1] * step->M0;
+         for (j = 0; j < step->M1_count; j++, r >>= 8)
+           *out++ = r;
+
+         if (i == 0)
+           break;
+       }
+      R[i / 2] = r;
+    }
+}
+
+/* ----- arithmetic mod 3 */
+
+/* Reduce input to the canonical range -1,0,1 */
+int8_t
+_sntrup_mod_3 (int16_t x)
+{
+  uint16_t ux, a, r;
+  /* x is either an canonical representative of Fq, |x| <= (q-1) / 2,
+     or result of polynomial multiplication in which case |x| <= 2p
+     which is a smaller range. */
+  assert_maybe (x <= SNTRUP761_Q12);
+  assert_maybe (x >= -SNTRUP761_Q12);
+
+  /* We want ((x + 1) mod q) - 1, but also add a multiple of 3 so we
+     can use unsigned arithmetic. And (q-1)/2 happens to be a multiple
+     of 3. */
+  ux = x + 1 + SNTRUP761_Q12;
+  /* Magic constant is ceil (2^16 / 3). */
+  a = ((uint32_t) 21846 * ux) >> 16;
+  r = ux - 3*a; /* Interpreted as two's complement, |r| < 3 */
+  r += (r >> 14);
+  assert_maybe (r < 3);
+  return (int8_t) r - 1;
+}
+
+/* ----- arithmetic mod q */
+
+/* Reduce input to the canonical range -(q-1)/2...(q-1)/2 */
+int16_t
+_sntrup761_mod_q (int32_t x)
+{
+  uint32_t ux;
+  /* When called from Rq_mult_small, inputs are ideally limited to
+     w*(q-1), but to allow overweight inputs and small coeffients
+     premultiplied by 3, we must allow inputs up to 3*p*(q-1). When
+     called from Fq_recip and Rq_recip3 inputs may be up to 2 q^2,
+     which is a larger range. */
+  assert_maybe (x < 2*SNTRUP761_Q * SNTRUP761_Q);
+  assert_maybe (x > -2*SNTRUP761_Q * SNTRUP761_Q);
+  /* We want ((x + (q-1)/2) mod q) - (q-1)/2, but also add a multiple
+     of q so we can use unsigned arithmetic. */
+  ux = x + SNTRUP761_Q12 + 2*SNTRUP761_Q * SNTRUP761_Q;
+  /* Magic constant is ceil (2^32 / q) */
+  return (int32_t) uint32_16_mod (ux, SNTRUP761_Q, 935519) - SNTRUP761_Q12;
+}
+
+
+/* ----- polynomials mod q */
+
+/* h = f*g in the ring Rq. Tolerates g coeffients outside of the proper
+   range, up to absolute value 3. */
+void
+_sntrup761_Rq_mult_small (sntrup761_Rq_t h, const sntrup761_Rq_t f, const sntrup761_R3_t g)
+{
+  int32_t fg[SNTRUP761_P + SNTRUP761_P - 1];
+  int i, j;
+
+  for (i = 0; i < SNTRUP761_P; i++)
+    {
+      int32_t result;
+      for (result = 0, j = 0; j <= i; j++)
+       result +=  f[j] * (int32_t) g[i - j];
+      fg[i] = result;
+    }
+  for (i = SNTRUP761_P; i < SNTRUP761_P + SNTRUP761_P - 1; i++)
+    {
+      int32_t result;
+      for (result = 0, j = i - SNTRUP761_P + 1; j < SNTRUP761_P; j++)
+       result += f[j] * (int32_t) g[i - j];
+      fg[i] = result;
+    }
+
+  for (i = SNTRUP761_P + SNTRUP761_P - 2; i >= SNTRUP761_P; --i)
+    {
+      fg[i - SNTRUP761_P] += fg[i];
+      fg[i - SNTRUP761_P + 1] += fg[i];
+    }
+
+  for (i = 0; i < SNTRUP761_P; i++)
+    /* Coeffients to be reduced are bounded by
+       2*3*p*(q-1)/2 = 10478970 < q*q. */
+    h[i] = _sntrup761_mod_q (fg[i]);
+}
+
+/* ----- underlying hash function */
+
+/* e.g., b = 0 means out = Hash0(in) */
+static void
+hash_init (struct sha512_ctx *ctx, uint8_t b)
+{
+  sha512_init (ctx);
+  sha512_update (ctx, 1, &b);
+}
+
+#define hash_update sha512_update
+
+static void
+hash_digest (struct sha512_ctx *ctx, uint8_t *digest)
+{
+  uint8_t h[SHA512_DIGEST_SIZE];
+  sha512_digest (ctx, h);
+  memcpy (digest, h, SNTRUP_HASH_SIZE);
+}
+
+void
+_sntrup_hash_prefix (uint8_t *out, uint8_t b, size_t size, const uint8_t *in)
+{
+  struct sha512_ctx ctx;
+  hash_init (&ctx, b);
+  hash_update (&ctx, size, in);
+  hash_digest (&ctx, out);
+}
+
+/* ----- session-key hash */
+
+void
+_sntrup_hash_session (uint8_t *k, uint8_t b,
+                     const uint8_t y[SNTRUP761_R3_SIZE],
+                     const uint8_t z[SNTRUP761_CIPHER_SIZE])
+{
+  struct sha512_ctx ctx;
+  uint8_t x[SNTRUP_HASH_SIZE];
+
+  _sntrup_hash_prefix (x, 3, SNTRUP761_R3_SIZE, y);
+  hash_init (&ctx, b);
+  hash_update (&ctx, sizeof (x), x);
+  hash_update (&ctx, SNTRUP761_CIPHER_SIZE, z);
+  hash_digest (&ctx, k);
+}
+
+void
+_sntrup_hash_confirm (uint8_t *h, const uint8_t r[SNTRUP761_R3_SIZE],
+                     const uint8_t cache[SNTRUP_HASH_SIZE])
+{
+  struct sha512_ctx ctx;
+  uint8_t x[SNTRUP_HASH_SIZE];
+
+  _sntrup_hash_prefix (x, 3, SNTRUP761_R3_SIZE, r);
+  hash_init (&ctx, 2);
+  hash_update (&ctx, sizeof (x), x);
+  hash_update (&ctx, SNTRUP_HASH_SIZE, cache);
+  hash_digest (&ctx, h);
+}
+
+/* ----- randomness */
+uint32_t
+_sntrup_urandom32 (void *random_ctx, nettle_random_func * random)
+{
+  union {
+    uint8_t c[4];
+    uint32_t i;
+  } u;
+  random (random_ctx, 4, u.c);
+  return bswap32_if_be (u.i);
+}
+
+/* ----- sorting to generate short polynomial */
+void
+_sntrup761_short_random (sntrup761_R3_t out, void *random_ctx, nettle_random_func * random)
+{
+  uint32_t L[SNTRUP761_P];
+  int i;
+
+  for (i = 0; i < SNTRUP761_W; i++)
+    /* Low two bits set randomly to 00 or 10. */
+    L[i] = _sntrup_urandom32 (random_ctx, random) & ~(uint32_t) 1;
+  for (; i < SNTRUP761_P; i++)
+    /* Low two bits set to 01. */
+    L[i] = ((_sntrup_urandom32 (random_ctx, random) & ~(uint32_t) 3)) | 1;
+
+  crypto_sort_uint32 (L, SNTRUP761_P);
+
+  for (i = 0; i < SNTRUP761_P; i++)
+    {
+      assert_maybe (i == 0 || L[i-1] <= L[i]);
+      out[i] = (L[i] & 3) - 1;
+    }
+}
+
+/* ----- encoding small polynomials (including short polynomials) */
+void
+_sntrup761_small_encode (uint8_t *s, const sntrup761_R3_t f)
+{
+  const int8_t *p;
+  int i;
+
+  /* Relies on SNTRUP761_P mod 4 = 1 */
+  for (i = 0, p = f; i < SNTRUP761_P / 4; i++)
+    {
+      int8_t x;
+      x = *p++ + 1;
+      x += (*p++ + 1) << 2;
+      x += (*p++ + 1) << 4;
+      x += (*p++ + 1) << 6;
+      *s++ = x;
+    }
+  *s = *p + 1;
+}
diff --git a/sntrupdata.c b/sntrupdata.c
new file mode 100644 (file)
index 0000000..dc05b8f
--- /dev/null
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <stdint.h>
+
+static uint32_t
+invert (unsigned x)
+{
+  /* Return ceil(2^32 / x) = floor ((2^32 + x-1) / x) = 1 + floor
+     ((2^32-1) / x. */
+  return 1 + (uint32_t) -1 / x;
+}
+
+static void
+output_encoding (const char *name, unsigned n, unsigned m)
+{
+  uint32_t M0, M1;
+  unsigned c;
+
+  printf ("struct sntrup_encoding_step\n%s[SNTRUP761_ENCODING_STEPS] =\n{\n", name);
+  for (M0 = M1 = m; n > 1; n = (n+1)/2)
+    {
+      unsigned c0, c1;
+      printf ("  { %u, %u, %u, %u, %u, ", n, M0, M1, invert(M0), invert(M1));
+      c1 = 0;
+      if (!(n & 1))
+       {
+         for (M1 *= M0; M1 >= 16384; M1 = (M1 + 255) >> 8)
+           c1++;
+         if (n == 2)
+           /* Add count for final element. */
+           for (; M1 > 1; M1 >>= 8)
+             c1++;
+       }
+      for (c0 = 0, M0 *= M0; M0 >= 16384; M0 = (M0 + 255) >> 8)
+       c0++;
+      printf (" %u, %u },\n", c0, c1);
+    }
+  printf ("};\n");
+}
+
+int
+main (void)
+{
+  unsigned Q = 4591;
+  unsigned P = 761;
+  output_encoding ("_sntrup761_encoding_Rq", P, Q);
+  output_encoding ("_sntrup761_encoding_rounded", P, (Q+2)/3);
+}