+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.
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 \
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.
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
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 "$<"`
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+#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);
+}