[PATCH 04/10] tests: add sntrup761 internal arithmetic regression test
Jussi Kivilinna <[email protected]> Sun, 2 Aug 2026 12:55:09 +0300
| Newsgroups | gmane.comp.encryption.gpg.libgcrypt.devel |
|---|---|
| Message-ID | <[email protected]> |
* tests/Makefile.am (tests_bin): Add 't-sntrup761'. (t_sntrup761_CPPFLAGS): New. * tests/t-sntrup761.c: New. -- Polynomial multiplication and modular reduction helpers of SNTRUP761 are file-static, so test includes implementation directly, same way as bench-slope reaches cipher internals. Outputs are compared against reference reduction written with plain C operators. Input patterns maximize intermediate accumulators instead of sampling randomly, as that is what deferred reduction depends on. Signed-off-by: Jussi Kivilinna <[email protected]> --- tests/Makefile.am | 3 +- tests/t-sntrup761.c | 407 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 409 insertions(+), 1 deletion(-) create mode 100644 tests/t-sntrup761.c diff --git a/tests/Makefile.am b/tests/Makefile.am index 8fcde4fc..8c468b4d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -26,7 +26,7 @@ tests_bin = \ t-mpi-bit t-mpi-point t-lock \ prime basic keygen pubkey hmac hashtest t-kdf keygrip \ aeswrap random t-kem t-thread-local t-fips-service-ind \ - t-cipher-internal + t-cipher-internal t-sntrup761 if USE_RSA tests_bin += pkcs1v2 t-rsa-pss t-rsa-15 t-rsa-testparm @@ -106,6 +106,7 @@ t_thread_local_LDADD = $(standard_ldadd) $(GPG_ERROR_MT_LIBS) @LDADD_FOR_TESTS_K t_thread_local_CFLAGS = $(GPG_ERROR_MT_CFLAGS) testdrv_LDADD = $(LDADD_FOR_TESTS_KLUDGE) t_cipher_internal_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/cipher +t_sntrup761_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/cipher # Build a version of the test driver for the build platform. testdrv-build: testdrv.c diff --git a/tests/t-sntrup761.c b/tests/t-sntrup761.c new file mode 100644 index 00000000..7d8b2c80 --- /dev/null +++ b/tests/t-sntrup761.c @@ -0,0 +1,407 @@ +/* t-sntrup761.c - SNTRUP761 internal arithmetic regression tests + * Copyright (C) 2026 Jussi Kivilinna <[email protected]> + * + * This file is part of Libgcrypt. + * + * Libgcrypt is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Reference functions derived from public domain source, written + * by (in alphabetical order): + * - Daniel J. Bernstein + * - Chitchanok Chuengsatiansup + * - Tanja Lange + * - Christine van Vredendaal + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* Stub out external dependencies so that including implementation does + not require linking them. */ +#define _gcry_md_hash_buffer t_sntrup761_md_hash_buffer +#define _gcry_ct_not_memequal t_sntrup761_ct_not_memequal +#define _gcry_ct_memmov_cond t_sntrup761_ct_memmov_cond + +static void +t_sntrup761_md_hash_buffer (int algo, void *digest, const void *buf, + size_t len) +{ + (void)algo; + (void)digest; + (void)buf; + (void)len; +} + +static unsigned int +t_sntrup761_ct_not_memequal (const void *b1, const void *b2, size_t len) +{ + (void)b1; + (void)b2; + (void)len; + return 0; +} + +static void +t_sntrup761_ct_memmov_cond (void *dst, const void *src, size_t len, + unsigned long op_enable) +{ + (void)dst; + (void)src; + (void)len; + (void)op_enable; +} + +/* Include after implementation, as 't-common.h' pulls in public gcrypt.h + which 'sntrup761.h' refuses to see. */ +#include "../cipher/sntrup761.c" + +#define PGM "t-sntrup761" +#include "t-common.h" + +static uint32_t rng_counter; + +static uint32_t +rng (void) +{ + unsigned char ctr[4], dig[20]; + + ctr[0] = rng_counter; + ctr[1] = rng_counter >> 8; + ctr[2] = rng_counter >> 16; + ctr[3] = rng_counter >> 24; + rng_counter++; + + gcry_md_hash_buffer (GCRY_MD_SHA1, dig, ctr, sizeof (ctr)); + + return ((uint32_t)dig[0] | ((uint32_t)dig[1] << 8) + | ((uint32_t)dig[2] << 16) | ((uint32_t)dig[3] << 24)); +} + +/* Canonical representative of X modulo M, in [0,M). */ +static long +ref_mod (long long x, long m) +{ + long long r = x % m; + + if (r < 0) + r += m; + return (long)r; +} + +static int +ref_F3_freeze (long long x) +{ + return ref_mod (x + 1, 3) - 1; +} + +static int +ref_Fq_freeze (long long x) +{ + return ref_mod (x + q12, q) - q12; +} + +/* Multiplication in Z[x]/(q, x^p - x - 1). */ +static void +ref_Rq_mult_small (Fq *h, const Fq *f, const small *g) +{ + static long long fg[p + p - 1]; + int i, j; + + for (i = 0; i < p + p - 1; i++) + fg[i] = 0; + for (i = 0; i < p; i++) + for (j = 0; j < p; j++) + fg[i + j] += (long long)f[i] * g[j]; + for (i = p + p - 2; i >= p; i--) + { + fg[i - p] += fg[i]; + fg[i - p + 1] += fg[i]; + } + for (i = 0; i < p; i++) + h[i] = ref_Fq_freeze (fg[i]); +} + +static void +ref_R3_mult (small *h, const small *f, const small *g) +{ + static long long fg[p + p - 1]; + int i, j; + + for (i = 0; i < p + p - 1; i++) + fg[i] = 0; + for (i = 0; i < p; i++) + for (j = 0; j < p; j++) + fg[i + j] += (long long)f[i] * g[j]; + for (i = p + p - 2; i >= p; i--) + { + fg[i - p] += fg[i]; + fg[i - p + 1] += fg[i]; + } + for (i = 0; i < p; i++) + h[i] = ref_F3_freeze (fg[i]); +} + +static void +test_freeze_helpers (void) +{ + /* Coprime to q, so that sweep hits different residue every step. */ + static const long sweep_step = 9973; + static const long fq_extremes[] = + { + -2 * (long)q12 * q12, 2 * (long)q12 * q12, + -2 * (long)q12 * q12 + 1, 2 * (long)q12 * q12 - 1, + -(long)q12 * q12, (long)q12 * q12, + -(long)p * q12, (long)p * q12 + }; + long x; + unsigned int i; + + if (verbose) + fprintf (stderr, PGM ": checking F3_freeze over all int16_t\n"); + for (x = -32768; x <= 32767; x++) + { + int got = F3_freeze ((int16_t)x); + int want = ref_F3_freeze (x); + + if (got != want) + fail ("F3_freeze(%ld): got %d, want %d", x, got, want); + } + + if (verbose) + fprintf (stderr, PGM ": checking Fq_freeze over reachable range\n"); + for (x = -(2 * (long)q12 + 8); x <= 2 * (long)q12 + 8; x++) + { + int got = Fq_freeze ((int32_t)x); + int want = ref_Fq_freeze (x); + + if (got != want) + fail ("Fq_freeze(%ld): got %d, want %d", x, got, want); + } + + /* Widest values callers produce, from Rq_recip3. */ + for (x = -2 * (long)q12 * q12; x <= 2 * (long)q12 * q12; x += sweep_step) + { + int got = Fq_freeze ((int32_t)x); + int want = ref_Fq_freeze (x); + + if (got != want) + fail ("Fq_freeze(%ld): got %d, want %d", x, got, want); + } + + for (i = 0; i < DIM (fq_extremes); i++) + { + int got = Fq_freeze ((int32_t)fq_extremes[i]); + int want = ref_Fq_freeze (fq_extremes[i]); + + if (got != want) + fail ("Fq_freeze(%ld): got %d, want %d", fq_extremes[i], got, want); + } +} + +/* Input patterns maximize intermediate accumulators. */ +static void +make_inputs (int pattern, Fq *f, small *g) +{ + int i; + + switch (pattern) + { + case 0: + for (i = 0; i < p; i++) + { + f[i] = q12; + g[i] = 1; + } + break; + case 1: + for (i = 0; i < p; i++) + { + f[i] = -q12; + g[i] = -1; + } + break; + case 2: + for (i = 0; i < p; i++) + { + f[i] = q12; + g[i] = -1; + } + break; + case 3: + for (i = 0; i < p; i++) + { + f[i] = q12; + g[i] = (i & 1) ? -1 : 1; + } + break; + case 4: + for (i = 0; i < p; i++) + { + f[i] = (i & 1) ? -q12 : q12; + g[i] = 1; + } + break; + case 5: + for (i = 0; i < p; i++) + { + f[i] = (rng () & 1) ? q12 : -q12; + g[i] = 1; + } + break; + case 6: + for (i = 0; i < p; i++) + { + f[i] = 0; + g[i] = 0; + } + break; + case 7: + for (i = 0; i < p; i++) + { + f[i] = 0; + g[i] = 0; + } + f[p - 1] = q12; + g[p - 1] = -1; + break; + case 8: + for (i = 0; i < p; i++) + { + f[i] = q12; + g[i] = 0; + } + g[0] = 1; + g[p - 1] = 1; + break; + case 9: + for (i = 0; i < p; i++) + { + f[i] = (int)(rng () % (2 * q12 + 1)) - q12; + g[i] = i < w ? ((rng () & 1) ? 1 : -1) : 0; + } + break; + default: + for (i = 0; i < p; i++) + { + f[i] = (int)(rng () % (2 * q12 + 1)) - q12; + g[i] = (int)(rng () % 3) - 1; + } + break; + } +} + +static void +test_mult (void) +{ + static Fq f[p], h[p], h_ref[p]; + static small g[p], fs[p], h3[p], h3_ref[p]; + int pattern, i; + + for (pattern = 0; pattern < 32; pattern++) + { + rng_counter = pattern << 24; + + make_inputs (pattern, f, g); + for (i = 0; i < p; i++) + fs[i] = g[i]; + + Rq_mult_small (h, f, g); + ref_Rq_mult_small (h_ref, f, g); + if (memcmp (h, h_ref, sizeof (h))) + { + for (i = 0; i < p; i++) + if (h[i] != h_ref[i]) + { + fail ("Rq_mult_small pattern %d coeff %d: got %d, want %d", + pattern, i, (int)h[i], (int)h_ref[i]); + break; + } + } + + R3_mult (h3, fs, g); + ref_R3_mult (h3_ref, fs, g); + if (memcmp (h3, h3_ref, sizeof (h3))) + { + for (i = 0; i < p; i++) + if (h3[i] != h3_ref[i]) + { + fail ("R3_mult pattern %d coeff %d: got %d, want %d", + pattern, i, (int)h3[i], (int)h3_ref[i]); + break; + } + } + } +} + +int +main (int argc, char **argv) +{ + int last_argc = -1; + + if (argc) + { + argc--; + argv++; + } + + while (argc && last_argc != argc) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; + argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + fputs ("usage: " PGM " [--verbose]\n", stdout); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; + argv++; + } + else if (!strncmp (*argv, "--", 2)) + { + fprintf (stderr, PGM ": unknown option '%s'\n", *argv); + exit (1); + } + } + + xgcry_control ((GCRYCTL_SET_VERBOSITY, (int) verbose)); + + if (!gcry_check_version (GCRYPT_VERSION)) + die ("version mismatch\n"); + + xgcry_control ((GCRYCTL_DISABLE_SECMEM, 0)); + xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0)); + xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0)); + + test_freeze_helpers (); + test_mult (); + + if (verbose) + fprintf (stderr, PGM ": %d error(s)\n", error_count); + return !!error_count; +} -- 2.53.0