From d40d7d9bc23acb371c7e7bd7d380753273b51b19 Mon Sep 17 00:00:00 2001 From: "Matthias J. Kannwischer" Date: Tue, 11 Feb 2025 17:01:03 +0800 Subject: [PATCH 1/2] Add functional tests This commit adds a basic functional test. We probably want to extend this later. Signed-off-by: Matthias J. Kannwischer --- .gitignore | 3 + Makefile | 51 +++++++ mldsa/api.h | 29 ++++ mldsa/{ => fips202}/fips202.c | 0 mldsa/{ => fips202}/fips202.h | 0 mldsa/randombytes.c | 80 ----------- mldsa/sign.c | 2 +- mldsa/symmetric-shake.c | 2 +- mldsa/symmetric.h | 2 +- test/mk/auto.mk | 0 test/mk/components.mk | 67 +++++++++ test/mk/config.mk | 112 +++++++++++++++ test/mk/rules.mk | 74 ++++++++++ test/notrandombytes/notrandombytes.c | 118 +++++++++++++++ test/notrandombytes/notrandombytes.h | 23 +++ test/test_mldsa.c | 208 +++++++++++++++++++++++++++ 16 files changed, 688 insertions(+), 83 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile rename mldsa/{ => fips202}/fips202.c (100%) rename mldsa/{ => fips202}/fips202.h (100%) delete mode 100644 mldsa/randombytes.c create mode 100644 test/mk/auto.mk create mode 100644 test/mk/components.mk create mode 100644 test/mk/config.mk create mode 100644 test/mk/rules.mk create mode 100644 test/notrandombytes/notrandombytes.c create mode 100644 test/notrandombytes/notrandombytes.h create mode 100644 test/test_mldsa.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..80d6bb4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 + +test/build \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a587240 --- /dev/null +++ b/Makefile @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: Apache-2.0 + +.PHONY: func \ + func_44 \ + func_65 \ + func_87 \ + run_func \ + run_func_44 \ + run_func_65\ + run_func_87 \ + build test all \ + clean quickcheck + +.DEFAULT_GOAL := build +all: build + +W := $(EXEC_WRAPPER) + +include test/mk/config.mk +include test/mk/components.mk +include test/mk/rules.mk + +quickcheck: test + +build: func + $(Q)echo " Everything builds fine!" + +test: run_func + $(Q)echo " Everything checks fine!" + +run_func_44: func_44 + $(W) $(MLDSA44_DIR)/bin/test_mldsa44 +run_func_65: func_65 + $(W) $(MLDSA65_DIR)/bin/test_mldsa65 +run_func_87: func_87 + $(W) $(MLDSA87_DIR)/bin/test_mldsa87 +run_func: run_func_44 run_func_65 run_func_87 + +func_44: $(MLDSA44_DIR)/bin/test_mldsa44 + $(Q)echo " FUNC ML-DSA-44: $^" +func_65: $(MLDSA65_DIR)/bin/test_mldsa65 + $(Q)echo " FUNC ML-DSA-65: $^" +func_87: $(MLDSA87_DIR)/bin/test_mldsa87 + $(Q)echo " FUNC ML-DSA-87: $^" +func: func_44 func_65 func_87 + +lib: $(BUILD_DIR)/libmldsa.a $(BUILD_DIR)/libmldsa44.a $(BUILD_DIR)/libmldsa65.a $(BUILD_DIR)/libmldsa87.a + +clean: + -$(RM) -rf *.gcno *.gcda *.lcov *.o *.so + -$(RM) -rf $(BUILD_DIR) \ No newline at end of file diff --git a/mldsa/api.h b/mldsa/api.h index 032fa9f..7d96cba 100644 --- a/mldsa/api.h +++ b/mldsa/api.h @@ -94,5 +94,34 @@ int pqcrystals_dilithium5_ref_open(uint8_t *m, size_t *mlen, const uint8_t *ctx, size_t ctxlen, const uint8_t *pk); +#if DILITHIUM_MODE == 2 +#define CRYPTO_PUBLICKEYBYTES pqcrystals_dilithium2_PUBLICKEYBYTES +#define CRYPTO_SECRETKEYBYTES pqcrystals_dilithium2_SECRETKEYBYTES +#define CRYPTO_BYTES pqcrystals_dilithium2_BYTES +#define crypto_sign_keypair pqcrystals_dilithium2_ref_keypair +#define crypto_sign_signature pqcrystals_dilithium2_ref_signature +#define crypto_sign pqcrystals_dilithium2_ref +#define crypto_sign_verify pqcrystals_dilithium2_ref_verify +#define crypto_sign_open pqcrystals_dilithium2_ref_open +#elif DILITHIUM_MODE == 3 +#define CRYPTO_PUBLICKEYBYTES pqcrystals_dilithium3_PUBLICKEYBYTES +#define CRYPTO_SECRETKEYBYTES pqcrystals_dilithium3_SECRETKEYBYTES +#define CRYPTO_BYTES pqcrystals_dilithium3_BYTES +#define crypto_sign_keypair pqcrystals_dilithium3_ref_keypair +#define crypto_sign_signature pqcrystals_dilithium3_ref_signature +#define crypto_sign pqcrystals_dilithium3_ref +#define crypto_sign_verify pqcrystals_dilithium3_ref_verify +#define crypto_sign_open pqcrystals_dilithium3_ref_open +#elif DILITHIUM_MODE == 5 +#define CRYPTO_PUBLICKEYBYTES pqcrystals_dilithium5_PUBLICKEYBYTES +#define CRYPTO_SECRETKEYBYTES pqcrystals_dilithium5_SECRETKEYBYTES +#define CRYPTO_BYTES pqcrystals_dilithium5_BYTES +#define crypto_sign_keypair pqcrystals_dilithium5_ref_keypair +#define crypto_sign_signature pqcrystals_dilithium5_ref_signature +#define crypto_sign pqcrystals_dilithium5_ref +#define crypto_sign_verify pqcrystals_dilithium5_ref_verify +#define crypto_sign_open pqcrystals_dilithium5_ref_open +#endif + #endif diff --git a/mldsa/fips202.c b/mldsa/fips202/fips202.c similarity index 100% rename from mldsa/fips202.c rename to mldsa/fips202/fips202.c diff --git a/mldsa/fips202.h b/mldsa/fips202/fips202.h similarity index 100% rename from mldsa/fips202.h rename to mldsa/fips202/fips202.h diff --git a/mldsa/randombytes.c b/mldsa/randombytes.c deleted file mode 100644 index 7f4b857..0000000 --- a/mldsa/randombytes.c +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include -#include "randombytes.h" - -#ifdef _WIN32 -#include -#include -#else -#include -#include -#ifdef __linux__ -#define _GNU_SOURCE -#include -#include -#else -#include -#endif -#endif - -#ifdef _WIN32 -void randombytes(uint8_t *out, size_t outlen) { - HCRYPTPROV ctx; - size_t len; - - if(!CryptAcquireContext(&ctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) - abort(); - - while(outlen > 0) { - len = (outlen > 1048576) ? 1048576 : outlen; - if(!CryptGenRandom(ctx, len, (BYTE *)out)) - abort(); - - out += len; - outlen -= len; - } - - if(!CryptReleaseContext(ctx, 0)) - abort(); -} -#elif defined(__linux__) && defined(SYS_getrandom) -void randombytes(uint8_t *out, size_t outlen) { - ssize_t ret; - - while(outlen > 0) { - ret = syscall(SYS_getrandom, out, outlen, 0); - if(ret == -1 && errno == EINTR) - continue; - else if(ret == -1) - abort(); - - out += ret; - outlen -= ret; - } -} -#else -void randombytes(uint8_t *out, size_t outlen) { - static int fd = -1; - ssize_t ret; - - while(fd == -1) { - fd = open("/dev/urandom", O_RDONLY); - if(fd == -1 && errno == EINTR) - continue; - else if(fd == -1) - abort(); - } - - while(outlen > 0) { - ret = read(fd, out, outlen); - if(ret == -1 && errno == EINTR) - continue; - else if(ret == -1) - abort(); - - out += ret; - outlen -= ret; - } -} -#endif diff --git a/mldsa/sign.c b/mldsa/sign.c index 7d3f882..9f2a140 100644 --- a/mldsa/sign.c +++ b/mldsa/sign.c @@ -6,7 +6,7 @@ #include "poly.h" #include "randombytes.h" #include "symmetric.h" -#include "fips202.h" +#include "fips202/fips202.h" /************************************************* * Name: crypto_sign_keypair diff --git a/mldsa/symmetric-shake.c b/mldsa/symmetric-shake.c index 11ec09c..27c704b 100644 --- a/mldsa/symmetric-shake.c +++ b/mldsa/symmetric-shake.c @@ -1,7 +1,7 @@ #include #include "params.h" #include "symmetric.h" -#include "fips202.h" +#include "fips202/fips202.h" void dilithium_shake128_stream_init(keccak_state *state, const uint8_t seed[SEEDBYTES], uint16_t nonce) { diff --git a/mldsa/symmetric.h b/mldsa/symmetric.h index cba12d1..0e455d3 100644 --- a/mldsa/symmetric.h +++ b/mldsa/symmetric.h @@ -4,7 +4,7 @@ #include #include "params.h" -#include "fips202.h" +#include "fips202/fips202.h" typedef keccak_state stream128_state; typedef keccak_state stream256_state; diff --git a/test/mk/auto.mk b/test/mk/auto.mk new file mode 100644 index 0000000..e69de29 diff --git a/test/mk/components.mk b/test/mk/components.mk new file mode 100644 index 0000000..5bc1800 --- /dev/null +++ b/test/mk/components.mk @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: Apache-2.0 + +FIPS202_SRCS = $(wildcard mldsa/fips202/*.c) +SOURCES += $(wildcard mldsa/*.c) + +ALL_TESTS = test_mldsa +NON_NIST_TESTS = $(filter-out gen_NISTKAT,$(ALL_TESTS)) + +MLDSA44_DIR = $(BUILD_DIR)/mldsa44 +MLDSA65_DIR = $(BUILD_DIR)/mldsa65 +MLDSA87_DIR = $(BUILD_DIR)/mldsa87 + +MLDSA44_OBJS = $(call MAKE_OBJS,$(MLDSA44_DIR),$(SOURCES) $(FIPS202_SRCS)) +$(MLDSA44_OBJS): CFLAGS += -DDILITHIUM_MODE=2 +MLDSA65_OBJS = $(call MAKE_OBJS,$(MLDSA65_DIR),$(SOURCES) $(FIPS202_SRCS)) +$(MLDSA65_OBJS): CFLAGS += -DDILITHIUM_MODE=3 +MLDSA87_OBJS = $(call MAKE_OBJS,$(MLDSA87_DIR),$(SOURCES) $(FIPS202_SRCS)) +$(MLDSA87_OBJS): CFLAGS += -DDILITHIUM_MODE=5 + +$(BUILD_DIR)/libmldsa44.a: $(MLDSA44_OBJS) +$(BUILD_DIR)/libmldsa65.a: $(MLDSA65_OBJS) +$(BUILD_DIR)/libmldsa87.a: $(MLDSA87_OBJS) + +$(BUILD_DIR)/libmldsa.a: $(MLDSA44_OBJS) $(MLDSA65_OBJS) $(MLDSA87_OBJS) + +$(MLDSA44_DIR)/bin/bench_mldsa44: CFLAGS += -Itest/hal +$(MLDSA65_DIR)/bin/bench_mldsa65: CFLAGS += -Itest/hal +$(MLDSA87_DIR)/bin/bench_mldsa87: CFLAGS += -Itest/hal +$(MLDSA44_DIR)/bin/bench_components_mldsa44: CFLAGS += -Itest/hal +$(MLDSA65_DIR)/bin/bench_components_mldsa65: CFLAGS += -Itest/hal +$(MLDSA87_DIR)/bin/bench_components_mldsa87: CFLAGS += -Itest/hal + +$(MLDSA44_DIR)/bin/bench_mldsa44: $(MLDSA44_DIR)/test/hal/hal.c.o +$(MLDSA65_DIR)/bin/bench_mldsa65: $(MLDSA65_DIR)/test/hal/hal.c.o +$(MLDSA87_DIR)/bin/bench_mldsa87: $(MLDSA87_DIR)/test/hal/hal.c.o +$(MLDSA44_DIR)/bin/bench_components_mldsa44: $(MLDSA44_DIR)/test/hal/hal.c.o +$(MLDSA65_DIR)/bin/bench_components_mldsa65: $(MLDSA65_DIR)/test/hal/hal.c.o +$(MLDSA87_DIR)/bin/bench_components_mldsa87: $(MLDSA87_DIR)/test/hal/hal.c.o + +$(MLDSA44_DIR)/bin/%: CFLAGS += -DDILITHIUM_MODE=2 +$(MLDSA65_DIR)/bin/%: CFLAGS += -DDILITHIUM_MODE=3 +$(MLDSA87_DIR)/bin/%: CFLAGS += -DDILITHIUM_MODE=5 + +# Link tests with respective library +define ADD_SOURCE +$(BUILD_DIR)/$(1)/bin/$(2)$(shell echo $(1) | tr -d -c 0-9): LDLIBS += -L$(BUILD_DIR) -l$(1) +$(BUILD_DIR)/$(1)/bin/$(2)$(shell echo $(1) | tr -d -c 0-9): $(BUILD_DIR)/$(1)/test/$(2).c.o $(BUILD_DIR)/lib$(1).a +endef + +$(foreach scheme,mldsa44 mldsa65 mldsa87, \ + $(foreach test,$(ALL_TESTS), \ + $(eval $(call ADD_SOURCE,$(scheme),$(test))) \ + ) \ +) + +# nistkat tests require special RNG +$(MLDSA44_DIR)/bin/gen_NISTKAT44: CFLAGS += -Itest/nistrng +$(MLDSA44_DIR)/bin/gen_NISTKAT44: $(call MAKE_OBJS, $(MLDSA44_DIR), $(wildcard test/nistrng/*.c)) +$(MLDSA65_DIR)/bin/gen_NISTKAT65: CFLAGS += -Itest/nistrng +$(MLDSA65_DIR)/bin/gen_NISTKAT65: $(call MAKE_OBJS, $(MLDSA65_DIR), $(wildcard test/nistrng/*.c)) +$(MLDSA87_DIR)/bin/gen_NISTKAT87: CFLAGS += -Itest/nistrng +$(MLDSA87_DIR)/bin/gen_NISTKAT87: $(call MAKE_OBJS, $(MLDSA87_DIR), $(wildcard test/nistrng/*.c)) + +# All other tests use test-only RNG +$(NON_NIST_TESTS:%=$(MLDSA44_DIR)/bin/%44): $(call MAKE_OBJS, $(MLDSA44_DIR), $(wildcard test/notrandombytes/*.c)) +$(NON_NIST_TESTS:%=$(MLDSA65_DIR)/bin/%65): $(call MAKE_OBJS, $(MLDSA65_DIR), $(wildcard test/notrandombytes/*.c)) +$(NON_NIST_TESTS:%=$(MLDSA87_DIR)/bin/%87): $(call MAKE_OBJS, $(MLDSA87_DIR), $(wildcard test/notrandombytes/*.c)) diff --git a/test/mk/config.mk b/test/mk/config.mk new file mode 100644 index 0000000..53dfabe --- /dev/null +++ b/test/mk/config.mk @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: Apache-2.0 +ifndef _CONFIG +_CONFIG := + +SRCDIR := $(CURDIR) + +############## +# GCC config # +############## + +CROSS_PREFIX ?= +CC ?= gcc +CPP ?= cpp +AR ?= ar +CC := $(CROSS_PREFIX)$(CC) +CPP := $(CROSS_PREFIX)$(CPP) +AR := $(CROSS_PREFIX)$(AR) +LD := $(CC) +OBJCOPY := $(CROSS_PREFIX)objcopy +SIZE := $(CROSS_PREFIX)size + +# NOTE: gcc-ar is a wrapper around ar that ensures proper integration with GCC plugins, +# such as lto. Using gcc-ar is preferred when creating or linking static libraries +# if the binary is compiled with -flto. However, it is not universally present, so +# only use it if available. +CC_AR ?= $(if $(and $(findstring gcc,$(shell $(CC) --version)), $(findstring gcc-ar, $(shell which $(CROSS_PREFIX)gcc-ar))),gcc-ar,ar) +CC_AR := $(CROSS_PREFIX)$(CC_AR) + +################# +# Common config # +################# +CFLAGS := \ + -Wall \ + -Wextra \ + -Wpedantic \ + -Werror \ + -Wmissing-prototypes \ + -Wshadow \ + -Wpointer-arith \ + -Wredundant-decls \ + -Wno-long-long \ + -Wno-unknown-pragmas \ + -Wno-unused-command-line-argument \ + -O3 \ + -fomit-frame-pointer \ + -std=c99 \ + -pedantic \ + -MMD \ + $(CFLAGS) + +################## +# Some Variables # +################## +Q ?= @ + +HOST_PLATFORM := $(shell uname -s)-$(shell uname -m) +# linux x86_64 +ifeq ($(HOST_PLATFORM),Linux-x86_64) + CFLAGS += -z noexecstack +endif + +ifeq ($(CYCLES),PMU) + CFLAGS += -DPMU_CYCLES +endif + +ifeq ($(CYCLES),PERF) + CFLAGS += -DPERF_CYCLES +endif + +ifeq ($(CYCLES),M1) + CFLAGS += -DM1_CYCLES +endif + +############################## +# Include retained variables # +############################## + +AUTO ?= 1 +CYCLES ?= +OPT ?= 1 +RETAINED_VARS := CROSS_PREFIX CYCLES OPT AUTO + +ifeq ($(AUTO),1) +include test/mk/auto.mk +endif + +BUILD_DIR ?= test/build + +MAKE_OBJS = $(2:%=$(1)/%.o) +OBJS = $(call MAKE_OBJS,$(BUILD_DIR),$(1)) + +CONFIG := $(BUILD_DIR)/config.mk + +-include $(CONFIG) + +$(CONFIG): + @echo " GEN $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + @echo "# These variables are retained and can't be changed without a clean" > $@ + @$(foreach var,$(RETAINED_VARS),echo "$(var) := $($(var))" >> $@; echo "LAST_$(var) := $($(var))" >> $@;) + +define VAR_CHECK +ifneq ($$(origin LAST_$(1)),undefined) +ifneq "$$($(1))" "$$(LAST_$(1))" +$$(info Variable $(1) changed, forcing rebuild!) +.PHONY: $(CONFIG) +endif +endif +endef + +$(foreach VAR,$(RETAINED_VARS),$(eval $(call VAR_CHECK,$(VAR)))) +endif diff --git a/test/mk/rules.mk b/test/mk/rules.mk new file mode 100644 index 0000000..057bacb --- /dev/null +++ b/test/mk/rules.mk @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: Apache-2.0 + +$(BUILD_DIR)/mldsa44/bin/%: $(CONFIG) + $(Q)echo " LD $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(LD) $(CFLAGS) -o $@ $(filter %.o,$^) $(LDLIBS) + +$(BUILD_DIR)/mldsa65/bin/%: $(CONFIG) + $(Q)echo " LD $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(LD) $(CFLAGS) -o $@ $(filter %.o,$^) $(LDLIBS) + +$(BUILD_DIR)/mldsa87/bin/%: $(CONFIG) + $(Q)echo " LD $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(LD) $(CFLAGS) -o $@ $(filter %.o,$^) $(LDLIBS) + +$(BUILD_DIR)/%.a: $(CONFIG) + $(Q)echo " AR $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)rm -f $@ + $(Q)$(CC_AR) rcs $@ $(filter %.o,$^) + # $AR doesn't care about duplicated symbols, one can only find it out + # via actually linking. The easiest one to do this that one can think + # of is to create a dummy C file with empty main function on the fly, + # pipe it to $CC and link with the built library + $(eval _LIB := $(subst $(BUILD_DIR)/lib,,$(@:%.a=%))) + $(eval _CFLAGS := $(subst -static,,$(CFLAGS))) + +ifneq ($(findstring Darwin,$(HOST_PLATFORM)),) # if is on macOS + $(Q)echo "int main(void) {return 0;}" \ + | $(CC) -x c - -L$(BUILD_DIR) $(_CFLAGS) \ + -all_load -Wl,-undefined,dynamic_lookup -l$(_LIB) \ + -Imldsa $(wildcard test/notrandombytes/*.c) -o $(@:%.a=%_tmp.a.out) + $(Q)rm -f $(@:%.a=%_tmp.a.out) +else # if not on macOS + $(Q)echo "int main(void) {return 0;}" \ + | $(CC) -x c - -L$(BUILD_DIR) $(_CFLAGS) \ + -Wl,--whole-archive,--unresolved-symbols=ignore-in-object-files -l$(_LIB) \ + -Wl,--no-whole-archive \ + -Imldsa $(wildcard test/notrandombytes/*.c) -o $(@:%.a=%_tmp.a.out) + $(Q)rm -f $(@:%.a=%_tmp.a.out) +endif + $(Q)echo " AR Checked for duplicated symbols" + +$(BUILD_DIR)/mldsa44/%.c.o: %.c $(CONFIG) + $(Q)echo " CC $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + +$(BUILD_DIR)/mldsa44/%.S.o: %.S $(CONFIG) + $(Q)echo " AS $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + +$(BUILD_DIR)/mldsa65/%.c.o: %.c $(CONFIG) + $(Q)echo " CC $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + +$(BUILD_DIR)/mldsa65/%.S.o: %.S $(CONFIG) + $(Q)echo " AS $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + +$(BUILD_DIR)/mldsa87/%.c.o: %.c $(CONFIG) + $(Q)echo " CC $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + +$(BUILD_DIR)/mldsa87/%.S.o: %.S $(CONFIG) + $(Q)echo " AS $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(CC) -c -o $@ $(CFLAGS) $< diff --git a/test/notrandombytes/notrandombytes.c b/test/notrandombytes/notrandombytes.c new file mode 100644 index 0000000..375ab93 --- /dev/null +++ b/test/notrandombytes/notrandombytes.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2025 The mldsa-native project authors + * SPDX-License-Identifier: LicenseRef-PD-hp OR CC0-1.0 OR 0BSD OR MIT-0 OR MI + * Based on https://cr.yp.to/papers.html#surf by Daniel. J. Bernstein + */ + +/** + * WARNING + * + * The randombytes() implementation in this file is for TESTING ONLY. + * You MUST NOT use this implementation outside of testing. + * + */ + +#include "notrandombytes.h" +#include +#include + +#ifdef ENABLE_CT_TESTING +#include +#endif + +static uint32_t seed[32] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, + 2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, 2, 7, 9, 5}; +static uint32_t in[12]; +static uint32_t out[8]; +static int32_t outleft = 0; + +void randombytes_reset(void) +{ + memset(in, 0, sizeof(in)); + memset(out, 0, sizeof(out)); + outleft = 0; +} + +#define ROTATE(x, b) (((x) << (b)) | ((x) >> (32 - (b)))) +#define MUSH(i, b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x, b)); + +static void surf(void) +{ + uint32_t t[12]; + uint32_t x; + uint32_t sum = 0; + int32_t r; + int32_t i; + int32_t loop; + + for (i = 0; i < 12; ++i) + { + t[i] = in[i] ^ seed[12 + i]; + } + for (i = 0; i < 8; ++i) + { + out[i] = seed[24 + i]; + } + x = t[11]; + for (loop = 0; loop < 2; ++loop) + { + for (r = 0; r < 16; ++r) + { + sum += 0x9e3779b9; + MUSH(0, 5) + MUSH(1, 7) + MUSH(2, 9) + MUSH(3, 13) + MUSH(4, 5) + MUSH(5, 7) + MUSH(6, 9) + MUSH(7, 13) + MUSH(8, 5) + MUSH(9, 7) + MUSH(10, 9) + MUSH(11, 13) + } + for (i = 0; i < 8; ++i) + { + out[i] ^= t[i + 4]; + } + } +} + +void randombytes(uint8_t *buf, size_t n) +{ +#ifdef ENABLE_CT_TESTING + uint8_t *buf_orig = buf; + size_t n_orig = n; +#endif + + while (n > 0) + { + if (!outleft) + { + if (!++in[0]) + { + if (!++in[1]) + { + if (!++in[2]) + { + ++in[3]; + } + } + } + surf(); + outleft = 8; + } + *buf = (uint8_t)out[--outleft]; + ++buf; + --n; + } + +#ifdef ENABLE_CT_TESTING + /* + * Mark all randombytes output as secret (undefined). + * Valgrind will propagate this to everything derived from it. + */ + VALGRIND_MAKE_MEM_UNDEFINED(buf_orig, n_orig); +#endif +} diff --git a/test/notrandombytes/notrandombytes.h b/test/notrandombytes/notrandombytes.h new file mode 100644 index 0000000..a38bc22 --- /dev/null +++ b/test/notrandombytes/notrandombytes.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 The mldsa-native project authors + * SPDX-License-Identifier: LicenseRef-PD-hp OR CC0-1.0 OR 0BSD OR MIT-0 OR MI + * Based on https://cr.yp.to/papers.html#surf by Daniel. J. Bernstein + */ +#ifndef NOTRANDOMBYTES_H +#define NOTRANDOMBYTES_H + +#include +#include + +/** + * WARNING + * + * The randombytes() implementation in this file is for TESTING ONLY. + * You MUST NOT use this implementation outside of testing. + * + */ + +void randombytes_reset(void); +void randombytes(uint8_t *buf, size_t n); + +#endif /* NOTRANDOMBYTES_H */ diff --git a/test/test_mldsa.c b/test/test_mldsa.c new file mode 100644 index 0000000..4edf53a --- /dev/null +++ b/test/test_mldsa.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2025 The mldsa-native project authors + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "../mldsa/api.h" +#include "notrandombytes/notrandombytes.h" + +#define NTESTS 100 +#define MLEN 59 +#define CTXLEN 1 + +static int test_sign(void) +{ + uint8_t pk[CRYPTO_PUBLICKEYBYTES]; + uint8_t sk[CRYPTO_SECRETKEYBYTES]; + uint8_t sm[MLEN + CRYPTO_BYTES]; + uint8_t m[MLEN]; + uint8_t m2[MLEN + CRYPTO_BYTES]; + uint8_t ctx[CTXLEN]; + size_t smlen; + size_t mlen; + int rc; + + + crypto_sign_keypair(pk, sk); + randombytes(ctx, CTXLEN); + randombytes(m, MLEN); + + crypto_sign(sm, &smlen, m, MLEN, ctx, CTXLEN, sk); + + rc = crypto_sign_open(m2, &mlen, sm, smlen, ctx, CTXLEN, pk); + + + if(rc){ + printf("ERROR: crypto_sign_open\n"); + return 1; + } + + if(memcmp(m, m2, MLEN)){ + printf("ERROR: crypto_sign_open - wrong message\n"); + return 1; + } + + if(smlen != MLEN + CRYPTO_BYTES){ + printf("ERROR: crypto_sign_open - wrong smlen\n"); + return 1; + } + + if(mlen != MLEN){ + printf("ERROR: crypto_sign_open - wrong mlen\n"); + return 1; + } + + return 0; +} + +static int test_wrong_pk(void){ + uint8_t pk[CRYPTO_PUBLICKEYBYTES]; + uint8_t sk[CRYPTO_SECRETKEYBYTES]; + uint8_t sm[MLEN + CRYPTO_BYTES]; + uint8_t m[MLEN]; + uint8_t m2[MLEN + CRYPTO_BYTES] = {0}; + uint8_t ctx[CTXLEN]; + size_t smlen; + size_t mlen; + int rc; + size_t idx; + + crypto_sign_keypair(pk, sk); + randombytes(ctx, CTXLEN); + randombytes(m, MLEN); + + crypto_sign(sm, &smlen, m, MLEN, ctx, CTXLEN, sk); + + /* flip bit in public key */ + randombytes((uint8_t *)&idx, sizeof(size_t)); + idx %= CRYPTO_PUBLICKEYBYTES; + + pk[idx] ^= 1; + + rc = crypto_sign_open(m2, &mlen, sm, smlen, ctx, CTXLEN, pk); + + if(!rc){ + printf("ERROR: wrong_pk: crypto_sign_open\n"); + return 1; + } + + for(size_t i=0;i Date: Tue, 11 Feb 2025 17:05:33 +0800 Subject: [PATCH 2/2] Add quickcheck CI Signed-off-by: Matthias J. Kannwischer --- .github/workflows/ci.yml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f383d0e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: Apache-2.0 + +name: CI +permissions: + contents: read +on: + workflow_dispatch: + push: + branches: ["main"] + pull_request: + branches: ["main"] + types: [ "opened", "synchronize" ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + quickcheck: + strategy: + fail-fast: false + matrix: + external: + - ${{ github.repository_owner != 'pq-code-package' }} + target: + - runner: pqcp-arm64 + name: 'aarch64' + - runner: ubuntu-latest + name: 'x86_64' + - runner: macos-latest + name: 'macos (aarch64)' + - runner: macos-13 + name: 'macos (x86_64)' + name: Quickcheck (${{ matrix.target.name }}) + runs-on: ${{ matrix.target.runner }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: make quickcheck + run: | + make quickcheck \ No newline at end of file