nss/nss-3.118-ml-dsa-leancrypto.patch
2026-08-19 19:37:08 -04:00

21026 lines
708 KiB
Diff

diff --git a/lib/freebl/Makefile b/lib/freebl/Makefile
--- a/lib/freebl/Makefile
+++ b/lib/freebl/Makefile
@@ -605,10 +605,26 @@
VERIFIED_SRCS += Hacl_Hash_SHA3.c Hacl_P256.c Hacl_P384.c Hacl_P521.c libcrux_sha3_portable.c libcrux_mlkem768_portable.c libcrux_mlkem1024_portable.c libcrux_mlkem_portable.c libcrux_core.c
VERIFIED_SRCS += Hacl_Ed25519.c
VERIFIED_SRCS += Hacl_Curve25519_51.c
+INCLUDES += -Ileancrypto
+ML_DSA_SRCS += ml_dsa_44_ntt.c
+ML_DSA_SRCS += ml_dsa_44_poly.c
+ML_DSA_SRCS += ml_dsa_44_rounding.c
+ML_DSA_SRCS += ml_dsa_44_signature_c.c
+ML_DSA_SRCS += ml_dsa_65_ntt.c
+ML_DSA_SRCS += ml_dsa_65_poly.c
+ML_DSA_SRCS += ml_dsa_65_rounding.c
+ML_DSA_SRCS += ml_dsa_65_signature_c.c
+ML_DSA_SRCS += ml_dsa_87_ntt.c
+ML_DSA_SRCS += ml_dsa_87_poly.c
+ML_DSA_SRCS += ml_dsa_87_rounding.c
+ML_DSA_SRCS += ml_dsa_87_signature_c.c
+ML_DSA_SRCS += signature_domain_separation.c
+ML_DSA_SRCS += mldsa_zetas.c
+
# Bug 1918767 / Bug 1918711 - by setting KRML_MUSTINLINE=inline here, we
# avoid it being defined to `inline __forceinline` (for msvc) or `inline
# __attribute__((always_inline))` (for gcc/clang) in
# verified/karamel/include/krml/internal/target.h. These other
# configurations can cause excessive stack usage.
@@ -642,12 +658,12 @@
rijndael_tables:
$(CC) -o $(OBJDIR)/make_rijndael_tab rijndael_tables.c \
$(DEFINES) $(INCLUDES) $(OBJDIR)/libfreebl.a
$(OBJDIR)/make_rijndael_tab
-vpath %.h mpi ecl verified deprecated
-vpath %.c mpi ecl verified deprecated
+vpath %.h mpi ecl verified oqs leancrypto deprecated
+vpath %.c mpi ecl verified oqs leancrypto deprecated
vpath %.S mpi ecl
vpath %.s mpi ecl
vpath %.asm mpi ecl
INCLUDES += -Impi -Iecl -Iverified -Iverified/internal -Iverified/karamel/include -Iverified/karamel/krmllib/dist/minimal -Iverified/eurydice -Ideprecated
diff --git a/lib/freebl/freebl.gyp b/lib/freebl/freebl.gyp
--- a/lib/freebl/freebl.gyp
+++ b/lib/freebl/freebl.gyp
@@ -762,10 +762,11 @@
],
'target_defaults': {
'include_dirs': [
'mpi',
'ecl',
+ 'leancrypto',
'verified',
'verified/internal',
'verified/karamel/include',
'verified/karamel/krmllib/dist/minimal',
'deprecated',
diff --git a/lib/freebl/freebl_base.gypi b/lib/freebl/freebl_base.gypi
--- a/lib/freebl/freebl_base.gypi
+++ b/lib/freebl/freebl_base.gypi
@@ -62,10 +62,24 @@
'verified/Hacl_P521.c',
'sha3.c',
'shake.c',
'verified/Hacl_Curve25519_51.c',
'verified/Hacl_Ed25519.c',
+ 'leancrypto/ml_dsa_44_ntt.c',
+ 'leancrypto/ml_dsa_44_poly.c',
+ 'leancrypto/ml_dsa_44_rounding.c',
+ 'leancrypto/ml_dsa_44_signature_c.c',
+ 'leancrypto/ml_dsa_65_ntt.c',
+ 'leancrypto/ml_dsa_65_poly.c',
+ 'leancrypto/ml_dsa_65_rounding.c',
+ 'leancrypto/ml_dsa_65_signature_c.c',
+ 'leancrypto/ml_dsa_87_ntt.c',
+ 'leancrypto/ml_dsa_87_poly.c',
+ 'leancrypto/ml_dsa_87_rounding.c',
+ 'leancrypto/ml_dsa_87_signature_c.c',
+ 'leancrypto/signature_domain_separation.c',
+ 'leancrypto/mldsa_zetas.c',
],
'defines': [
# For kyber-pqcrystals-ref.c. If we ever decide to support Kyber512 or
# Kyber1024, we'll need to build separate static libraries with different
# values of KYBER_K.
diff --git a/lib/freebl/leancrypto/alignment.h b/lib/freebl/leancrypto/alignment.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/alignment.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef ALIGNMENT_H
+#define ALIGNMENT_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__GNUC__)
+#define __align(x) __attribute__((aligned(x)))
+#elif defined(_MSC_VER)
+//#define __align(x) __declspec(align(x))
+#define __align(x) __attribute__((aligned(x)))
+#elif defined(__ARMCC_VERSION)
+/* Nothing, the used macro is known to the compiler */
+#else
+#define __align(x)
+#endif
+
+#define ALIGNED_UINT8_COEFFS(N) N
+#define ALIGNED_UINT8_UINT64(N) ((N + 7) / 8)
+
+#define BUF_ALIGNED_UINT8_UINT64(N) \
+ union { \
+ uint8_t coeffs[ALIGNED_UINT8_COEFFS(N)]; \
+ uint64_t vec[ALIGNED_UINT8_UINT64(N)]; \
+ }
+
+static inline int
+aligned(const uint8_t *ptr, uint32_t alignmask)
+{
+ if ((uintptr_t)ptr & alignmask)
+ return 0;
+ return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ALIGNMENT_H */
diff --git a/lib/freebl/leancrypto/atomic.h b/lib/freebl/leancrypto/atomic.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/atomic.h
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see COPYING file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef _ATOMIC_H
+#define _ATOMIC_H
+
+#ifndef LINUX_KERNEL
+
+/*
+ * Atomic operations only work on:
+ * GCC >= 4.1
+ * Clang / LLVM
+ */
+
+/**
+ * Atomic type and operations equivalent to the Linux kernel.
+ */
+typedef struct {
+ volatile int counter;
+} atomic_t;
+
+/**
+ * Memory barrier
+ */
+static inline void
+mb(void)
+{
+ __sync_synchronize();
+}
+
+#define ATOMIC_INIT(i) \
+ { \
+ (i) \
+ }
+
+/**
+ * Read atomic variable
+ * @param v atomic variable
+ * @return variable content
+ */
+static inline int
+atomic_read(const atomic_t *v)
+{
+ int i;
+
+ mb();
+ i = ((v)->counter);
+ mb();
+
+ return i;
+}
+
+/**
+ * Set atomic variable
+ * @param v atomic variable
+ * @param i value to be set
+ */
+static inline void
+atomic_set(atomic_t *v, int i)
+{
+ mb();
+ ((v)->counter) = i;
+ mb();
+}
+
+/**
+ * Atomic add operation
+ * @param i integer value to add
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int
+atomic_add(int i, atomic_t *v)
+{
+ return __sync_add_and_fetch(&v->counter, i);
+}
+
+/**
+ * Atomic add value from variable and test for zero
+ * @param i integer value to add
+ * @param v atomic variable
+ * @return true if the result is zero, or false for all other cases.
+ */
+static inline int
+atomic_add_and_test(int i, atomic_t *v)
+{
+ return !(__sync_add_and_fetch(&v->counter, i));
+}
+
+/**
+ * Atomic increment by 1
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int
+atomic_inc(atomic_t *v)
+{
+ return atomic_add(1, v);
+}
+
+/**
+ * Atomic increment and test for zero
+ * @param v pointer of type atomic_t
+ * @return true if the result is zero, or false for all other cases.
+ */
+static inline int
+atomic_inc_and_test(atomic_t *v)
+{
+ return atomic_add_and_test(1, v);
+}
+
+/**
+ * Atomic subtract operation
+ * @param i integer value to subtract
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int
+atomic_sub(int i, atomic_t *v)
+{
+ return __sync_sub_and_fetch(&v->counter, i);
+}
+
+/**
+ * Atomic subtract value from variable and test for zero
+ * @param i integer value to subtract
+ * @param v atomic variable
+ * @return true if the result is zero, or false for all other cases.
+ */
+static inline int
+atomic_sub_and_test(int i, atomic_t *v)
+{
+ return !(__sync_sub_and_fetch(&v->counter, i));
+}
+
+/**
+ * Atomic decrement by 1
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int
+atomic_dec(atomic_t *v)
+{
+ return atomic_sub(1, v);
+}
+
+/**
+ * Atomic decrement by 1 and test for zero
+ * @param v atomic variable
+ * @return true if the result is zero, or false for all other cases.
+ */
+static inline int
+atomic_dec_and_test(atomic_t *v)
+{
+ return atomic_sub_and_test(1, v);
+}
+
+/**
+ * Atomic or operation
+ * @param i integer value to or
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int
+atomic_or(int i, atomic_t *v)
+{
+ return __sync_or_and_fetch(&v->counter, i);
+}
+
+/**
+ * Atomic xor operation
+ * @param i integer value to xor
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int
+atomic_xor(int i, atomic_t *v)
+{
+ return __sync_xor_and_fetch(&v->counter, i);
+}
+
+/**
+ * Atomic and operation
+ * @param i integer value to and
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int
+atomic_and(int i, atomic_t *v)
+{
+ return __sync_and_and_fetch(&v->counter, i);
+}
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wsync-fetch-and-nand-semantics-changed"
+#endif
+/**
+ * Atomic nand operation
+ * @param i integer value to nand
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int
+atomic_nand(int i, atomic_t *v)
+{
+ return __sync_nand_and_fetch(&v->counter, i);
+}
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+/**
+ * Atomic compare and exchange operation (if current value of atomic
+ * variable is equal to the old value, set the new value)
+ * @param v atomic variable
+ * @param old integer value to compare with
+ * @param new integer value to set atomic variable to
+ * @return original value if comparison is successful and new was written
+ * To verify that the exchange was successful, the caller must compare
+ * the return value with the old value.
+ */
+static inline int
+atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+ return __sync_val_compare_and_swap(&v->counter, old, new);
+}
+
+/**
+ * Atomic operation with a caller-provided function to derive the new
+ * value from the old value. Note, the caller-provided function may be called
+ * multiple times.
+ *
+ * @param v atomic variable
+ * @param data parameter that is given to the check function to maintain a state
+ * @param check_func Function that returns the new value to be set. The function
+ * is invoked with the old value as input parameter.
+ */
+static inline void
+atomic_update_with_func(atomic_t *v, void *data,
+ int (*check_func)(void *data,
+ int old))
+{
+ int old;
+
+ do {
+ old = atomic_read(v);
+ } while (atomic_cmpxchg(v, old, check_func(data, old)) != old);
+}
+
+#endif /* LINUX_KERNEL */
+#endif /* _ATOMIC_H */
diff --git a/lib/freebl/leancrypto/atomic_bool.h b/lib/freebl/leancrypto/atomic_bool.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/atomic_bool.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see COPYING file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef _ATOMIC_BOOL_H
+#define _ATOMIC_BOOL_H
+
+#include "bool.h"
+
+/*
+ * Atomic operations only work on:
+ * GCC >= 4.1
+ * Clang / LLVM
+ */
+
+/**
+ * Atomic type and operations equivalent to the Linux kernel.
+ */
+typedef struct {
+ volatile bool counter;
+} atomic_bool_t;
+
+/**
+ * Memory barrier
+ */
+static inline void
+atomic_bool_mb(void)
+{
+ __sync_synchronize();
+}
+
+#define ATOMIC_BOOL_INIT(i) \
+ { \
+ (i) \
+ }
+
+/**
+ * Read atomic variable
+ * @param v atomic variable
+ * @return variable content
+ */
+static inline bool
+atomic_bool_read(const atomic_bool_t *v)
+{
+ bool i;
+
+ atomic_bool_mb();
+ i = ((v)->counter);
+ atomic_bool_mb();
+
+ return i;
+}
+
+/**
+ * Set atomic variable
+ * @param i value to be set
+ * @param v atomic variable
+ */
+static inline void
+atomic_bool_set(bool i, atomic_bool_t *v)
+{
+ atomic_bool_mb();
+ ((v)->counter) = i;
+ atomic_bool_mb();
+}
+
+/**
+ * Set atomic variable to true
+ * @param v atomic variable
+ */
+static inline void
+atomic_bool_set_true(atomic_bool_t *v)
+{
+ atomic_bool_set(true, v);
+}
+
+/**
+ * Set atomic variable to false
+ * @param v atomic variable
+ */
+static inline void
+atomic_bool_set_false(atomic_bool_t *v)
+{
+ atomic_bool_set(false, v);
+}
+
+/**
+ * Atomic compare and exchange operation (if current value of atomic
+ * variable is equal to the old value, set the new value)
+ * @param v atomic variable
+ * @param old integer value to compare with
+ * @param new integer value to set atomic variable to
+ * @return true if comparison is successful and new was written
+ */
+static inline int
+atomic_bool_cmpxchg(atomic_bool_t *v, bool old, bool new)
+{
+ return __sync_bool_compare_and_swap(&v->counter, old, new);
+}
+
+#endif /* _ATOMIC_BOOL_H */
diff --git a/lib/freebl/leancrypto/binhexbin.h b/lib/freebl/leancrypto/binhexbin.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/binhexbin.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef _BINHEXBIN_H
+#define _BINHEXBIN_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void hex2bin(const char *hex, const size_t hexlen, uint8_t *bin,
+ const size_t binlen);
+int hex2bin_alloc(const char *hex, const size_t hexlen, uint8_t **bin,
+ size_t *binlen);
+int bin2hex_alloc(const uint8_t *bin, const size_t binlen, char **hex,
+ size_t *hexlen);
+void bin2print(const unsigned char *bin, const size_t binlen, FILE *out,
+ const char *explanation);
+void bin2hex(const uint8_t *bin, const size_t binlen, char *hex,
+ const size_t hexlen, const int u);
+
+int bin2hex_html(const char *str, const size_t strlen, char *html,
+ const size_t htmllen);
+int bin2hex_html_from_url(const char *str, const size_t strlen, char *html,
+ const size_t htmllen);
+int bin2hex_html_alloc(const char *str, const size_t strlen, char **html,
+ size_t *htmllen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BINHEXBIN_H */
diff --git a/lib/freebl/leancrypto/bitshift.h b/lib/freebl/leancrypto/bitshift.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/bitshift.h
@@ -0,0 +1,91 @@
+/* Conversion of a pointer value to an integer
+ *
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef BITSHIFT_H
+#define BITSHIFT_H
+
+#include "bitshift_le.h"
+#include "bitshift_be.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+
+static inline uint64_t
+ptr_to_64(const uint8_t *p)
+{
+ return ptr_to_be64(p);
+}
+
+static inline uint32_t
+ptr_to_32(const uint8_t *p)
+{
+ return ptr_to_be32(p);
+}
+
+static inline void
+val64_to_ptr(uint8_t *p, const uint64_t value)
+{
+ be64_to_ptr(p, value);
+}
+
+static inline void
+val32_to_ptr(uint8_t *p, const uint32_t value)
+{
+ be32_to_ptr(p, value);
+}
+
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+
+static inline uint64_t
+ptr_to_64(const uint8_t *p)
+{
+ return ptr_to_le64(p);
+}
+
+static inline uint32_t
+ptr_to_32(const uint8_t *p)
+{
+ return ptr_to_le32(p);
+}
+
+static inline void
+val64_to_ptr(uint8_t *p, const uint64_t value)
+{
+ le64_to_ptr(p, value);
+}
+
+static inline void
+val32_to_ptr(uint8_t *p, const uint32_t value)
+{
+ le32_to_ptr(p, value);
+}
+
+#else
+#error "Endianess not defined"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BITSHIFT_H */
diff --git a/lib/freebl/leancrypto/bitshift_be.h b/lib/freebl/leancrypto/bitshift_be.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/bitshift_be.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef BITSHIFT_BE_H
+#define BITSHIFT_BE_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Conversion of Big-Endian representations in byte streams - the data
+ * representation in the integer values is the host representation.
+ */
+static inline uint32_t
+ptr_to_be32(const uint8_t *p)
+{
+ return (uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 |
+ (uint32_t)p[2] << 8 | (uint32_t)p[3];
+}
+
+static inline uint64_t
+ptr_to_be64(const uint8_t *p)
+{
+ return (uint64_t)ptr_to_be32(p) << 32 | (uint64_t)ptr_to_be32(p + 4);
+}
+
+static inline void
+be32_to_ptr(uint8_t *p, const uint32_t value)
+{
+ p[0] = (uint8_t)(value >> 24);
+ p[1] = (uint8_t)(value >> 16);
+ p[2] = (uint8_t)(value >> 8);
+ p[3] = (uint8_t)(value);
+}
+
+static inline void
+be64_to_ptr(uint8_t *p, const uint64_t value)
+{
+ be32_to_ptr(p, (uint32_t)(value >> 32));
+ be32_to_ptr(p + 4, (uint32_t)(value));
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BITSHIFT_BE_H */
diff --git a/lib/freebl/leancrypto/bitshift_le.h b/lib/freebl/leancrypto/bitshift_le.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/bitshift_le.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef BITSHIFT_LE_H
+#define BITSHIFT_LE_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Conversion of Little-Endian representations in byte streams - the data
+ * representation in the integer values is the host representation.
+ */
+static inline uint32_t
+ptr_to_le32(const uint8_t *p)
+{
+ return (uint32_t)p[0] | (uint32_t)p[1] << 8 | (uint32_t)p[2] << 16 |
+ (uint32_t)p[3] << 24;
+}
+
+static inline uint64_t
+ptr_to_le64(const uint8_t *p)
+{
+ return (uint64_t)ptr_to_le32(p) | (uint64_t)ptr_to_le32(p + 4) << 32;
+}
+
+static inline void
+le32_to_ptr(uint8_t *p, const uint32_t value)
+{
+ p[0] = (uint8_t)(value);
+ p[1] = (uint8_t)(value >> 8);
+ p[2] = (uint8_t)(value >> 16);
+ p[3] = (uint8_t)(value >> 24);
+}
+
+static inline void
+le64_to_ptr(uint8_t *p, const uint64_t value)
+{
+ le32_to_ptr(p + 4, (uint32_t)(value >> 32));
+ le32_to_ptr(p, (uint32_t)(value));
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BITSHIFT_LE_H */
diff --git a/lib/freebl/leancrypto/bool.h b/lib/freebl/leancrypto/bool.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/bool.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef BOOL_H
+#define BOOL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LINUX_KERNEL
+
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ < 202311L && !defined(false)
+/* Boolean variable */
+enum { false,
+ true };
+typedef _Bool bool;
+#endif
+
+#endif /* LINUX_KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BOOL_H */
diff --git a/lib/freebl/leancrypto/build_bug_on.h b/lib/freebl/leancrypto/build_bug_on.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/build_bug_on.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 - 2023, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef _BUILD_BUG_ON_H
+#define _BUILD_BUG_ON_H
+
+#ifndef LINUX_KERNEL
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2 * !!(condition)]))
+#endif
+
+#endif /* _BUILD_BUG_ON_H */
diff --git a/lib/freebl/leancrypto/compare.h b/lib/freebl/leancrypto/compare.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/compare.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef COMPARE_H
+#define COMPARE_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int get_current_selftest_level(void);
+
+#ifdef LC_SELFTEST_ENABLED
+#define LC_SELFTEST_RUN(x) \
+ if (*x == get_current_selftest_level()) \
+ return; \
+ *x = get_current_selftest_level();
+#else /* LC_SELFTEST_ENABLED */
+#define LC_SELFTEST_RUN(x) \
+ (void)x; \
+ if (1) \
+ return;
+#endif /* LC_SELFTEST_ENABLED */
+
+int lc_compare(const uint8_t *act, const uint8_t *exp, const size_t len,
+ const char *info);
+void lc_compare_selftest(const uint8_t *act, const uint8_t *exp,
+ const size_t len, const char *info);
+void lc_disable_selftest(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* COMPARE_H */
diff --git a/lib/freebl/leancrypto/conv_be_le.h b/lib/freebl/leancrypto/conv_be_le.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/conv_be_le.h
@@ -0,0 +1,157 @@
+/* Conversion functions from LE to BE and vice versa
+ *
+ * Copyright (C) 2015 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef CONV_BE_LE_H
+#define CONV_BE_LE_H
+
+#include "ext_headers.h"
+#include "rotate.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined(CONVERSION_TEST) && (defined(__gcc__) || defined(__clang__))
+#define __HAVE_BUILTIN_BSWAP16__
+#define __HAVE_BUILTIN_BSWAP32__
+#define __HAVE_BUILTIN_BSWAP64__
+#endif
+
+/* Byte swap for 16-bit, 32-bit and 64-bit integers. */
+#ifndef __HAVE_BUILTIN_BSWAP16__
+static inline uint16_t
+_lc_bswap16(uint16_t x)
+{
+ return (uint16_t)((rol16(x, 8) & 0x00ff) | (ror16(x, 8) & 0xff00));
+}
+#define _lc_swap16(x) _lc_bswap16((uint16_t)(x))
+#else
+#define _lc_swap16(x) (uint16_t) __builtin_bswap16((uint16_t)(x))
+#endif
+
+#if !defined(__HAVE_BUILTIN_BSWAP32__) || !defined(__HAVE_BUILTIN_BSWAP64__)
+static inline uint32_t
+_lc_bswap32(uint32_t x)
+{
+ return ((rol32(x, 8) & 0x00ff00ffL) | (ror32(x, 8) & 0xff00ff00L));
+}
+#define _lc_swap32(x) _lc_bswap32((uint32_t)(x))
+#else
+#define _lc_swap32(x) (uint32_t) __builtin_bswap32((uint32_t)(x))
+#endif
+
+#ifndef __HAVE_BUILTIN_BSWAP64__
+static inline uint64_t
+_lc_bswap64(uint64_t x)
+{
+ return ((uint64_t)_lc_bswap32((uint32_t)x) << 32) |
+ (_lc_bswap32((uint32_t)(x >> 32)));
+}
+#define _lc_swap64(x) _lc_bswap64((uint64_t)(x))
+#else
+#define _lc_swap64(x) (uint64_t) __builtin_bswap64((uint64_t)(x))
+#endif
+
+/* Endian dependent byte swap operations. */
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+
+static inline uint16_t
+le_bswap16(uint16_t x)
+{
+ return _lc_swap16(x);
+}
+
+static inline uint16_t
+be_bswap16(uint16_t x)
+{
+ return x;
+}
+
+static inline uint32_t
+le_bswap32(uint32_t x)
+{
+ return _lc_swap32(x);
+}
+
+static inline uint32_t
+be_bswap32(uint32_t x)
+{
+ return x;
+}
+
+static inline uint64_t
+le_bswap64(uint64_t x)
+{
+ return _lc_swap64(x);
+}
+
+static inline uint64_t
+be_bswap64(uint64_t x)
+{
+ return x;
+}
+
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+
+static inline uint16_t
+le_bswap16(uint16_t x)
+{
+ return x;
+}
+
+static inline uint16_t
+be_bswap16(uint16_t x)
+{
+ return _lc_swap16(x);
+}
+
+static inline uint32_t
+le_bswap32(uint32_t x)
+{
+ return x;
+}
+
+static inline uint32_t
+be_bswap32(uint32_t x)
+{
+ return _lc_swap32(x);
+}
+
+static inline uint64_t
+le_bswap64(uint64_t x)
+{
+ return x;
+}
+
+static inline uint64_t
+be_bswap64(uint64_t x)
+{
+ return _lc_swap64(x);
+}
+
+#else
+#error "Endianess not defined"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONV_BE_LE_H */
diff --git a/lib/freebl/leancrypto/dilithium_debug.h b/lib/freebl/leancrypto/dilithium_debug.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_debug.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_DEBUG_H
+#define DILITHIUM_DEBUG_H
+
+#include "dilithium_type.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LC_DILITHIUM_DEBUG
+
+/* Disable selftests */
+#define LC_DILITHIUM_TEST_INIT 1
+
+void dilithium_print_buffer(const uint8_t *buffer, const size_t bufferlen,
+ const char *explanation);
+void dilithium_print_polyvecl_k(polyvecl mat[LC_DILITHIUM_K],
+ const char *explanation);
+void dilithium_print_polyvecl(polyvecl *polyvec, const char *explanation);
+void dilithium_print_polyveck(polyveck *polyvec, const char *explanation);
+void dilithium_print_poly(poly *vec, const char *explanation);
+
+#else /* LC_DILITHIUM_DEBUG */
+
+/* Enable selftests */
+#define LC_DILITHIUM_TEST_INIT 0
+
+static inline void
+dilithium_print_buffer(const uint8_t *buffer,
+ const size_t bufferlen,
+ const char *explanation)
+{
+ (void)buffer;
+ (void)bufferlen;
+ (void)explanation;
+}
+
+static inline void
+dilithium_print_polyvecl_k(polyvecl mat[LC_DILITHIUM_K],
+ const char *explanation)
+{
+ (void)mat;
+ (void)explanation;
+}
+
+static inline void
+dilithium_print_polyvecl(polyvecl *polyvec,
+ const char *explanation)
+{
+ (void)polyvec;
+ (void)explanation;
+}
+
+static inline void
+dilithium_print_polyveck(polyveck *polyvec,
+ const char *explanation)
+{
+ (void)polyvec;
+ (void)explanation;
+}
+
+static inline void
+dilithium_print_poly(poly *vec, const char *explanation)
+{
+ (void)vec;
+ (void)explanation;
+}
+
+#endif /* LC_DILITHIUM_DEBUG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_DEBUG_H */
diff --git a/lib/freebl/leancrypto/dilithium_ntt.h b/lib/freebl/leancrypto/dilithium_ntt.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_ntt.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_NTT_H
+#define DILITHIUM_NTT_H
+
+#include "ext_headers.h"
+#include "dilithium_type.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ntt(int32_t a[LC_DILITHIUM_N]);
+void invntt_tomont(int32_t a[LC_DILITHIUM_N]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_NTT_H */
diff --git a/lib/freebl/leancrypto/dilithium_pack.h b/lib/freebl/leancrypto/dilithium_pack.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_pack.h
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_PACK_H
+#define DILITHIUM_PACK_H
+
+#include "build_bug_on.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*******************************************************************************
+ * Pack / Unpack public key
+ ******************************************************************************/
+static inline void
+pack_pk_rho(struct lc_dilithium_pk *pk,
+ const uint8_t rho[LC_DILITHIUM_SEEDBYTES])
+{
+ memcpy(pk->pk, rho, LC_DILITHIUM_SEEDBYTES);
+}
+
+static inline void
+pack_pk_t1(struct lc_dilithium_pk *pk, const polyveck *t1)
+{
+ unsigned int i;
+ uint8_t *pubkey = pk->pk + LC_DILITHIUM_SEEDBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyt1_pack(pubkey + i * LC_DILITHIUM_POLYT1_PACKEDBYTES,
+ &t1->vec[i]);
+}
+
+static inline void
+unpack_pk_rho(uint8_t rho[LC_DILITHIUM_SEEDBYTES],
+ const struct lc_dilithium_pk *pk)
+{
+ memcpy(rho, pk->pk, LC_DILITHIUM_SEEDBYTES);
+}
+
+static inline void
+unpack_pk_t1(polyveck *t1, const struct lc_dilithium_pk *pk)
+{
+ unsigned int i;
+ const uint8_t *pubkey = pk->pk + LC_DILITHIUM_SEEDBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyt1_unpack(&t1->vec[i],
+ pubkey + i * LC_DILITHIUM_POLYT1_PACKEDBYTES);
+}
+
+/*******************************************************************************
+ * Pack / Unpack secret key
+ ******************************************************************************/
+static inline void
+pack_sk_rho(struct lc_dilithium_sk *sk,
+ const uint8_t rho[LC_DILITHIUM_SEEDBYTES])
+{
+ memcpy(sk->sk, rho, LC_DILITHIUM_SEEDBYTES);
+}
+
+static inline void
+pack_sk_key(struct lc_dilithium_sk *sk,
+ const uint8_t key[LC_DILITHIUM_SEEDBYTES])
+{
+ memcpy(sk->sk + LC_DILITHIUM_SEEDBYTES, key, LC_DILITHIUM_SEEDBYTES);
+}
+
+static inline void
+pack_sk_tr(struct lc_dilithium_sk *sk,
+ const uint8_t tr[LC_DILITHIUM_TRBYTES])
+{
+ memcpy(sk->sk + 2 * LC_DILITHIUM_SEEDBYTES, tr, LC_DILITHIUM_TRBYTES);
+}
+
+static inline void
+pack_sk_s1(struct lc_dilithium_sk *sk, const polyvecl *s1)
+{
+ unsigned int i;
+ uint8_t *seckey =
+ sk->sk + 2 * LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_TRBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ polyeta_pack(seckey + i * LC_DILITHIUM_POLYETA_PACKEDBYTES,
+ &s1->vec[i]);
+}
+
+static inline void
+pack_sk_s2(struct lc_dilithium_sk *sk, const polyveck *s2)
+{
+ unsigned int i;
+ uint8_t *seckey = sk->sk + 2 * LC_DILITHIUM_SEEDBYTES +
+ LC_DILITHIUM_TRBYTES +
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYETA_PACKEDBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyeta_pack(seckey + i * LC_DILITHIUM_POLYETA_PACKEDBYTES,
+ &s2->vec[i]);
+}
+
+static inline void
+pack_sk_t0(struct lc_dilithium_sk *sk, const polyveck *t0)
+{
+ unsigned int i;
+ uint8_t *seckey = sk->sk + 2 * LC_DILITHIUM_SEEDBYTES +
+ LC_DILITHIUM_TRBYTES +
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYETA_PACKEDBYTES +
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYETA_PACKEDBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyt0_pack(seckey + i * LC_DILITHIUM_POLYT0_PACKEDBYTES,
+ &t0->vec[i]);
+}
+
+static inline void
+unpack_sk_rho(uint8_t rho[LC_DILITHIUM_SEEDBYTES],
+ const struct lc_dilithium_sk *sk)
+{
+ memcpy(rho, sk->sk, LC_DILITHIUM_SEEDBYTES);
+}
+
+static inline void
+unpack_sk_key(uint8_t key[LC_DILITHIUM_SEEDBYTES],
+ const struct lc_dilithium_sk *sk)
+{
+ memcpy(key, sk->sk + LC_DILITHIUM_SEEDBYTES, LC_DILITHIUM_SEEDBYTES);
+}
+
+static inline void
+unpack_sk_tr(uint8_t tr[LC_DILITHIUM_TRBYTES],
+ const struct lc_dilithium_sk *sk)
+{
+ memcpy(tr, sk->sk + 2 * LC_DILITHIUM_SEEDBYTES, LC_DILITHIUM_TRBYTES);
+}
+
+static inline void
+unpack_sk_s1(polyvecl *s1, const struct lc_dilithium_sk *sk)
+{
+ unsigned int i;
+ const uint8_t *seckey =
+ sk->sk + 2 * LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_TRBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ polyeta_unpack(&s1->vec[i],
+ seckey + i * LC_DILITHIUM_POLYETA_PACKEDBYTES);
+}
+
+static inline void
+unpack_sk_s2(polyveck *s2, const struct lc_dilithium_sk *sk)
+{
+ unsigned int i;
+ const uint8_t *seckey =
+ sk->sk + 2 * LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_TRBYTES +
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYETA_PACKEDBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyeta_unpack(&s2->vec[i],
+ seckey + i * LC_DILITHIUM_POLYETA_PACKEDBYTES);
+}
+
+static inline void
+unpack_sk_t0(polyveck *t0, const struct lc_dilithium_sk *sk)
+{
+ unsigned int i;
+ const uint8_t *seckey =
+ sk->sk + 2 * LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_TRBYTES +
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYETA_PACKEDBYTES +
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYETA_PACKEDBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyt0_unpack(&t0->vec[i],
+ seckey + i * LC_DILITHIUM_POLYT0_PACKEDBYTES);
+}
+
+/**
+ * @brief pack_sig - Bit-pack signature sig = (c, z, h).
+ *
+ * NOTE: A signature is the concatenation of sig = (c || packed z || packed h).
+ * As c is already present in the first bytes of sig, this function does
+ * not need to copy it yet again to the right location. This implies that
+ * this function does not process c.
+ *
+ * @param [out] sig signature
+ * @param [in] z pointer to vector z
+ * @param [in] h pointer to hint vector h
+ */
+static inline void
+pack_sig(struct lc_dilithium_sig *sig, const polyvecl *z,
+ const polyveck *h)
+{
+ unsigned int i, j, k;
+ /* Skip c */
+ uint8_t *signature = sig->sig + LC_DILITHIUM_CTILDE_BYTES;
+
+ BUILD_BUG_ON((1ULL << (sizeof(j) << 3)) < LC_DILITHIUM_N);
+ BUILD_BUG_ON((1ULL << (sizeof(k) << 3)) < LC_DILITHIUM_N);
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ polyz_pack(signature + i * LC_DILITHIUM_POLYZ_PACKEDBYTES,
+ &z->vec[i]);
+ signature += LC_DILITHIUM_L * LC_DILITHIUM_POLYZ_PACKEDBYTES;
+
+ /* Encode h */
+ memset(signature, 0, LC_DILITHIUM_OMEGA + LC_DILITHIUM_K);
+
+ k = 0;
+ for (i = 0; i < LC_DILITHIUM_K; ++i) {
+ for (j = 0; j < LC_DILITHIUM_N; ++j)
+ if (h->vec[i].coeffs[j] != 0)
+ signature[k++] = (uint8_t)j;
+
+ signature[LC_DILITHIUM_OMEGA + i] = (uint8_t)k;
+ }
+}
+
+/**
+ * @brief unpack_sig_z - Unpack z part of signature sig = (c, z, h).
+ *
+ * NOTE: The c value is not unpacked as it can be used right from the signature.
+ * To access it, a caller simply needs to use the first
+ * LC_DILITHIUM_CTILDE_BYTES of the signature.
+ *
+ * @param [out] z pointer to output vector z
+ * @param [in] sig signature
+ */
+static inline void
+unpack_sig_z(polyvecl *z, const struct lc_dilithium_sig *sig)
+{
+ unsigned int i;
+ /* Skip c */
+ const uint8_t *signature = sig->sig + LC_DILITHIUM_CTILDE_BYTES;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ polyz_unpack(&z->vec[i],
+ signature + i * LC_DILITHIUM_POLYZ_PACKEDBYTES);
+}
+
+/**
+ * @brief unpack_sig - Unpack h value of signature sig = (c, z, h).
+ *
+ * NOTE: The c value is not unpacked as it can be used right from the signature.
+ * To access it, a caller simply needs to use the first
+ * LC_DILITHIUM_CTILDE_BYTES of the signature.
+ *
+ * @param [out] h pointer to output hint vector h
+ * @param [in] sig signature
+ *
+ * @return 1 in case of malformed signature; otherwise 0.
+ */
+static inline int
+unpack_sig_h(polyveck *h, const struct lc_dilithium_sig *sig)
+{
+ unsigned int i, j, k;
+ /* Skip c */
+ const uint8_t *signature =
+ sig->sig + LC_DILITHIUM_CTILDE_BYTES +
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYZ_PACKEDBYTES;
+
+ /* Decode h */
+ k = 0;
+ for (i = 0; i < LC_DILITHIUM_K; ++i) {
+ for (j = 0; j < LC_DILITHIUM_N; ++j)
+ h->vec[i].coeffs[j] = 0;
+
+ if (signature[LC_DILITHIUM_OMEGA + i] < k ||
+ signature[LC_DILITHIUM_OMEGA + i] > LC_DILITHIUM_OMEGA)
+ return 1;
+
+ for (j = k; j < signature[LC_DILITHIUM_OMEGA + i]; ++j) {
+ /* Coefficients are ordered for strong unforgeability */
+ if (j > k && signature[j] <= signature[j - 1])
+ return 1;
+ h->vec[i].coeffs[signature[j]] = 1;
+ }
+
+ k = signature[LC_DILITHIUM_OMEGA + i];
+ }
+
+ /* Extra indices are zero for strong unforgeability */
+ for (j = k; j < LC_DILITHIUM_OMEGA; ++j)
+ if (signature[j])
+ return 1;
+
+ return 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_PACK_H */
diff --git a/lib/freebl/leancrypto/dilithium_pct.h b/lib/freebl/leancrypto/dilithium_pct.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_pct.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_PCT_H
+#define DILITHIUM_PCT_H
+
+#include "fips_mode.h"
+#include "ret_checkers.h"
+#include "small_stack_support.h"
+#include "visibility.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline int
+_lc_dilithium_pct_fips(const struct lc_dilithium_pk *pk,
+ const struct lc_dilithium_sk *sk)
+{
+ struct workspace {
+ uint8_t m[32];
+ struct lc_dilithium_sig sig;
+ };
+ int ret;
+ LC_DECLARE_MEM(ws, struct workspace, sizeof(uint64_t));
+
+ CKINT(lc_dilithium_sign(&ws->sig, ws->m, sizeof(ws->m), sk,
+ lc_seeded_rng));
+ CKINT(lc_dilithium_verify(&ws->sig, ws->m, sizeof(ws->m), pk));
+
+out:
+ LC_RELEASE_MEM(ws);
+ return ret;
+}
+
+static inline int
+lc_dilithium_pct_fips(const struct lc_dilithium_pk *pk,
+ const struct lc_dilithium_sk *sk)
+{
+ FIPS140_PCT_LOOP(_lc_dilithium_pct_fips(pk, sk))
+
+ return 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_PCT_H */
diff --git a/lib/freebl/leancrypto/dilithium_poly.h b/lib/freebl/leancrypto/dilithium_poly.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_poly.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_POLY_H
+#define DILITHIUM_POLY_H
+
+#include "dilithium_type.h"
+#include "dilithium_reduce.h"
+#include "dilithium_rounding.h"
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ int32_t coeffs[LC_DILITHIUM_N];
+} poly;
+
+/**
+ * @brief poly_add - Add polynomials. No modular reduction is performed.
+ *
+ * @param [out] c pointer to output polynomial
+ * @param [in] a pointer to first summand
+ * @param [in] b pointer to second summand
+ */
+static inline void
+poly_add(poly *c, const poly *a, const poly *b)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ c->coeffs[i] = a->coeffs[i] + b->coeffs[i];
+}
+
+/**
+ * @brief poly_sub - Subtract polynomials. No modular reduction is
+ * performed.
+ *
+ * @param [out] c pointer to output polynomial
+ * @param [in] a pointer to first input polynomial
+ * @param [in] b pointer to second input polynomial to be subtraced from first
+ * input polynomial
+ */
+static inline void
+poly_sub(poly *c, const poly *a, const poly *b)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ c->coeffs[i] = a->coeffs[i] - b->coeffs[i];
+}
+
+/**
+ * @brief poly_shiftl - Multiply polynomial by 2^D without modular reduction.
+ * Assumes input coefficients to be less than 2^{31-D} in
+ * absolute value.
+ *
+ * @param [in,out] a pointer to input/output polynomial
+ */
+static inline void
+poly_shiftl(poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ a->coeffs[i] <<= LC_DILITHIUM_D;
+}
+
+/**
+ * @brief poly_decompose - For all coefficients c of the input polynomial,
+ * compute high and low bits c0, c1 such
+ * c mod Q = c1*ALPHA + c0 with
+ * -ALPHA/2 < c0 <= ALPHA/2 except c1 = (Q-1)/ALPHA
+ * where we set c1 = 0 and
+ * -ALPHA/2 <= c0 = c mod Q - Q < 0.
+ * Assumes coefficients to be standard representatives.
+ *
+ * @param [out] a1 pointer to output polynomial with coefficients c1
+ * @param [out] a0 pointer to output polynomial with coefficients c0
+ * @param [in] a pointer to input polynomial
+ */
+static inline void
+poly_decompose(poly *a1, poly *a0, const poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ a1->coeffs[i] = decompose(&a0->coeffs[i], a->coeffs[i]);
+}
+
+/**
+ * @brief poly_make_hint - Compute hint polynomial. The coefficients of which
+ * indicate whether the low bits of the corresponding
+ * coefficient of the input polynomial overflow into the
+ * high bits.
+ *
+ * @param [out] h pointer to output hint polynomial
+ * @param [in] a0 pointer to low part of input polynomial
+ * @param [in] a1 pointer to high part of input polynomial
+ *
+ * @return number of 1 bits.
+ */
+static inline unsigned int
+poly_make_hint(poly *h, const poly *a0,
+ const poly *a1)
+{
+ unsigned int i, s = 0;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i) {
+ h->coeffs[i] = make_hint(a0->coeffs[i], a1->coeffs[i]);
+ s += (unsigned int)h->coeffs[i];
+ }
+
+ return s;
+}
+
+/**
+ * @brief poly_use_hint - Use hint polynomial to correct the high bits of a
+ * polynomial.
+ *
+ * @param [out] b pointer to output polynomial with corrected high bits
+ * @param [in] a pointer to input polynomial
+ * @param [in] h pointer to input hint polynomial
+ */
+static inline void
+poly_use_hint(poly *b, const poly *a, const poly *h)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ b->coeffs[i] = use_hint(a->coeffs[i], h->coeffs[i]);
+}
+
+int poly_chknorm(const poly *a, int32_t B);
+
+#define POLY_UNIFORM_NBLOCKS \
+ ((768 + LC_SHAKE_128_SIZE_BLOCK - 1) / LC_SHAKE_128_SIZE_BLOCK)
+void poly_uniform(poly *a, const uint8_t seed[LC_DILITHIUM_SEEDBYTES],
+ uint16_t nonce, void *ws_buf);
+
+#if LC_DILITHIUM_ETA == 2
+#define POLY_UNIFORM_ETA_NBLOCKS \
+ ((136 + LC_SHAKE_256_SIZE_BLOCK - 1) / LC_SHAKE_256_SIZE_BLOCK)
+#elif LC_DILITHIUM_ETA == 4
+#define POLY_UNIFORM_ETA_NBLOCKS \
+ ((227 + LC_SHAKE_256_SIZE_BLOCK - 1) / LC_SHAKE_256_SIZE_BLOCK)
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+#define POLY_UNIFORM_ETA_BYTES POLY_UNIFORM_ETA_NBLOCKS *LC_SHAKE_256_SIZE_BLOCK
+void poly_uniform_eta(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf);
+
+#define POLY_UNIFORM_GAMMA1_NBLOCKS \
+ ((LC_DILITHIUM_POLYZ_PACKEDBYTES + LC_SHAKE_256_SIZE_BLOCK - 1) / \
+ LC_SHAKE_256_SIZE_BLOCK)
+#define POLY_UNIFORM_GAMMA1_BYTES \
+ POLY_UNIFORM_GAMMA1_NBLOCKS *LC_SHAKE_256_SIZE_BLOCK
+
+#define POLY_CHALLENGE_BYTES LC_SHAKE_256_SIZE_BLOCK
+void poly_challenge(poly *c, const uint8_t seed[LC_DILITHIUM_CTILDE_BYTES],
+ void *ws_buf);
+
+void polyeta_pack(uint8_t *r, const poly *a);
+void polyeta_unpack(poly *r, const uint8_t *a);
+
+void polyt1_pack(uint8_t *r, const poly *a);
+
+void polyt0_pack(uint8_t *r, const poly *a);
+void polyt0_unpack(poly *r, const uint8_t *a);
+
+void polyz_pack(uint8_t *r, const poly *a);
+void polyz_unpack(poly *r, const uint8_t *a);
+
+void polyw1_pack(uint8_t *r, const poly *a);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_POLY_H */
diff --git a/lib/freebl/leancrypto/dilithium_poly_c.h b/lib/freebl/leancrypto/dilithium_poly_c.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_poly_c.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2023 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_POLY_C_H
+#define DILITHIUM_POLY_C_H
+
+#include "dilithium_ntt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief poly_reduce - Inplace reduction of all coefficients of polynomial to
+ * representative in [-6283009,6283007].
+ *
+ * @param [in,out] a pointer to input/output polynomial
+ */
+static inline void
+poly_reduce(poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ a->coeffs[i] = reduce32(a->coeffs[i]);
+}
+
+/**
+ * @brief poly_caddq - For all coefficients of in/out polynomial add Q if
+ * coefficient is negative.
+ *
+ * @param [in,out] a pointer to input/output polynomial
+ */
+static inline void
+poly_caddq(poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ a->coeffs[i] = caddq(a->coeffs[i]);
+}
+
+/**
+ * @brief poly_pointwise_montgomery - Pointwise multiplication of polynomials in
+ * NTT domain representation and
+ * multiplication of resulting polynomial
+ * by 2^{-32}.
+ *
+ * @param [out] c pointer to output polynomial
+ * @param [in] a pointer to first input polynomial
+ * @param [in] b pointer to second input polynomial
+ */
+static inline void
+poly_pointwise_montgomery(poly *c, const poly *a,
+ const poly *b)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ c->coeffs[i] =
+ montgomery_reduce((int64_t)a->coeffs[i] * b->coeffs[i]);
+}
+
+/**
+ * @brief poly_power2round - For all coefficients c of the input polynomial,
+ * compute c0, c1 such that c mod Q = c1*2^D + c0
+ * with -2^{D-1} < c0 <= 2^{D-1}. Assumes coefficients
+ * to be standard representatives.
+ *
+ * @param [out] a1 pointer to output polynomial with coefficients c1
+ * @param [out] a0 pointer to output polynomial with coefficients c0
+ * @param [in] a pointer to input polynomial
+ */
+static inline void
+poly_power2round(poly *a1, poly *a0, const poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ a1->coeffs[i] = power2round(&a0->coeffs[i], a->coeffs[i]);
+}
+
+/**
+ * @brief polyt1_unpack - Unpack polynomial t1 with 10-bit coefficients.
+ * Output coefficients are standard representatives.
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+static inline void
+polyt1_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r->coeffs[4 * i + 0] =
+ ((a[5 * i + 0] >> 0) | ((uint32_t)a[5 * i + 1] << 8)) &
+ 0x3FF;
+ r->coeffs[4 * i + 1] =
+ ((a[5 * i + 1] >> 2) | ((uint32_t)a[5 * i + 2] << 6)) &
+ 0x3FF;
+ r->coeffs[4 * i + 2] =
+ ((a[5 * i + 2] >> 4) | ((uint32_t)a[5 * i + 3] << 4)) &
+ 0x3FF;
+ r->coeffs[4 * i + 3] =
+ ((a[5 * i + 3] >> 6) | ((uint32_t)a[5 * i + 4] << 2)) &
+ 0x3FF;
+ }
+}
+
+/**
+ * @brief poly_ntt - Inplace forward NTT. Coefficients can grow by
+ * 8*Q in absolute value.
+ *
+ * @param [in,out] a pointer to input/output polynomial
+ */
+static inline void
+poly_ntt(poly *a)
+{
+ ntt(a->coeffs);
+}
+
+/**
+ * @brief poly_invntt_tomont - Inplace inverse NTT and multiplication by 2^{32}.
+ * Input coefficients need to be less than Q in
+ * absolute value and output coefficients are again
+ * bounded by Q.
+ *
+ * @param [in,out] a pointer to input/output polynomial
+ */
+static inline void
+poly_invntt_tomont(poly *a)
+{
+ invntt_tomont(a->coeffs);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_POLY_C_H */
diff --git a/lib/freebl/leancrypto/dilithium_poly_common.h b/lib/freebl/leancrypto/dilithium_poly_common.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_poly_common.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_POLY_COMMON_H
+#define DILITHIUM_POLY_COMMON_H
+
+#include "dilithium_type.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void poly_uniform_gamma1(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_POLY_COMMON_H */
diff --git a/lib/freebl/leancrypto/dilithium_polyvec.h b/lib/freebl/leancrypto/dilithium_polyvec.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_polyvec.h
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_POLYVEC_H
+#define DILITHIUM_POLYVEC_H
+
+#include "conv_be_le.h"
+#include "dilithium_type.h"
+#include "dilithium_poly.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ poly vec[LC_DILITHIUM_L];
+} polyvecl;
+
+/* Vectors of polynomials of length K */
+typedef struct {
+ poly vec[LC_DILITHIUM_K];
+} polyveck;
+
+/**************************************************************/
+/************ Vectors of polynomials of length L **************/
+/**************************************************************/
+
+static inline void
+polyvecl_reduce(polyvecl *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ poly_reduce(&v->vec[i]);
+}
+
+/**
+ * @brief polyvecl_add - Add vectors of polynomials of length L.
+ * No modular reduction is performed.
+ *
+ * @param [out] w pointer to output vector
+ * @param [in] u pointer to first summand
+ * @param [in] v pointer to second summand
+ */
+static inline void
+polyvecl_add(polyvecl *w, const polyvecl *u,
+ const polyvecl *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ poly_add(&w->vec[i], &u->vec[i], &v->vec[i]);
+}
+
+/**
+ * @brief polyvecl_ntt - Forward NTT of all polynomials in vector of length L.
+ * Output coefficients can be up to 16*Q larger than input
+ * coefficients.
+ *
+ * @param [in,out] v pointer to input/output vector
+ */
+static inline void
+polyvecl_ntt(polyvecl *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ poly_ntt(&v->vec[i]);
+}
+
+static inline void
+polyvecl_invntt_tomont(polyvecl *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ poly_invntt_tomont(&v->vec[i]);
+}
+
+static inline void
+polyvecl_pointwise_poly_montgomery(polyvecl *r,
+ const poly *a,
+ const polyvecl *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ poly_pointwise_montgomery(&r->vec[i], a, &v->vec[i]);
+}
+
+/**
+ * @brief polyvecl_chknorm - Check infinity norm of polynomials in vector of
+ * length L. Assumes input polyvecl to be reduced by
+ * polyvecl_reduce().
+ *
+ * @param [in] v pointer to vector
+ * @param [in] bound norm bound
+ *
+ * @return 0 if norm of all polynomials is strictly smaller than B <= (Q-1)/8
+ * and 1 otherwise.
+ */
+static inline int
+polyvecl_chknorm(const polyvecl *v, int32_t bound)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ if (poly_chknorm(&v->vec[i], bound))
+ return 1;
+
+ return 0;
+}
+
+/**************************************************************/
+/************ Vectors of polynomials of length K **************/
+/**************************************************************/
+
+/**
+ * @brief polyveck_reduce - Reduce coefficients of polynomials in vector of
+ * length LC_DILITHIUM_K to representatives in
+ * [-6283009,6283007].
+ *
+ * @param [in,out] v pointer to input/output vector
+ */
+static inline void
+polyveck_reduce(polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_reduce(&v->vec[i]);
+}
+
+/**
+ * @brief polyveck_caddq - For all coefficients of polynomials in vector of
+ * length LC_DILITHIUM_K add LC_DILITHIUM_Q if
+ * coefficient is negative.
+ *
+ * @param [in,out] v pointer to input/output vector
+ */
+static inline void
+polyveck_caddq(polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_caddq(&v->vec[i]);
+}
+
+/**
+ * @brief polyveck_add - Add vectors of polynomials of length LC_DILITHIUM_K.
+ * No modular reduction is performed.
+ *
+ * @param [out] w pointer to output vector
+ * @param [in] u pointer to first summand
+ * @param [in] v pointer to second summand
+ */
+static inline void
+polyveck_add(polyveck *w, const polyveck *u,
+ const polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_add(&w->vec[i], &u->vec[i], &v->vec[i]);
+}
+
+/**
+ * @brief olyveck_sub - Subtract vectors of polynomials of length
+ * LC_DILITHIUM_K. No modular reduction is performed.
+ *
+ * @param [out] w pointer to output vector
+ * @param [in] u pointer to first input vector
+ * @param [in] v pointer to second input vector to be subtracted from first
+ * input vector
+ */
+static inline void
+polyveck_sub(polyveck *w, const polyveck *u,
+ const polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_sub(&w->vec[i], &u->vec[i], &v->vec[i]);
+}
+
+/**
+ * @brief polyveck_shiftl - Multiply vector of polynomials of Length K by
+ * 2^D without modular reduction. Assumes input
+ * coefficients to be less than 2^{31-D}.
+ *
+ * @param [in,out] v pointer to input/output vector
+ */
+static inline void
+polyveck_shiftl(polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_shiftl(&v->vec[i]);
+}
+
+/**
+ * @brief polyveck_ntt - Forward NTT of all polynomials in vector of length K.
+ * Output coefficients can be up to 16*Q larger than input
+ * coefficients.
+ *
+ * @param [in,out] v pointer to input/output vector
+ */
+static inline void
+polyveck_ntt(polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_ntt(&v->vec[i]);
+}
+
+/**
+ * @brief polyveck_invntt_tomont - Inverse NTT and multiplication by 2^{32} of
+ * polynomials in vector of length K. Input
+ * coefficients need to be less than 2*Q.
+ *
+ * @param [in,out] v pointer to input/output vector
+ */
+static inline void
+polyveck_invntt_tomont(polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_invntt_tomont(&v->vec[i]);
+}
+
+static inline void
+polyveck_pointwise_poly_montgomery(polyveck *r,
+ const poly *a,
+ const polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_pointwise_montgomery(&r->vec[i], a, &v->vec[i]);
+}
+
+/**
+ * @brief polyveck_chknorm - Check infinity norm of polynomials in vector of
+ * length K. Assumes input polyveck to be reduced by
+ * polyveck_reduce().
+ *
+ * @param [in] v pointer to vector
+ * @param [in] bound norm bound
+ *
+ * @return 0 if norm of all polynomials are strictly smaller than B <= (Q-1)/8
+ * and 1 otherwise.
+ */
+static inline int
+polyveck_chknorm(const polyveck *v, int32_t bound)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ if (poly_chknorm(&v->vec[i], bound))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * @brief polyveck_power2round - For all coefficients a of polynomials in vector
+ * of length K, compute a0, a1 such that
+ * a mod^+ Q = a1*2^D + a0 with
+ * -2^{D-1} < a0 <= 2^{D-1}. Assumes coefficients
+ * to be standard representatives.
+ *
+ * @param [out] v1 pointer to output vector of polynomials with coefficients a1
+ * @param [in] v0 pointer to output vector of polynomials with coefficients a0
+ * @param [in] v pointer to input vector
+ */
+static inline void
+polyveck_power2round(polyveck *v1, polyveck *v0,
+ const polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_power2round(&v1->vec[i], &v0->vec[i], &v->vec[i]);
+}
+
+/**
+ * @brief polyveck_decompose - For all coefficients a of polynomials in vector
+ * of length K, compute high and low bits a0, a1
+ * such a mod^+ Q = a1*ALPHA + a0 with
+ * -ALPHA/2 < a0 <= ALPHA/2 except a1 = (Q-1)/ALPHA
+ * where we set a1 = 0 and
+ * -ALPHA/2 <= a0 = a mod Q - Q < 0. Assumes
+ * coefficients to be standard representatives.
+ *
+ * @param [out] v1 pointer to output vector of polynomials with coefficients a1
+ * @param [in] v0 pointer to output vector of polynomials with coefficients a0
+ * @param [in] v pointer to input vector
+ */
+static inline void
+polyveck_decompose(polyveck *v1, polyveck *v0,
+ const polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_decompose(&v1->vec[i], &v0->vec[i], &v->vec[i]);
+}
+
+/**
+ * @brief polyveck_make_hint - Compute hint vector.
+ *
+ * @param [out] h pointer to output vector
+ * @param [in] v0 pointer to low part of input vector
+ * @param [in] v1 pointer to high part of input vector
+ *
+ * @return number of 1 bits.
+ */
+static inline unsigned int
+polyveck_make_hint(polyveck *h, const polyveck *v0,
+ const polyveck *v1)
+{
+ unsigned int i, s = 0;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ s += poly_make_hint(&h->vec[i], &v0->vec[i], &v1->vec[i]);
+
+ return s;
+}
+
+/**
+ * @brief polyveck_use_hint - Use hint vector to correct the high bits of input
+ * vector.
+ *
+ * @param [out] w pointer to output vector of polynomials with corrected high
+ * bits
+ * @param [in] u pointer to input vector
+ * @param [in] h pointer to input hint vector
+ */
+static inline void
+polyveck_use_hint(polyveck *w, const polyveck *u,
+ const polyveck *h)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_use_hint(&w->vec[i], &u->vec[i], &h->vec[i]);
+}
+
+static inline void
+polyveck_pack_w1(uint8_t r[LC_DILITHIUM_K * LC_DILITHIUM_POLYW1_PACKEDBYTES],
+ const polyveck *w1)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyw1_pack(&r[i * LC_DILITHIUM_POLYW1_PACKEDBYTES],
+ &w1->vec[i]);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_POLYVEC_H */
diff --git a/lib/freebl/leancrypto/dilithium_polyvec_c.h b/lib/freebl/leancrypto/dilithium_polyvec_c.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_polyvec_c.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2023 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_POLYVEC_C_H
+#define DILITHIUM_POLYVEC_C_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline void
+polyvecl_uniform_eta(polyvecl *v, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ poly_uniform_eta(&v->vec[i], seed, le_bswap16(nonce++), ws_buf);
+}
+
+static inline void
+polyvecl_uniform_gamma1(polyvecl *v, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ poly_uniform_gamma1(
+ &v->vec[i], seed,
+ le_bswap16((uint16_t)(LC_DILITHIUM_L * nonce + i)),
+ ws_buf);
+}
+
+static inline void
+polyveck_uniform_eta(polyveck *v, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_uniform_eta(&v->vec[i], seed, le_bswap16(nonce++), ws_buf);
+}
+
+/**
+ * @brief expand_mat - Implementation of ExpandA. Generates matrix A with
+ * uniformly random coefficients a_{i,j} by performing
+ * rejection sampling on the output stream of
+ * SHAKE128(rho|j|i).
+ *
+ * @param [out] mat output matrix
+ * @param [in] rho byte array containing seed rho
+ */
+static inline void
+polyvec_matrix_expand(polyvecl mat[LC_DILITHIUM_K],
+ const uint8_t rho[LC_DILITHIUM_SEEDBYTES], void *ws_buf)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ for (j = 0; j < LC_DILITHIUM_L; ++j)
+ poly_uniform(
+ &mat[i].vec[j], rho,
+ le_bswap16((uint16_t)(i << 8) + (uint16_t)j),
+ ws_buf);
+}
+
+/**
+ * @brief polyvecl_pointwise_acc_montgomery -
+ * Pointwise multiply vectors of polynomials of length L, multiply
+ * resulting vector by 2^{-32} and add (accumulate) polynomials
+ * in it. Input/output vectors are in NTT domain representation.
+ *
+ * @param [out] w output polynomial
+ * @param [in] u pointer to first input vector
+ * @param [in] v pointer to second input vector
+ */
+static inline void
+polyvecl_pointwise_acc_montgomery(poly *w, const polyvecl *u,
+ const polyvecl *v,
+ void *ws_buf)
+{
+ unsigned int i;
+ poly *t = ws_buf;
+
+ poly_pointwise_montgomery(w, &u->vec[0], &v->vec[0]);
+ for (i = 1; i < LC_DILITHIUM_L; ++i) {
+ poly_pointwise_montgomery(t, &u->vec[i], &v->vec[i]);
+ poly_add(w, w, t);
+ }
+}
+
+static inline void
+polyvec_matrix_pointwise_montgomery(polyveck *t,
+ const polyvecl mat[LC_DILITHIUM_K],
+ const polyvecl *v, void *ws_buf)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyvecl_pointwise_acc_montgomery(&t->vec[i], &mat[i], v,
+ ws_buf);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_POLYVEC_C_H */
diff --git a/lib/freebl/leancrypto/dilithium_reduce.h b/lib/freebl/leancrypto/dilithium_reduce.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_reduce.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_REDUCE_H
+#define DILITHIUM_REDUCE_H
+
+#include "dilithium_type.h"
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MONT -4186625 // 2^32 % Q
+#define QINV 58728449 // q^(-1) mod 2^32
+
+/**
+ * @brief montgomery_reduce - For finite field element a with
+ * -2^{31}Q <= a <= Q*2^31,
+ * compute r \equiv a*2^{-32} (mod Q) such that
+ * -Q < r < Q.
+ *
+ * @param [in] a finite field element
+ *
+ * @return r
+ */
+static inline int32_t
+montgomery_reduce(int64_t a)
+{
+ int32_t t;
+
+ t = (int32_t)(a * QINV);
+ t = (int32_t)((a - (int64_t)t * LC_DILITHIUM_Q) >> 32);
+ return t;
+}
+
+/**
+ * @brief reduce32 - For finite field element a with a <= 2^{31} - 2^{22} - 1,
+ * compute r \equiv a (mod Q) such that
+ * -6283009 <= r <= 6283007.
+ *
+ * @param [in] a finite field element
+ *
+ * @return r
+ */
+static inline int32_t
+reduce32(int32_t a)
+{
+ int32_t t;
+
+ t = (a + (1 << 22)) >> 23;
+ t = a - t * LC_DILITHIUM_Q;
+ return t;
+}
+
+/**
+ * @brief caddq - Add Q if input coefficient is negative.
+ *
+ * @param [in] a finite field element
+ *
+ * @return r
+ */
+static inline int32_t
+caddq(int32_t a)
+{
+ a += (a >> 31) & LC_DILITHIUM_Q;
+ return a;
+}
+
+/**
+ * @brief freeze - For finite field element a, compute standard representative
+ * r = a mod^+ Q.
+ *
+ * @param [in] a finite field element a
+ *
+ * @return r
+ */
+static inline int32_t
+freeze(int32_t a)
+{
+ a = reduce32(a);
+ a = caddq(a);
+ return a;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_REDUCE_H */
diff --git a/lib/freebl/leancrypto/dilithium_rounding.h b/lib/freebl/leancrypto/dilithium_rounding.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_rounding.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_ROUNDING_H
+#define DILITHIUM_ROUNDING_H
+
+#include "dilithium_type.h"
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int32_t power2round(int32_t *a0, int32_t a);
+int32_t decompose(int32_t *a0, int32_t a);
+int32_t make_hint(int32_t a0, int32_t a1);
+int32_t use_hint(int32_t a, int32_t hint);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_ROUNDING_H */
diff --git a/lib/freebl/leancrypto/dilithium_service_helpers.h b/lib/freebl/leancrypto/dilithium_service_helpers.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_service_helpers.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_SERVICE_HELPERS_H
+#define DILITHIUM_SERVICE_HELPERS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief rej_uniform - Sample uniformly random coefficients in [0, Q-1] by
+ * performing rejection sampling on array of random bytes.
+ *
+ * @param [out] a pointer to output array (allocated)
+ * @param [in] len number of coefficients to be sampled
+ * @param [in] buf array of random bytes
+ * @param [in] buflen length of array of random bytes
+ *
+ * @return number of sampled coefficients. Can be smaller than len if not enough
+ * random bytes were given.
+ */
+static inline unsigned int
+rej_uniform(int32_t *a, unsigned int len,
+ const uint8_t *buf, unsigned int buflen)
+{
+ unsigned int ctr, pos;
+ uint32_t t;
+
+ ctr = pos = 0;
+ while (ctr < len && pos + 3 <= buflen) {
+ t = buf[pos++];
+ t |= (uint32_t)buf[pos++] << 8;
+ t |= (uint32_t)buf[pos++] << 16;
+ t &= 0x7FFFFF;
+
+ if (t < LC_DILITHIUM_Q)
+ a[ctr++] = (int32_t)t;
+ }
+
+ return ctr;
+}
+
+/**
+ * @brief rej_eta - Sample uniformly random coefficients in [-ETA, ETA] by
+ * performing rejection sampling on array of random bytes.
+ *
+ * @param [out] a pointer to output array (allocated)
+ * @param [in] len number of coefficients to be sampled
+ * @param [in] buf array of random bytes
+ * @param [in] buflen length of array of random bytes
+ *
+ * @return number of sampled coefficients. Can be smaller than len if not enough
+ * random bytes were given.
+ */
+static inline unsigned int
+rej_eta(int32_t *a, unsigned int len,
+ const uint8_t *buf, unsigned int buflen)
+{
+ unsigned int ctr, pos;
+ int32_t t0, t1;
+
+ ctr = pos = 0;
+ while (ctr < len && pos < buflen) {
+ t0 = buf[pos] & 0x0F;
+ t1 = buf[pos++] >> 4;
+
+#if LC_DILITHIUM_ETA == 2
+ if (t0 < 15) {
+ t0 = t0 - (205 * t0 >> 10) * 5;
+ a[ctr++] = 2 - t0;
+ }
+ if (t1 < 15 && ctr < len) {
+ t1 = t1 - (205 * t1 >> 10) * 5;
+ a[ctr++] = 2 - t1;
+ }
+#elif LC_DILITHIUM_ETA == 4
+ if (t0 < 9)
+ a[ctr++] = 4 - t0;
+ if (t1 < 9 && ctr < len)
+ a[ctr++] = 4 - t1;
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+ }
+
+ return ctr;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_SERVICE_HELPERS_H */
diff --git a/lib/freebl/leancrypto/dilithium_signature_c.h b/lib/freebl/leancrypto/dilithium_signature_c.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_signature_c.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_SIGNATURE_C_H
+#define DILITHIUM_SIGNATURE_C_H
+
+#include "dilithium_type.h"
+#include "lc_rng.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int lc_dilithium_keypair_c(struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+int lc_dilithium_keypair_from_seed_c(struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+int lc_dilithium_sign_c(struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+int lc_dilithium_sign_ctx_c(struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+int lc_dilithium_sign_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk);
+int lc_dilithium_sign_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_sign_final_c(struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_verify_c(const struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk);
+int lc_dilithium_verify_ctx_c(const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk);
+int lc_dilithium_verify_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk);
+int lc_dilithium_verify_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_verify_final_c(const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_SIGNATURE_C_H */
diff --git a/lib/freebl/leancrypto/dilithium_signature_impl.h b/lib/freebl/leancrypto/dilithium_signature_impl.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_signature_impl.h
@@ -0,0 +1,1039 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_SIGNATURE_IMPL_H
+#define DILITHIUM_SIGNATURE_IMPL_H
+
+#include "alignment.h"
+#include "build_bug_on.h"
+#include "dilithium_type.h"
+#include "dilithium_debug.h"
+#include "dilithium_pack.h"
+#include "lc_hash.h"
+#include "lc_memcmp_secure.h"
+#include "lc_sha3.h"
+#include "ret_checkers.h"
+#include "signature_domain_separation.h"
+#include "small_stack_support.h"
+#include "static_rng.h"
+#include "timecop.h"
+#include "visibility.h"
+#include "binhexbin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Enable this macro to report the rejection code paths taken with the
+ * signature generation operation. When disabled, the compiler should
+ * eliminate this code which means that the counting code is folded away.
+ */
+#undef REJECTION_TEST_SAMPLING
+
+#define _WS_POLY_UNIFORM_BUF_SIZE \
+ (POLY_UNIFORM_NBLOCKS * LC_SHAKE_128_SIZE_BLOCK + 2)
+
+#ifndef LC_POLY_UNIFOR_BUF_SIZE_MULTIPLIER
+#error "LC_POLY_UNIFOR_BUF_SIZE_MULTIPLIER is not defined"
+#endif
+
+#define WS_POLY_UNIFORM_BUF_SIZE \
+ (_WS_POLY_UNIFORM_BUF_SIZE * LC_POLY_UNIFOR_BUF_SIZE_MULTIPLIER)
+
+static int lc_dilithium_keypair_from_seed_impl(struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk,
+ const uint8_t *seed,
+ size_t seedlen);
+
+static int
+lc_dilithium_keypair_impl(struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ struct workspace {
+ union {
+ polyvecl s1, s1hat;
+ } s1;
+ union {
+ polyvecl mat[LC_DILITHIUM_K];
+ polyveck t0;
+ } matrix;
+ polyveck s2, t1;
+ uint8_t seedbuf[2 * LC_DILITHIUM_SEEDBYTES +
+ LC_DILITHIUM_CRHBYTES];
+ union {
+ poly polyvecl_pointwise_acc_montgomery_buf;
+ uint8_t poly_uniform_buf[WS_POLY_UNIFORM_BUF_SIZE];
+ uint8_t poly_uniform_eta_buf[POLY_UNIFORM_ETA_BYTES];
+ uint8_t tr[LC_DILITHIUM_TRBYTES];
+ } tmp;
+ };
+ static const uint8_t dimension[2] = { LC_DILITHIUM_K, LC_DILITHIUM_L };
+ const uint8_t *rho, *rhoprime, *key;
+ int ret;
+ LC_HASH_CTX_ON_STACK(shake256_ctx, lc_shake256);
+ LC_DECLARE_MEM(ws, struct workspace, sizeof(uint64_t));
+
+ CKNULL(pk, -EINVAL);
+ CKNULL(sk, -EINVAL);
+
+ lc_rng_check(&rng_ctx);
+
+ /* Get randomness for rho, rhoprime and key */
+ CKINT(lc_rng_generate(rng_ctx, NULL, 0, ws->seedbuf,
+ LC_DILITHIUM_SEEDBYTES));
+ dilithium_print_buffer(ws->seedbuf, LC_DILITHIUM_SEEDBYTES,
+ "Keygen - Seed");
+
+ lc_hash_init(shake256_ctx);
+ lc_hash_update(shake256_ctx, ws->seedbuf, LC_DILITHIUM_SEEDBYTES);
+ lc_hash_update(shake256_ctx, dimension, sizeof(dimension));
+ lc_hash_set_digestsize(shake256_ctx, sizeof(ws->seedbuf));
+ lc_hash_final(shake256_ctx, ws->seedbuf);
+ lc_hash_zero(shake256_ctx);
+
+ rho = ws->seedbuf;
+ dilithium_print_buffer(ws->seedbuf, LC_DILITHIUM_SEEDBYTES,
+ "Keygen - RHO");
+ pack_pk_rho(pk, rho);
+ pack_sk_rho(sk, rho);
+
+ /*
+ * Timecop: RHO' is a random number which is enlarged to sample the
+ * vectors S1 and S2 from. The sampling operation is not considered
+ * relevant for the side channel operation as (a) an attacker does not
+ * have access to the random number and (b) only the result after the
+ * sampling operation of S1 and S2 is released.
+ */
+ rhoprime = rho + LC_DILITHIUM_SEEDBYTES;
+ dilithium_print_buffer(rhoprime, LC_DILITHIUM_CRHBYTES,
+ "Keygen - RHOPrime");
+
+ key = rhoprime + LC_DILITHIUM_CRHBYTES;
+ dilithium_print_buffer(key, LC_DILITHIUM_SEEDBYTES, "Keygen - Key");
+
+ /* Timecop: key goes into the secret key */
+ poison(key, LC_DILITHIUM_SEEDBYTES);
+
+ pack_sk_key(sk, key);
+
+ /* Sample short vectors s1 and s2 */
+
+ polyvecl_uniform_eta(&ws->s1.s1, rhoprime, 0,
+ ws->tmp.poly_uniform_eta_buf);
+ polyveck_uniform_eta(&ws->s2, rhoprime, LC_DILITHIUM_L,
+ ws->tmp.poly_uniform_eta_buf);
+
+ /* Timecop: s1 and s2 are secret */
+ poison(&ws->s1.s1, sizeof(polyvecl));
+ poison(&ws->s2, sizeof(polyveck));
+
+ dilithium_print_polyvecl(&ws->s1.s1,
+ "Keygen - S1 L x N matrix after ExpandS:");
+ dilithium_print_polyveck(&ws->s2,
+ "Keygen - S2 K x N matrix after ExpandS:");
+
+ pack_sk_s1(sk, &ws->s1.s1);
+ pack_sk_s2(sk, &ws->s2);
+
+ polyvecl_ntt(&ws->s1.s1hat);
+ dilithium_print_polyvecl(&ws->s1.s1hat,
+ "Keygen - S1 L x N matrix after NTT:");
+
+ /* Expand matrix */
+ polyvec_matrix_expand(ws->matrix.mat, rho, ws->tmp.poly_uniform_buf);
+ dilithium_print_polyvecl_k(
+ ws->matrix.mat, "Keygen - MAT K x L x N matrix after ExpandA:");
+
+ polyvec_matrix_pointwise_montgomery(
+ &ws->t1, ws->matrix.mat, &ws->s1.s1hat,
+ &ws->tmp.polyvecl_pointwise_acc_montgomery_buf);
+ dilithium_print_polyveck(&ws->t1,
+ "Keygen - T K x N matrix after A*NTT(s1):");
+
+ polyveck_reduce(&ws->t1);
+ dilithium_print_polyveck(
+ &ws->t1, "Keygen - T K x N matrix reduce after A*NTT(s1):");
+
+ polyveck_invntt_tomont(&ws->t1);
+ dilithium_print_polyveck(&ws->t1,
+ "Keygen - T K x N matrix after NTT-1:");
+
+ /* Add error vector s2 */
+ polyveck_add(&ws->t1, &ws->t1, &ws->s2);
+ dilithium_print_polyveck(&ws->t1,
+ "Keygen - T K x N matrix after add S2:");
+
+ /* Extract t1 and write public key */
+ polyveck_caddq(&ws->t1);
+ dilithium_print_polyveck(&ws->t1, "Keygen - T K x N matrix caddq:");
+
+ polyveck_power2round(&ws->t1, &ws->matrix.t0, &ws->t1);
+ dilithium_print_polyveck(&ws->matrix.t0,
+ "Keygen - T0 K x N matrix after power2round:");
+ dilithium_print_polyveck(&ws->t1,
+ "Keygen - T1 K x N matrix after power2round:");
+
+ pack_sk_t0(sk, &ws->matrix.t0);
+ pack_pk_t1(pk, &ws->t1);
+ dilithium_print_buffer(pk->pk, LC_DILITHIUM_PUBLICKEYBYTES,
+ "Keygen - PK after pkEncode:");
+
+ /* Compute H(rho, t1) and write secret key */
+ lc_xof(lc_shake256, pk->pk, sizeof(pk->pk), ws->tmp.tr,
+ sizeof(ws->tmp.tr));
+ dilithium_print_buffer(ws->tmp.tr, sizeof(ws->tmp.tr), "Keygen - TR:");
+ pack_sk_tr(sk, ws->tmp.tr);
+
+ dilithium_print_buffer(sk->sk, LC_DILITHIUM_SECRETKEYBYTES,
+ "Keygen - SK:");
+
+ /* Timecop: pk and sk are not relevant for side-channels any more. */
+ unpoison(pk->pk, sizeof(pk->pk));
+ unpoison(sk->sk, sizeof(sk->sk));
+
+out:
+ LC_RELEASE_MEM(ws);
+ return ret;
+}
+
+static int
+lc_dilithium_keypair_from_seed_impl(struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk,
+ const uint8_t *seed,
+ size_t seedlen)
+{
+ struct lc_static_rng_data s_rng_state;
+ LC_STATIC_DRNG_ON_STACK(s_drng, &s_rng_state);
+ int ret;
+
+ if (seedlen != LC_DILITHIUM_SEEDBYTES)
+ return -EINVAL;
+
+ /* Set the seed that the key generation can pull via the RNG. */
+ s_rng_state.seed = seed;
+ s_rng_state.seedlen = seedlen;
+
+ /* Generate the key pair from the seed. */
+ CKINT(lc_dilithium_keypair_impl(pk, sk, &s_drng));
+
+out:
+ return ret;
+}
+
+static int
+lc_dilithium_sign_internal_ahat(struct lc_dilithium_sig *sig,
+ const struct lc_dilithium_sk *sk,
+ struct lc_dilithium_ctx *ctx,
+ struct lc_rng_ctx *rng_ctx)
+{
+ struct workspace_sign {
+ polyvecl s1, y, z;
+ polyveck t0, s2, w1, w0, h;
+ poly cp;
+ uint8_t seedbuf[LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_RNDBYTES +
+ LC_DILITHIUM_CRHBYTES];
+ union {
+ uint8_t poly_uniform_gamma1_buf[WS_POLY_UNIFORM_BUF_SIZE];
+ uint8_t poly_challenge_buf[POLY_CHALLENGE_BYTES];
+ } tmp;
+ };
+ unsigned int n;
+ uint8_t *key, *mu, *rhoprime, *rnd;
+ const polyvecl *mat = ctx->ahat;
+ uint16_t nonce = 0;
+ int ret = 0;
+ struct lc_hash_ctx *hash_ctx = &ctx->dilithium_hash_ctx;
+ uint8_t __maybe_unused rej_total = 0;
+ LC_DECLARE_MEM(ws, struct workspace_sign, sizeof(uint64_t));
+
+ /* AHat must be present at this time */
+ CKNULL(mat, -EINVAL);
+
+ key = ws->seedbuf;
+ rnd = key + LC_DILITHIUM_SEEDBYTES;
+ mu = rnd + LC_DILITHIUM_RNDBYTES;
+
+ /*
+ * If the external mu is provided, use this verbatim, otherwise
+ * calculate the mu value.
+ */
+ if (ctx->external_mu) {
+ if (ctx->external_mu_len != LC_DILITHIUM_CRHBYTES)
+ return -EINVAL;
+ memcpy(mu, ctx->external_mu, LC_DILITHIUM_CRHBYTES);
+ } else {
+ /*
+ * Set the digestsize - for SHA512 this is a noop, for SHAKE256,
+ * it sets the value. The BUILD_BUG_ON is to check that the
+ * SHA-512 output size is identical to the expected length.
+ */
+ BUILD_BUG_ON(LC_DILITHIUM_CRHBYTES != LC_SHA3_512_SIZE_DIGEST);
+ lc_hash_set_digestsize(hash_ctx, LC_DILITHIUM_CRHBYTES);
+ lc_hash_final(hash_ctx, mu);
+ }
+ dilithium_print_buffer(mu, LC_DILITHIUM_CRHBYTES, "Siggen - MU:");
+
+ if (rng_ctx) {
+ CKINT(lc_rng_generate(rng_ctx, NULL, 0, rnd,
+ LC_DILITHIUM_RNDBYTES));
+ } else {
+ memset(rnd, 0, LC_DILITHIUM_RNDBYTES);
+ }
+ dilithium_print_buffer(rnd, LC_DILITHIUM_RNDBYTES, "Siggen - RND:");
+
+ unpack_sk_key(key, sk);
+
+ /* Timecop: key is secret */
+ poison(key, LC_DILITHIUM_SEEDBYTES);
+
+ /* Re-use the ws->seedbuf, but making sure that mu is unchanged */
+ BUILD_BUG_ON(LC_DILITHIUM_CRHBYTES >
+ LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_RNDBYTES);
+ rhoprime = key;
+
+ lc_xof(lc_shake256, key,
+ LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_RNDBYTES +
+ LC_DILITHIUM_CRHBYTES,
+ rhoprime, LC_DILITHIUM_CRHBYTES);
+ dilithium_print_buffer(rhoprime, LC_DILITHIUM_CRHBYTES,
+ "Siggen - RHOPrime:");
+
+ /*
+ * Timecop: RHO' is the hash of the secret value of key which is
+ * enlarged to sample the intermediate vector y from. Due to the hashing
+ * any side channel on RHO' cannot allow the deduction of the original
+ * key.
+ */
+ unpoison(rhoprime, LC_DILITHIUM_CRHBYTES);
+
+ unpack_sk_s1(&ws->s1, sk);
+
+ /* Timecop: s1 is secret */
+ poison(&ws->s1, sizeof(polyvecl));
+
+ polyvecl_ntt(&ws->s1);
+ dilithium_print_polyvecl(&ws->s1,
+ "Siggen - S1 L x N matrix after NTT:");
+
+ unpack_sk_s2(&ws->s2, sk);
+
+ /* Timecop: s2 is secret */
+ poison(&ws->s2, sizeof(polyveck));
+
+ polyveck_ntt(&ws->s2);
+ dilithium_print_polyveck(&ws->s2,
+ "Siggen - S2 K x N matrix after NTT:");
+
+ unpack_sk_t0(&ws->t0, sk);
+ polyveck_ntt(&ws->t0);
+ dilithium_print_polyveck(&ws->t0,
+ "Siggen - T0 K x N matrix after NTT:");
+
+rej:
+ /* Sample intermediate vector y */
+ polyvecl_uniform_gamma1(&ws->y, rhoprime, nonce++,
+ ws->tmp.poly_uniform_gamma1_buf);
+ dilithium_print_polyvecl(
+ &ws->y,
+ "Siggen - Y L x N matrix after ExpandMask - start of loop");
+
+ /* Timecop: s2 is secret */
+ poison(&ws->y, sizeof(polyvecl));
+
+ /* Matrix-vector multiplication */
+ ws->z = ws->y;
+ polyvecl_ntt(&ws->z);
+
+ /* Use the cp for this operation as it is not used here so far. */
+ polyvec_matrix_pointwise_montgomery(&ws->w1, mat, &ws->z, &ws->cp);
+ polyveck_reduce(&ws->w1);
+ polyveck_invntt_tomont(&ws->w1);
+ dilithium_print_polyveck(&ws->w1,
+ "Siggen - W K x N matrix after NTT-1");
+
+ /* Decompose w and call the random oracle */
+ polyveck_caddq(&ws->w1);
+ polyveck_decompose(&ws->w1, &ws->w0, &ws->w1);
+
+ /* Timecop: the signature component w1 is not sensitive any more. */
+ unpoison(&ws->w1, sizeof(polyveck));
+ polyveck_pack_w1(sig->sig, &ws->w1);
+ dilithium_print_buffer(sig->sig,
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYW1_PACKEDBYTES,
+ "Siggen - w1Encode of W1");
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, mu, LC_DILITHIUM_CRHBYTES);
+ lc_hash_update(hash_ctx, sig->sig,
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYW1_PACKEDBYTES);
+ lc_hash_set_digestsize(hash_ctx, LC_DILITHIUM_CTILDE_BYTES);
+ lc_hash_final(hash_ctx, sig->sig);
+ lc_hash_zero(hash_ctx);
+ dilithium_print_buffer(sig->sig, LC_DILITHIUM_CTILDE_BYTES,
+ "Siggen - ctilde");
+
+ poly_challenge(&ws->cp, sig->sig, ws->tmp.poly_challenge_buf);
+ dilithium_print_poly(&ws->cp, "Siggen - c after SampleInBall");
+ poly_ntt(&ws->cp);
+ dilithium_print_poly(&ws->cp, "Siggen - c after NTT");
+
+ /* Compute z, reject if it reveals secret */
+ polyvecl_pointwise_poly_montgomery(&ws->z, &ws->cp, &ws->s1);
+ polyvecl_invntt_tomont(&ws->z);
+ polyvecl_add(&ws->z, &ws->z, &ws->y);
+ dilithium_print_polyvecl(&ws->z, "Siggen - z <- y + cs1");
+
+ polyvecl_reduce(&ws->z);
+ dilithium_print_polyvecl(&ws->z, "Siggen - z reduction");
+
+ /* Timecop: the signature component z is not sensitive any more. */
+ unpoison(&ws->z, sizeof(polyvecl));
+
+ if (polyvecl_chknorm(&ws->z, LC_DILITHIUM_GAMMA1 - LC_DILITHIUM_BETA)) {
+ dilithium_print_polyvecl(&ws->z, "Siggen - z rejection");
+ rej_total |= 1 << 0;
+ goto rej;
+ }
+
+ /*
+ * Check that subtracting cs2 does not change high bits of w and low
+ * bits do not reveal secret information.
+ */
+ polyveck_pointwise_poly_montgomery(&ws->h, &ws->cp, &ws->s2);
+ polyveck_invntt_tomont(&ws->h);
+ polyveck_sub(&ws->w0, &ws->w0, &ws->h);
+ polyveck_reduce(&ws->w0);
+
+ /* Timecop: verification data w0 is not sensitive any more. */
+ unpoison(&ws->w0, sizeof(polyveck));
+
+ if (polyveck_chknorm(&ws->w0,
+ LC_DILITHIUM_GAMMA2 - LC_DILITHIUM_BETA)) {
+ dilithium_print_polyveck(&ws->w0, "Siggen - r0 rejection");
+ rej_total |= 1 << 1;
+ goto rej;
+ }
+
+ /* Compute hints for w1 */
+ polyveck_pointwise_poly_montgomery(&ws->h, &ws->cp, &ws->t0);
+ polyveck_invntt_tomont(&ws->h);
+ polyveck_reduce(&ws->h);
+
+ /* Timecop: the signature component h is not sensitive any more. */
+ unpoison(&ws->h, sizeof(polyveck));
+
+ if (polyveck_chknorm(&ws->h, LC_DILITHIUM_GAMMA2)) {
+ dilithium_print_polyveck(&ws->h, "Siggen - ct0 rejection");
+ rej_total |= 1 << 2;
+ goto rej;
+ }
+
+ polyveck_add(&ws->w0, &ws->w0, &ws->h);
+
+ n = polyveck_make_hint(&ws->h, &ws->w0, &ws->w1);
+ if (n > LC_DILITHIUM_OMEGA) {
+ dilithium_print_polyveck(&ws->w0, "Siggen - h rejection");
+ rej_total |= 1 << 3;
+ goto rej;
+ }
+
+ /* Write signature */
+ dilithium_print_buffer(sig->sig, LC_DILITHIUM_CTILDE_BYTES,
+ "Siggen - Ctilde:");
+ dilithium_print_polyvecl(&ws->z, "Siggen - Z L x N matrix:");
+ dilithium_print_polyveck(&ws->h, "Siggen - H K x N matrix:");
+
+ pack_sig(sig, &ws->z, &ws->h);
+
+ dilithium_print_buffer(sig->sig, LC_DILITHIUM_CRYPTO_BYTES,
+ "Siggen - Signature:");
+
+out:
+ LC_RELEASE_MEM(ws);
+#ifdef REJECTION_TEST_SAMPLING
+ return ret ? ret : rej_total;
+#else
+ return ret;
+#endif
+}
+
+static int
+lc_dilithium_sign_internal_noahat(struct lc_dilithium_sig *sig,
+ const struct lc_dilithium_sk *sk,
+ struct lc_dilithium_ctx *ctx,
+ struct lc_rng_ctx *rng_ctx)
+{
+ struct workspace_sign {
+ polyvecl mat[LC_DILITHIUM_K];
+ uint8_t poly_uniform_buf[WS_POLY_UNIFORM_BUF_SIZE];
+ };
+ /* The first bytes of the key is rho. */
+ const uint8_t *rho = sk->sk;
+ int ret = 0;
+ LC_DECLARE_MEM(ws, struct workspace_sign, LC_DILITHIUM_AHAT_ALIGNMENT);
+
+ polyvec_matrix_expand(ws->mat, rho, ws->poly_uniform_buf);
+
+ /* Temporarily set the pointer */
+ ctx->ahat = ws->mat;
+
+ CKINT(lc_dilithium_sign_internal_ahat(sig, sk, ctx, rng_ctx));
+
+out:
+ ctx->ahat = NULL;
+ LC_RELEASE_MEM(ws);
+ return ret;
+}
+
+static int
+lc_dilithium_sk_expand_impl(const struct lc_dilithium_sk *sk,
+ struct lc_dilithium_ctx *ctx)
+{
+ struct workspace_sign {
+ uint8_t poly_uniform_buf[WS_POLY_UNIFORM_BUF_SIZE];
+ };
+ /* The first bytes of the key is rho. */
+ const uint8_t *rho = sk->sk;
+ polyvecl *mat = ctx->ahat;
+ int ret = 0;
+ LC_DECLARE_MEM(ws, struct workspace_sign, sizeof(uint64_t));
+
+ /*
+ * The compile time sanity check links API header file with
+ * Dilithium-internal definitions.
+ *
+ * Runtime sanity check ensures that the allocated context has
+ * sufficient size (e.g. not that caller used, say,
+ * LC_DILITHIUM_44_CTX_ON_STACK_AHAT with a ML-DSA 65 or 87 key)
+ */
+#if LC_DILITHIUM_MODE == 2
+ BUILD_BUG_ON(LC_DILITHIUM_44_AHAT_SIZE !=
+ sizeof(polyvecl) * LC_DILITHIUM_K);
+ if (ctx->ahat_size < LC_DILITHIUM_44_AHAT_SIZE) {
+ ret = -EOVERFLOW;
+ goto out;
+ }
+#elif LC_DILITHIUM_MODE == 3
+ BUILD_BUG_ON(LC_DILITHIUM_65_AHAT_SIZE !=
+ sizeof(polyvecl) * LC_DILITHIUM_K);
+ if (ctx->ahat_size < LC_DILITHIUM_65_AHAT_SIZE) {
+ ret = -EOVERFLOW;
+ goto out;
+ }
+#elif LC_DILITHIUM_MODE == 5
+ BUILD_BUG_ON(LC_DILITHIUM_87_AHAT_SIZE !=
+ sizeof(polyvecl) * LC_DILITHIUM_K);
+ if (ctx->ahat_size < LC_DILITHIUM_87_AHAT_SIZE) {
+ ret = -EOVERFLOW;
+ goto out;
+ }
+#else
+#error "Undefined LC_DILITHIUM_MODE"
+#endif
+
+ polyvec_matrix_expand(mat, rho, ws->poly_uniform_buf);
+ dilithium_print_polyvecl_k(mat,
+ "AHAT - A K x L x N matrix after ExpandA:");
+
+ ctx->ahat_expanded = 1;
+
+out:
+ LC_RELEASE_MEM(ws);
+ return ret;
+}
+
+static int
+lc_dilithium_sign_internal(struct lc_dilithium_sig *sig,
+ const struct lc_dilithium_sk *sk,
+ struct lc_dilithium_ctx *ctx,
+ struct lc_rng_ctx *rng_ctx)
+{
+ int ret;
+
+ if (!ctx->ahat)
+ return lc_dilithium_sign_internal_noahat(sig, sk, ctx, rng_ctx);
+
+ if (!ctx->ahat_expanded)
+ CKINT(lc_dilithium_sk_expand_impl(sk, ctx));
+
+ CKINT(lc_dilithium_sign_internal_ahat(sig, sk, ctx, rng_ctx));
+
+out:
+ return ret;
+}
+
+static int
+lc_dilithium_sign_ctx_impl(struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ uint8_t tr[LC_DILITHIUM_TRBYTES];
+ int ret = 0;
+
+ /* rng_ctx is allowed to be NULL as handled below */
+ if (!sig || !sk || !ctx)
+ return -EINVAL;
+ /* Either the message or the external mu must be provided */
+ if (!m && !ctx->external_mu)
+ return -EINVAL;
+
+ dilithium_print_buffer(m, mlen, "Siggen - Message");
+
+ unpack_sk_tr(tr, sk);
+
+ if (m) {
+ /* Compute mu = CRH(tr, msg) */
+ struct lc_hash_ctx *hash_ctx = &ctx->dilithium_hash_ctx;
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, tr, LC_DILITHIUM_TRBYTES);
+
+ CKINT(signature_domain_separation(
+ &ctx->dilithium_hash_ctx, ctx->ml_dsa_internal,
+ ctx->dilithium_prehash_type, ctx->userctx,
+ ctx->userctxlen, m, mlen, ctx->randomizer,
+ ctx->randomizerlen, LC_DILITHIUM_NIST_CATEGORY));
+ }
+
+ ret = lc_dilithium_sign_internal(sig, sk, ctx, rng_ctx);
+
+out:
+ lc_memset_secure(tr, 0, sizeof(tr));
+ return ret;
+}
+
+static int
+lc_dilithium_sign_impl(struct lc_dilithium_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ LC_DILITHIUM_CTX_ON_STACK(dilithium_ctx);
+ int ret = lc_dilithium_sign_ctx_impl(sig, dilithium_ctx, m, mlen, sk,
+ rng_ctx);
+
+ lc_dilithium_ctx_zero(dilithium_ctx);
+ return ret;
+}
+
+static int
+lc_dilithium_sign_init_impl(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk)
+{
+ uint8_t tr[LC_DILITHIUM_TRBYTES];
+ struct lc_hash_ctx *hash_ctx;
+
+ /* rng_ctx is allowed to be NULL as handled below */
+ if (!ctx || !sk)
+ return -EINVAL;
+
+ hash_ctx = &ctx->dilithium_hash_ctx;
+
+ /* Require the use of SHAKE256 */
+ if (hash_ctx->hash != lc_shake256)
+ return -EOPNOTSUPP;
+
+ unpack_sk_tr(tr, sk);
+
+ /* Compute mu = CRH(tr, msg) */
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, tr, LC_DILITHIUM_TRBYTES);
+ lc_memset_secure(tr, 0, sizeof(tr));
+
+ return signature_domain_separation(
+ &ctx->dilithium_hash_ctx, ctx->ml_dsa_internal,
+ ctx->dilithium_prehash_type, ctx->userctx, ctx->userctxlen,
+ NULL, 0, ctx->randomizer, ctx->randomizerlen,
+ LC_DILITHIUM_NIST_CATEGORY);
+}
+
+static int
+lc_dilithium_sign_update_impl(struct lc_dilithium_ctx *ctx,
+ const uint8_t *m, size_t mlen)
+{
+ if (!ctx || !m)
+ return -EINVAL;
+
+ /* Compute CRH(tr, msg) */
+ lc_hash_update(&ctx->dilithium_hash_ctx, m, mlen);
+
+ return 0;
+}
+
+static int
+lc_dilithium_sign_final_impl(struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ int ret = 0;
+
+ /* rng_ctx is allowed to be NULL as handled below */
+ if (!sig || !ctx || !sk) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = lc_dilithium_sign_internal(sig, sk, ctx, rng_ctx);
+
+out:
+ lc_dilithium_ctx_zero(ctx);
+ return ret;
+}
+
+static int
+lc_dilithium_verify_internal_ahat(const struct lc_dilithium_sig *sig,
+ const struct lc_dilithium_pk *pk,
+ struct lc_dilithium_ctx *ctx)
+{
+ struct workspace_verify {
+ union {
+ poly cp;
+ } matrix;
+ polyveck w1;
+ union {
+ polyveck t1, h;
+ polyvecl z;
+ uint8_t mu[LC_DILITHIUM_CRHBYTES];
+ BUF_ALIGNED_UINT8_UINT64(LC_DILITHIUM_CTILDE_BYTES)
+ c2;
+ } buf;
+
+ union {
+ poly polyvecl_pointwise_acc_montgomery_buf;
+ uint8_t buf[LC_DILITHIUM_K *
+ LC_DILITHIUM_POLYW1_PACKEDBYTES];
+ uint8_t poly_challenge_buf[POLY_CHALLENGE_BYTES];
+ } tmp;
+ };
+ /* The first bytes of the signature is c~ and thus contains c1. */
+ const uint8_t *c1 = sig->sig;
+ const polyvecl *mat = ctx->ahat;
+ struct lc_hash_ctx *hash_ctx = &ctx->dilithium_hash_ctx;
+ int ret = 0;
+ LC_DECLARE_MEM(ws, struct workspace_verify, sizeof(uint64_t));
+
+ /* AHat must be present at this time */
+ CKNULL(mat, -EINVAL);
+
+ unpack_sig_z(&ws->buf.z, sig);
+ if (polyvecl_chknorm(&ws->buf.z,
+ LC_DILITHIUM_GAMMA1 - LC_DILITHIUM_BETA)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ polyvecl_ntt(&ws->buf.z);
+ polyvec_matrix_pointwise_montgomery(
+ &ws->w1, mat, &ws->buf.z,
+ &ws->tmp.polyvecl_pointwise_acc_montgomery_buf);
+
+ /* Matrix-vector multiplication; compute Az - c2^dt1 */
+ poly_challenge(&ws->matrix.cp, c1, ws->tmp.poly_challenge_buf);
+ poly_ntt(&ws->matrix.cp);
+
+ unpack_pk_t1(&ws->buf.t1, pk);
+ polyveck_shiftl(&ws->buf.t1);
+ polyveck_ntt(&ws->buf.t1);
+ polyveck_pointwise_poly_montgomery(&ws->buf.t1, &ws->matrix.cp,
+ &ws->buf.t1);
+
+ polyveck_sub(&ws->w1, &ws->w1, &ws->buf.t1);
+ polyveck_reduce(&ws->w1);
+ polyveck_invntt_tomont(&ws->w1);
+
+ /* Reconstruct w1 */
+ polyveck_caddq(&ws->w1);
+ dilithium_print_polyveck(&ws->w1,
+ "Sigver - W K x N matrix before hint:");
+
+ if (unpack_sig_h(&ws->buf.h, sig))
+ return -EINVAL;
+ dilithium_print_polyveck(&ws->buf.h, "Siggen - H K x N matrix:");
+
+ polyveck_use_hint(&ws->w1, &ws->w1, &ws->buf.h);
+ dilithium_print_polyveck(&ws->w1,
+ "Sigver - W K x N matrix after hint:");
+ polyveck_pack_w1(ws->tmp.buf, &ws->w1);
+ dilithium_print_buffer(ws->tmp.buf,
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYW1_PACKEDBYTES,
+ "Sigver - W after w1Encode");
+
+ if (ctx->external_mu) {
+ if (ctx->external_mu_len != LC_DILITHIUM_CRHBYTES)
+ return -EINVAL;
+
+ /* Call random oracle and verify challenge */
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, ctx->external_mu,
+ LC_DILITHIUM_CRHBYTES);
+ } else {
+ lc_hash_set_digestsize(hash_ctx, LC_DILITHIUM_CRHBYTES);
+ lc_hash_final(hash_ctx, ws->buf.mu);
+
+ /* Call random oracle and verify challenge */
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, ws->buf.mu, LC_DILITHIUM_CRHBYTES);
+ }
+
+ lc_hash_update(hash_ctx, ws->tmp.buf,
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYW1_PACKEDBYTES);
+ lc_hash_set_digestsize(hash_ctx, LC_DILITHIUM_CTILDE_BYTES);
+ lc_hash_final(hash_ctx, ws->buf.c2.coeffs);
+ lc_hash_zero(hash_ctx);
+
+ /* Signature verification operation */
+ if (lc_memcmp_secure(c1, LC_DILITHIUM_CTILDE_BYTES, ws->buf.c2.coeffs,
+ LC_DILITHIUM_CTILDE_BYTES))
+ ret = -EBADMSG;
+
+out:
+ LC_RELEASE_MEM(ws);
+ return ret;
+}
+
+static int
+lc_dilithium_verify_internal_noahat(const struct lc_dilithium_sig *sig,
+ const struct lc_dilithium_pk *pk,
+ struct lc_dilithium_ctx *ctx)
+{
+ struct workspace_verify {
+ polyvecl mat[LC_DILITHIUM_K];
+ uint8_t poly_uniform_buf[WS_POLY_UNIFORM_BUF_SIZE];
+ };
+ /* The first bytes of the key is rho. */
+ const uint8_t *rho = pk->pk;
+ int ret = 0;
+ LC_DECLARE_MEM(ws, struct workspace_verify, sizeof(uint64_t));
+
+ polyvec_matrix_expand(ws->mat, rho, ws->poly_uniform_buf);
+
+ /* Temporarily set the pointer */
+ ctx->ahat = ws->mat;
+
+ CKINT(lc_dilithium_verify_internal_ahat(sig, pk, ctx));
+
+out:
+ ctx->ahat = NULL;
+ LC_RELEASE_MEM(ws);
+ return ret;
+}
+
+static int
+lc_dilithium_pk_expand_impl(const struct lc_dilithium_pk *pk,
+ struct lc_dilithium_ctx *ctx)
+{
+ struct workspace_verify {
+ uint8_t poly_uniform_buf[WS_POLY_UNIFORM_BUF_SIZE];
+ };
+ /* The first bytes of the key is rho. */
+ const uint8_t *rho = pk->pk;
+ polyvecl *mat = ctx->ahat;
+ int ret = 0;
+ LC_DECLARE_MEM(ws, struct workspace_verify, sizeof(uint64_t));
+
+ /*
+ * Runtime sanity check ensures that the allocated context has
+ * sufficient size (e.g. not that caller used, say,
+ * LC_DILITHIUM_44_CTX_ON_STACK_AHAT with a ML-DSA 65 or 87 key)
+ */
+#if LC_DILITHIUM_MODE == 2
+ if (ctx->ahat_size < LC_DILITHIUM_44_AHAT_SIZE) {
+ ret = -EOVERFLOW;
+ goto out;
+ }
+#elif LC_DILITHIUM_MODE == 3
+ if (ctx->ahat_size < LC_DILITHIUM_65_AHAT_SIZE) {
+ ret = -EOVERFLOW;
+ goto out;
+ }
+#elif LC_DILITHIUM_MODE == 5
+ if (ctx->ahat_size < LC_DILITHIUM_87_AHAT_SIZE) {
+ ret = -EOVERFLOW;
+ goto out;
+ }
+#else
+#error "Undefined LC_DILITHIUM_MODE"
+#endif
+
+ polyvec_matrix_expand(mat, rho, ws->poly_uniform_buf);
+ ctx->ahat_expanded = 1;
+
+out:
+ LC_RELEASE_MEM(ws);
+ return ret;
+}
+
+static int
+lc_dilithium_verify_internal(const struct lc_dilithium_sig *sig,
+ const struct lc_dilithium_pk *pk,
+ struct lc_dilithium_ctx *ctx)
+{
+ int ret;
+
+ if (!ctx->ahat)
+ return lc_dilithium_verify_internal_noahat(sig, pk, ctx);
+
+ if (!ctx->ahat_expanded)
+ CKINT(lc_dilithium_pk_expand_impl(pk, ctx));
+
+ CKINT(lc_dilithium_verify_internal_ahat(sig, pk, ctx));
+
+out:
+ return ret;
+}
+
+static int
+lc_dilithium_verify_ctx_impl(const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_pk *pk)
+{
+ uint8_t tr[LC_DILITHIUM_TRBYTES];
+ int ret = 0;
+
+ if (!sig || !pk || !ctx)
+ return -EINVAL;
+
+ /* Either the message or the external mu must be provided */
+ if (!m && !ctx->external_mu)
+ return -EINVAL;
+
+ /* Make sure that ->mu is large enough for ->tr */
+ BUILD_BUG_ON(LC_DILITHIUM_TRBYTES > LC_DILITHIUM_CRHBYTES);
+
+ /* Compute CRH(H(rho, t1), msg) */
+ lc_xof(lc_shake256, pk->pk, LC_DILITHIUM_PUBLICKEYBYTES, tr,
+ LC_DILITHIUM_TRBYTES);
+
+ if (m) {
+ struct lc_hash_ctx *hash_ctx = &ctx->dilithium_hash_ctx;
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, tr, LC_DILITHIUM_TRBYTES);
+ CKINT(signature_domain_separation(
+ &ctx->dilithium_hash_ctx, ctx->ml_dsa_internal,
+ ctx->dilithium_prehash_type, ctx->userctx,
+ ctx->userctxlen, m, mlen, ctx->randomizer,
+ ctx->randomizerlen, LC_DILITHIUM_NIST_CATEGORY));
+ }
+
+ ret = lc_dilithium_verify_internal(sig, pk, ctx);
+
+out:
+ lc_memset_secure(tr, 0, sizeof(tr));
+ return ret;
+}
+
+static int
+lc_dilithium_verify_impl(const struct lc_dilithium_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_pk *pk)
+{
+ LC_DILITHIUM_CTX_ON_STACK(ctx);
+ int ret = lc_dilithium_verify_ctx_impl(sig, ctx, m, mlen, pk);
+
+ lc_dilithium_ctx_zero(ctx);
+ return ret;
+}
+
+static int
+lc_dilithium_verify_init_impl(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ uint8_t mu[LC_DILITHIUM_TRBYTES];
+ struct lc_hash_ctx *hash_ctx;
+
+ /* rng_ctx is allowed to be NULL as handled below */
+ if (!ctx || !pk)
+ return -EINVAL;
+
+ hash_ctx = &ctx->dilithium_hash_ctx;
+
+ /* Require the use of SHAKE256 */
+ if (hash_ctx->hash != lc_shake256)
+ return -EOPNOTSUPP;
+
+ /* Compute CRH(H(rho, t1), msg) */
+ lc_xof(lc_shake256, pk->pk, LC_DILITHIUM_PUBLICKEYBYTES, mu,
+ LC_DILITHIUM_TRBYTES);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, mu, LC_DILITHIUM_TRBYTES);
+ lc_memset_secure(mu, 0, sizeof(mu));
+
+ return signature_domain_separation(
+ &ctx->dilithium_hash_ctx, ctx->ml_dsa_internal,
+ ctx->dilithium_prehash_type, ctx->userctx, ctx->userctxlen,
+ NULL, 0, ctx->randomizer, ctx->randomizerlen,
+ LC_DILITHIUM_NIST_CATEGORY);
+}
+
+static int
+lc_dilithium_verify_update_impl(struct lc_dilithium_ctx *ctx,
+ const uint8_t *m, size_t mlen)
+{
+ struct lc_hash_ctx *hash_ctx;
+
+ if (!ctx || !m)
+ return -EINVAL;
+
+ /* Compute CRH(H(rho, t1), msg) */
+ hash_ctx = &ctx->dilithium_hash_ctx;
+ lc_hash_update(hash_ctx, m, mlen);
+
+ return 0;
+}
+
+static int
+lc_dilithium_verify_final_impl(const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ int ret = 0;
+
+ if (!sig || !ctx || !pk) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = lc_dilithium_verify_internal(sig, pk, ctx);
+
+out:
+ lc_dilithium_ctx_zero(ctx);
+ return ret;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_SIGNATURE_IMPL_H */
diff --git a/lib/freebl/leancrypto/dilithium_type.h b/lib/freebl/leancrypto/dilithium_type.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_type.h
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_TYPE_H
+#define DILITHIUM_TYPE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Prevent Dilithium macros from getting undefined */
+#define LC_DILITHIUM_INTERNAL
+
+/*
+ * This define replaces all symbol names accordingly to allow double compilation
+ * of the same code base.
+ *
+ * Due to the replacement operation, this header file must be included as the
+ * first header file in the entire stack.
+ *
+ * This file can easily be replaced with lc_dilithium.h to achieve the common
+ * functionality without symbol duplication. But in this case, only the
+ * Dilithium security strength is compiled defined in lc_dilithium.h. Duplicate
+ * compilation different sizes would not be possible.
+ */
+#ifdef LC_DILITHIUM_TYPE_65
+#define DILITHIUM_F(name) lc_dilithium_65_##name
+#define lc_dilithium_pk lc_dilithium_65_pk
+#define lc_dilithium_sk lc_dilithium_65_sk
+#define lc_dilithium_sig lc_dilithium_65_sig
+#define lc_dilithium_ed25519_pk lc_dilithium_65_ed25519_pk
+#define lc_dilithium_ed25519_sk lc_dilithium_65_ed25519_sk
+#define lc_dilithium_ed25519_sig lc_dilithium_65_ed25519_sig
+#define lc_dilithium_ed25519_ctx lc_dilithium_65_ed25519_ctx
+#define lc_dilithium_ed448_pk lc_dilithium_65_ed448_pk
+#define lc_dilithium_ed448_sk lc_dilithium_65_ed448_sk
+#define lc_dilithium_ed448_sig lc_dilithium_65_ed448_sig
+#define lc_dilithium_ed448_ctx lc_dilithium_65_ed448_ctx
+
+#include "lc_dilithium_65.h"
+
+#elif defined LC_DILITHIUM_TYPE_44
+#define DILITHIUM_F(name) lc_dilithium_44_##name
+#define lc_dilithium_pk lc_dilithium_44_pk
+#define lc_dilithium_sk lc_dilithium_44_sk
+#define lc_dilithium_sig lc_dilithium_44_sig
+#define lc_dilithium_ed25519_pk lc_dilithium_44_ed25519_pk
+#define lc_dilithium_ed25519_sk lc_dilithium_44_ed25519_sk
+#define lc_dilithium_ed25519_sig lc_dilithium_44_ed25519_sig
+#define lc_dilithium_ed25519_ctx lc_dilithium_44_ed25519_ctx
+#define lc_dilithium_ed448_pk lc_dilithium_44_ed448_pk
+#define lc_dilithium_ed448_sk lc_dilithium_44_ed448_sk
+#define lc_dilithium_ed448_sig lc_dilithium_44_ed448_sig
+#define lc_dilithium_ed448_ctx lc_dilithium_44_ed448_ctx
+
+#include "lc_dilithium_44.h"
+
+#else
+#define DILITHIUM_F(name) lc_dilithium_87_##name
+#define lc_dilithium_pk lc_dilithium_87_pk
+#define lc_dilithium_sk lc_dilithium_87_sk
+#define lc_dilithium_sig lc_dilithium_87_sig
+#define lc_dilithium_ed25519_pk lc_dilithium_87_ed25519_pk
+#define lc_dilithium_ed25519_sk lc_dilithium_87_ed25519_sk
+#define lc_dilithium_ed25519_sig lc_dilithium_87_ed25519_sig
+#define lc_dilithium_ed25519_ctx lc_dilithium_87_ed25519_ctx
+#define lc_dilithium_ed448_pk lc_dilithium_87_ed448_pk
+#define lc_dilithium_ed448_sk lc_dilithium_87_ed448_sk
+#define lc_dilithium_ed448_sig lc_dilithium_87_ed448_sig
+#define lc_dilithium_ed448_ctx lc_dilithium_87_ed448_ctx
+
+#include "lc_dilithium_87.h"
+
+#endif
+
+/*
+ * The following defines simply allow duplicate compilation of the
+ * respective functions.
+ */
+#define lc_dilithium_keypair DILITHIUM_F(keypair)
+#define lc_dilithium_keypair_from_seed DILITHIUM_F(keypair_from_seed)
+#define lc_dilithium_sign DILITHIUM_F(sign)
+#define lc_dilithium_sign_ctx DILITHIUM_F(sign_ctx)
+#define lc_dilithium_sign_init DILITHIUM_F(sign_init)
+#define lc_dilithium_sign_update DILITHIUM_F(sign_update)
+#define lc_dilithium_sign_final DILITHIUM_F(sign_final)
+#define lc_dilithium_verify DILITHIUM_F(verify)
+#define lc_dilithium_verify_ctx DILITHIUM_F(verify_ctx)
+#define lc_dilithium_verify_init DILITHIUM_F(verify_init)
+#define lc_dilithium_verify_update DILITHIUM_F(verify_update)
+#define lc_dilithium_verify_final DILITHIUM_F(verify_final)
+#define lc_dilithium_ctx_alloc DILITHIUM_F(ctx_alloc)
+#define lc_dilithium_ctx_alloc_ahat DILITHIUM_F(ctx_alloc_ahat)
+#define lc_dilithium_ctx_zero_free DILITHIUM_F(ctx_zero_free)
+#define lc_dilithium_ctx_zero DILITHIUM_F(ctx_zero)
+
+#define lc_dilithium_keypair_c DILITHIUM_F(keypair_c)
+#define lc_dilithium_keypair_from_seed_c DILITHIUM_F(keypair_from_seed_c)
+#define lc_dilithium_sign_c DILITHIUM_F(sign_c)
+#define lc_dilithium_sign_ctx_c DILITHIUM_F(sign_ctx_c)
+#define lc_dilithium_sign_init_c DILITHIUM_F(sign_init_c)
+#define lc_dilithium_sign_update_c DILITHIUM_F(sign_update_c)
+#define lc_dilithium_sign_final_c DILITHIUM_F(sign_final_c)
+#define lc_dilithium_verify_c DILITHIUM_F(verify_c)
+#define lc_dilithium_verify_ctx_c DILITHIUM_F(verify_ctx_c)
+#define lc_dilithium_verify_init_c DILITHIUM_F(verify_init_c)
+#define lc_dilithium_verify_update_c DILITHIUM_F(verify_update_c)
+#define lc_dilithium_verify_final_c DILITHIUM_F(verify_final_c)
+
+#define lc_dilithium_ed25519_keypair DILITHIUM_F(ed25519_keypair)
+#define lc_dilithium_ed25519_sign DILITHIUM_F(ed25519_sign)
+#define lc_dilithium_ed25519_sign_ctx DILITHIUM_F(ed25519_sign_ctx)
+#define lc_dilithium_ed25519_sign_init DILITHIUM_F(ed25519_sign_init)
+#define lc_dilithium_ed25519_sign_update DILITHIUM_F(ed25519_sign_update)
+#define lc_dilithium_ed25519_sign_final DILITHIUM_F(ed25519_sign_final)
+#define lc_dilithium_ed25519_verify DILITHIUM_F(ed25519_verify)
+#define lc_dilithium_ed25519_verify_ctx DILITHIUM_F(ed25519_verify_ctx)
+#define lc_dilithium_ed25519_verify_init DILITHIUM_F(ed25519_verify_init)
+#define lc_dilithium_ed25519_verify_update DILITHIUM_F(ed25519_verify_update)
+#define lc_dilithium_ed25519_verify_final DILITHIUM_F(ed25519_verify_final)
+#define lc_dilithium_ed25519_ctx_alloc DILITHIUM_F(ed25519_ctx_alloc)
+#define lc_dilithium_ed25519_ctx_zero_free DILITHIUM_F(ed25519_ctx_zero_free)
+#define lc_dilithium_ed25519_ctx_zero DILITHIUM_F(ed25519_ctx_zero)
+
+#define lc_dilithium_ed448_keypair DILITHIUM_F(ed448_keypair)
+#define lc_dilithium_ed448_sign DILITHIUM_F(ed448_sign)
+#define lc_dilithium_ed448_sign_ctx DILITHIUM_F(ed448_sign_ctx)
+#define lc_dilithium_ed448_sign_init DILITHIUM_F(ed448_sign_init)
+#define lc_dilithium_ed448_sign_update DILITHIUM_F(ed448_sign_update)
+#define lc_dilithium_ed448_sign_final DILITHIUM_F(ed448_sign_final)
+#define lc_dilithium_ed448_verify DILITHIUM_F(ed448_verify)
+#define lc_dilithium_ed448_verify_ctx DILITHIUM_F(ed448_verify_ctx)
+#define lc_dilithium_ed448_verify_init DILITHIUM_F(ed448_verify_init)
+#define lc_dilithium_ed448_verify_update DILITHIUM_F(ed448_verify_update)
+#define lc_dilithium_ed448_verify_final DILITHIUM_F(ed448_verify_final)
+#define lc_dilithium_ed448_ctx_alloc DILITHIUM_F(ed448_ctx_alloc)
+#define lc_dilithium_ed448_ctx_zero_free DILITHIUM_F(ed448_ctx_zero_free)
+#define lc_dilithium_ed448_ctx_zero DILITHIUM_F(ed448_ctx_zero)
+
+#define dilithium_keypair_tester DILITHIUM_F(keypair_tester)
+#define dilithium_siggen_tester DILITHIUM_F(siggen_tester)
+#define dilithium_sigver_tester DILITHIUM_F(sigver_tester)
+
+#define ntt DILITHIUM_F(ntt)
+#define invntt_tomont DILITHIUM_F(invntt_tomont)
+#define poly_chknorm DILITHIUM_F(poly_chknorm)
+#define poly_uniform DILITHIUM_F(poly_uniform)
+#define poly_uniform_eta DILITHIUM_F(poly_uniform_eta)
+#define poly_uniform_gamma1 DILITHIUM_F(poly_uniform_gamma1)
+#define polyz_unpack DILITHIUM_F(polyz_unpack)
+#define poly_challenge DILITHIUM_F(poly_challenge)
+#define polyeta_pack DILITHIUM_F(polyeta_pack)
+#define polyeta_unpack DILITHIUM_F(polyeta_unpack)
+#define polyt1_pack DILITHIUM_F(polyt1_pack)
+#define polyt0_pack DILITHIUM_F(polyt0_pack)
+#define polyt0_unpack DILITHIUM_F(polyt0_unpack)
+#define polyz_pack DILITHIUM_F(polyz_pack)
+#define polyw1_pack DILITHIUM_F(polyw1_pack)
+#define power2round DILITHIUM_F(power2round)
+#define decompose DILITHIUM_F(decompose)
+#define make_hint DILITHIUM_F(make_hint)
+#define use_hint DILITHIUM_F(use_hint)
+
+#define dilithium_print_buffer DILITHIUM_F(print_buffer)
+#define dilithium_print_polyvecl_k DILITHIUM_F(print_polyvecl_k)
+#define dilithium_print_polyvecl DILITHIUM_F(print_polyvecl)
+#define dilithium_print_polyveck DILITHIUM_F(print_polyveck)
+#define dilithium_print_poly DILITHIUM_F(print_poly)
+
+/* AVX2 Implementation */
+#define dilithium_invntt_avx DILITHIUM_F(invntt_avx)
+#define dilithium_ntt_avx DILITHIUM_F(ntt_avx)
+#define dilithium_nttunpack_avx DILITHIUM_F(nttunpack_avx)
+#define dilithium_pointwise_avx DILITHIUM_F(pointwise_avx)
+#define dilithium_pointwise_acc_avx DILITHIUM_F(pointwise_acc_avx)
+#define poly_reduce_avx DILITHIUM_F(poly_reduce_avx)
+#define poly_caddq_avx DILITHIUM_F(poly_caddq_avx)
+#define poly_add_avx DILITHIUM_F(poly_add_avx)
+#define poly_sub_avx DILITHIUM_F(poly_sub_avx)
+#define poly_shiftl_avx DILITHIUM_F(poly_shiftl_avx)
+#define poly_chknorm_avx DILITHIUM_F(poly_chknorm_avx)
+#define poly_uniform_4x_avx DILITHIUM_F(poly_uniform_4x_avx)
+#define poly_uniform_eta_4x_avx DILITHIUM_F(poly_uniform_eta_4x_avx)
+#define poly_uniform_gamma1_4x_avx DILITHIUM_F(poly_uniform_gamma1_4x_avx)
+#define polyz_unpack_avx DILITHIUM_F(polyz_unpack_avx)
+#define poly_challenge_avx DILITHIUM_F(poly_challenge_avx)
+#define polyeta_pack_avx DILITHIUM_F(polyeta_pack_avx)
+#define polyeta_unpack_avx DILITHIUM_F(polyeta_unpack_avx)
+#define polyt1_pack_avx DILITHIUM_F(polyt1_pack_avx)
+#define polyt1_unpack_avx DILITHIUM_F(polyt1_unpack_avx)
+#define polyt0_pack_avx DILITHIUM_F(polyt0_pack_avx)
+#define polyt0_unpack_avx DILITHIUM_F(polyt0_unpack_avx)
+#define polyz_pack_avx DILITHIUM_F(polyz_pack_avx)
+#define polyw1_pack_avx DILITHIUM_F(polyw1_pack_avx)
+#define polyvec_matrix_expand DILITHIUM_F(polyvec_matrix_expand)
+#define polyvec_matrix_expand_row0 DILITHIUM_F(polyvec_matrix_expand_row0)
+#define polyvec_matrix_expand_row1 DILITHIUM_F(polyvec_matrix_expand_row1)
+#define polyvec_matrix_expand_row2 DILITHIUM_F(polyvec_matrix_expand_row2)
+#define polyvec_matrix_expand_row3 DILITHIUM_F(polyvec_matrix_expand_row3)
+#define polyvec_matrix_expand_row4 DILITHIUM_F(polyvec_matrix_expand_row4)
+#define polyvec_matrix_expand_row5 DILITHIUM_F(polyvec_matrix_expand_row5)
+#define polyvec_matrix_expand_row6 DILITHIUM_F(polyvec_matrix_expand_row6)
+#define polyvec_matrix_expand_row7 DILITHIUM_F(polyvec_matrix_expand_row7)
+#define rej_uniform_avx DILITHIUM_F(rej_uniform_avx)
+#define rej_eta_avx DILITHIUM_F(rej_eta_avx)
+#define idxlut DILITHIUM_F(idxlut)
+#define power2round_avx DILITHIUM_F(power2round_avx)
+#define decompose_avx DILITHIUM_F(decompose_avx)
+#define make_hint_avx DILITHIUM_F(make_hint_avx)
+#define use_hint_avx DILITHIUM_F(use_hint_avx)
+#define lc_dilithium_keypair_avx2 DILITHIUM_F(keypair_avx2)
+#define lc_dilithium_keypair_from_seed_avx2 DILITHIUM_F(keypair_from_seed_avx2)
+#define lc_dilithium_sign_avx2 DILITHIUM_F(sign_avx2)
+#define lc_dilithium_sign_ctx_avx2 DILITHIUM_F(sign_ctx_avx2)
+#define lc_dilithium_sign_init_avx2 DILITHIUM_F(sign_init_avx2)
+#define lc_dilithium_sign_update_avx2 DILITHIUM_F(sign_update_avx2)
+#define lc_dilithium_sign_final_avx2 DILITHIUM_F(sign_final_avx2)
+#define lc_dilithium_verify_avx2 DILITHIUM_F(verify_avx2)
+#define lc_dilithium_verify_ctx_avx2 DILITHIUM_F(verify_ctx_avx2)
+#define lc_dilithium_verify_init_avx2 DILITHIUM_F(verify_init_avx2)
+#define lc_dilithium_verify_update_avx2 DILITHIUM_F(verify_update_avx2)
+#define lc_dilithium_verify_final_avx2 DILITHIUM_F(verify_final_avx2)
+
+/* ARMv8 Implementation */
+#define intt_SIMD_top_armv8 DILITHIUM_F(intt_SIMD_top_armv8)
+#define intt_SIMD_bot_armv8 DILITHIUM_F(intt_SIMD_bot_armv8)
+#define ntt_SIMD_top_armv8 DILITHIUM_F(ntt_SIMD_top_armv8)
+#define ntt_SIMD_bot_armv8 DILITHIUM_F(ntt_SIMD_bot_armv8)
+#define poly_uniformx2 DILITHIUM_F(poly_uniformx2)
+#define poly_uniform_etax2 DILITHIUM_F(poly_uniform_etax2)
+#define poly_uniform_gamma1x2 DILITHIUM_F(poly_uniform_gamma1x2)
+#define armv8_10_to_32 DILITHIUM_F(armv8_10_to_32)
+#define poly_reduce_armv8 DILITHIUM_F(poly_reduce_armv8)
+#define poly_caddq_armv8 DILITHIUM_F(poly_caddq_armv8)
+#define poly_power2round_armv8 DILITHIUM_F(poly_power2round_armv8)
+#define poly_pointwise_montgomery_armv8 \
+ DILITHIUM_F(poly_pointwise_montgomery_armv8)
+#define polyvecl_pointwise_acc_montgomery_armv8 \
+ DILITHIUM_F(polyvecl_pointwise_acc_montgomery_armv8)
+#define lc_dilithium_keypair_armv8 DILITHIUM_F(keypair_armv8)
+#define lc_dilithium_keypair_from_seed_armv8 \
+ DILITHIUM_F(keypair_from_seed_armv8)
+#define lc_dilithium_sign_armv8 DILITHIUM_F(sign_armv8)
+#define lc_dilithium_sign_ctx_armv8 DILITHIUM_F(sign_ctx_armv8)
+#define lc_dilithium_sign_init_armv8 DILITHIUM_F(sign_init_armv8)
+#define lc_dilithium_sign_update_armv8 DILITHIUM_F(sign_update_armv8)
+#define lc_dilithium_sign_final_armv8 DILITHIUM_F(sign_final_armv8)
+#define lc_dilithium_verify_armv8 DILITHIUM_F(verify_armv8)
+#define lc_dilithium_verify_ctx_armv8 DILITHIUM_F(verify_ctx_armv8)
+#define lc_dilithium_verify_init_armv8 DILITHIUM_F(verify_init_armv8)
+#define lc_dilithium_verify_update_armv8 DILITHIUM_F(verify_update_armv8)
+#define lc_dilithium_verify_final_armv8 DILITHIUM_F(verify_final_armv8)
+
+/* ARMv7 Implementation */
+#define armv7_ntt_asm_smull DILITHIUM_F(armv7_ntt_asm_smull)
+#define armv7_inv_ntt_asm_smull DILITHIUM_F(armv7_inv_ntt_asm_smull)
+#define armv7_poly_pointwise_invmontgomery_asm_smull \
+ DILITHIUM_F(armv7_poly_pointwise_invmontgomery_asm_smull)
+#define armv7_poly_pointwise_acc_invmontgomery_asm_smull \
+ DILITHIUM_F(armv7_poly_pointwise_acc_invmontgomery_asm_smull)
+#define poly_uniform_armv7 DILITHIUM_F(poly_uniform_armv7)
+#define armv7_poly_reduce_asm DILITHIUM_F(armv7_poly_reduce_asm)
+#define armv7_rej_uniform_asm DILITHIUM_F(armv7_rej_uniform_asm)
+#define lc_dilithium_keypair_armv7 DILITHIUM_F(keypair_armv7)
+#define lc_dilithium_keypair_from_seed_armv7 \
+ DILITHIUM_F(keypair_from_seed_armv7)
+#define lc_dilithium_sign_armv7 DILITHIUM_F(sign_armv7)
+#define lc_dilithium_sign_ctx_armv7 DILITHIUM_F(sign_ctx_armv7)
+#define lc_dilithium_sign_init_armv7 DILITHIUM_F(sign_init_armv7)
+#define lc_dilithium_sign_update_armv7 DILITHIUM_F(sign_update_armv7)
+#define lc_dilithium_sign_final_armv7 DILITHIUM_F(sign_final_armv7)
+#define lc_dilithium_verify_armv7 DILITHIUM_F(verify_armv7)
+#define lc_dilithium_verify_ctx_armv7 DILITHIUM_F(verify_ctx_armv7)
+#define lc_dilithium_verify_init_armv7 DILITHIUM_F(verify_init_armv7)
+#define lc_dilithium_verify_update_armv7 DILITHIUM_F(verify_update_armv7)
+#define lc_dilithium_verify_final_armv7 DILITHIUM_F(verify_final_armv7)
+
+/* RISCV 64 ASM Implementation */
+#define lc_dilithium_keypair_riscv64 DILITHIUM_F(keypair_riscv64)
+#define lc_dilithium_keypair_from_seed_riscv64 \
+ DILITHIUM_F(keypair_from_seed_riscv64)
+#define lc_dilithium_sign_riscv64 DILITHIUM_F(sign_riscv64)
+#define lc_dilithium_sign_ctx_riscv64 DILITHIUM_F(sign_ctx_riscv64)
+#define lc_dilithium_sign_init_riscv64 DILITHIUM_F(sign_init_riscv64)
+#define lc_dilithium_sign_update_riscv64 DILITHIUM_F(sign_update_riscv64)
+#define lc_dilithium_sign_final_riscv64 DILITHIUM_F(sign_final_riscv64)
+#define lc_dilithium_verify_riscv64 DILITHIUM_F(verify_riscv64)
+#define lc_dilithium_verify_ctx_riscv64 DILITHIUM_F(verify_ctx_riscv64)
+#define lc_dilithium_verify_init_riscv64 DILITHIUM_F(verify_init_riscv64)
+#define lc_dilithium_verify_update_riscv64 DILITHIUM_F(verify_update_riscv64)
+#define lc_dilithium_verify_final_riscv64 DILITHIUM_F(verify_final_riscv64)
+#define dilithium_ntt_8l_rv64im DILITHIUM_F(ntt_8l_rv64im)
+#define dilithium_intt_8l_rv64im DILITHIUM_F(intt_8l_rv64im)
+#define dilithium_poly_basemul_8l_init_rv64im \
+ DILITHIUM_F(poly_basemul_8l_init_rv64im)
+#define dilithium_poly_basemul_8l_acc_rv64im \
+ DILITHIUM_F(poly_basemul_8l_acc_rv64im)
+#define dilithium_poly_basemul_8l_acc_end_rv64im \
+ DILITHIUM_F(poly_basemul_8l_acc_end_rv64im)
+#define dilithium_poly_basemul_8l_rv64im DILITHIUM_F(poly_basemul_8l_rv64im)
+#define dilithium_poly_reduce_rv64im DILITHIUM_F(poly_reduce_rv64im)
+
+/* RISCV 64 RVV Implementation */
+#define lc_dilithium_keypair_riscv64_rvv DILITHIUM_F(keypair_riscv64_rvv)
+#define lc_dilithium_keypair_from_seed_riscv64_rvv \
+ DILITHIUM_F(keypair_from_seed_riscv64_rvv)
+#define lc_dilithium_sign_riscv64_rvv DILITHIUM_F(sign_riscv64_rvv)
+#define lc_dilithium_sign_ctx_riscv64_rvv DILITHIUM_F(sign_ctx_riscv64_rvv)
+#define lc_dilithium_sign_init_riscv64_rvv DILITHIUM_F(sign_init_riscv64_rvv)
+#define lc_dilithium_sign_update_riscv64_rvv \
+ DILITHIUM_F(sign_update_riscv64_rvv)
+#define lc_dilithium_sign_final_riscv64_rvv DILITHIUM_F(sign_final_riscv64_rvv)
+#define lc_dilithium_verify_riscv64_rvv DILITHIUM_F(verify_riscv64_rvv)
+#define lc_dilithium_verify_ctx_riscv64_rvv DILITHIUM_F(verify_ctx_riscv64_rvv)
+#define lc_dilithium_verify_init_riscv64_rvv \
+ DILITHIUM_F(verify_init_riscv64_rvv)
+#define lc_dilithium_verify_update_riscv64_rvv \
+ DILITHIUM_F(verify_update_riscv64_rvv)
+#define lc_dilithium_verify_final_riscv64_rvv \
+ DILITHIUM_F(verify_final_riscv64_rvv)
+#define dilithium_ntt_8l_rvv DILITHIUM_F(ntt_8l_rvv)
+#define dilithium_intt_8l_rvv DILITHIUM_F(intt_8l_rvv)
+#define dilithium_poly_basemul_8l_rvv DILITHIUM_F(poly_basemul_8l_rvv)
+#define dilithium_poly_basemul_acc_8l_rvv DILITHIUM_F(poly_basemul_acc_8l_rvv)
+#define dilithium_ntt2normal_order_8l_rvv DILITHIUM_F(ntt2normal_order_8l_rvv)
+#define dilithium_normal2ntt_order_8l_rvv DILITHIUM_F(normal2ntt_order_8l_rvv)
+#define dilithium_poly_reduce_rvv DILITHIUM_F(poly_reduce_rvv)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_TYPE_H */
diff --git a/lib/freebl/leancrypto/dilithium_zetas.h b/lib/freebl/leancrypto/dilithium_zetas.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_zetas.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_ZETAS_H
+#define DILITHIUM_ZETAS_H
+
+#include "dilithium_type.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const int32_t dilithium_zetas[LC_DILITHIUM_N];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_ZETAS_H */
diff --git a/lib/freebl/leancrypto/errno_private-base.h b/lib/freebl/leancrypto/errno_private-base.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/errno_private-base.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _ASM_GENERIC_ERRNO_BASE_H
+#define _ASM_GENERIC_ERRNO_BASE_H
+
+#define EPERM 1 /* Operation not permitted */
+#define ENOENT 2 /* No such file or directory */
+#define ESRCH 3 /* No such process */
+#define EINTR 4 /* Interrupted system call */
+#define EIO 5 /* I/O error */
+#define ENXIO 6 /* No such device or address */
+#define E2BIG 7 /* Argument list too long */
+#define ENOEXEC 8 /* Exec format error */
+#define EBADF 9 /* Bad file number */
+#define ECHILD 10 /* No child processes */
+#define EAGAIN 11 /* Try again */
+#define ENOMEM 12 /* Out of memory */
+#define EACCES 13 /* Permission denied */
+#define EFAULT 14 /* Bad address */
+#define ENOTBLK 15 /* Block device required */
+#define EBUSY 16 /* Device or resource busy */
+#define EEXIST 17 /* File exists */
+#define EXDEV 18 /* Cross-device link */
+#define ENODEV 19 /* No such device */
+#define ENOTDIR 20 /* Not a directory */
+#define EISDIR 21 /* Is a directory */
+#define EINVAL 22 /* Invalid argument */
+#define ENFILE 23 /* File table overflow */
+#define EMFILE 24 /* Too many open files */
+#define ENOTTY 25 /* Not a typewriter */
+#define ETXTBSY 26 /* Text file busy */
+#define EFBIG 27 /* File too large */
+#define ENOSPC 28 /* No space left on device */
+#define ESPIPE 29 /* Illegal seek */
+#define EROFS 30 /* Read-only file system */
+#define EMLINK 31 /* Too many links */
+#define EPIPE 32 /* Broken pipe */
+#define EDOM 33 /* Math argument out of domain of func */
+#define ERANGE 34 /* Math result not representable */
+
+#endif
diff --git a/lib/freebl/leancrypto/errno_private.h b/lib/freebl/leancrypto/errno_private.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/errno_private.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _ASM_GENERIC_ERRNO_H
+#define _ASM_GENERIC_ERRNO_H
+
+#include "errno_private-base.h"
+
+#define EDEADLK 35 /* Resource deadlock would occur */
+#define ENAMETOOLONG 36 /* File name too long */
+#define ENOLCK 37 /* No record locks available */
+
+/*
+ * This error code is special: arch syscall entry code will return
+ * -ENOSYS if users try to call a syscall that doesn't exist. To keep
+ * failures of syscalls that really do exist distinguishable from
+ * failures due to attempts to use a nonexistent syscall, syscall
+ * implementations should refrain from returning -ENOSYS.
+ */
+#define ENOSYS 38 /* Invalid system call number */
+
+#define ENOTEMPTY 39 /* Directory not empty */
+#define ELOOP 40 /* Too many symbolic links encountered */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define ENOMSG 42 /* No message of desired type */
+#define EIDRM 43 /* Identifier removed */
+#define ECHRNG 44 /* Channel number out of range */
+#define EL2NSYNC 45 /* Level 2 not synchronized */
+#define EL3HLT 46 /* Level 3 halted */
+#define EL3RST 47 /* Level 3 reset */
+#define ELNRNG 48 /* Link number out of range */
+#define EUNATCH 49 /* Protocol driver not attached */
+#define ENOCSI 50 /* No CSI structure available */
+#define EL2HLT 51 /* Level 2 halted */
+#define EBADE 52 /* Invalid exchange */
+#define EBADR 53 /* Invalid request descriptor */
+#define EXFULL 54 /* Exchange full */
+#define ENOANO 55 /* No anode */
+#define EBADRQC 56 /* Invalid request code */
+#define EBADSLT 57 /* Invalid slot */
+
+#define EDEADLOCK EDEADLK
+
+#define EBFONT 59 /* Bad font file format */
+#define ENOSTR 60 /* Device not a stream */
+#define ENODATA 61 /* No data available */
+#define ETIME 62 /* Timer expired */
+#define ENOSR 63 /* Out of streams resources */
+#define ENONET 64 /* Machine is not on the network */
+#define ENOPKG 65 /* Package not installed */
+#define EREMOTE 66 /* Object is remote */
+#define ENOLINK 67 /* Link has been severed */
+#define EADV 68 /* Advertise error */
+#define ESRMNT 69 /* Srmount error */
+#define ECOMM 70 /* Communication error on send */
+#define EPROTO 71 /* Protocol error */
+#define EMULTIHOP 72 /* Multihop attempted */
+#define EDOTDOT 73 /* RFS specific error */
+#define EBADMSG 74 /* Not a data message */
+#define EOVERFLOW 75 /* Value too large for defined data type */
+#define ENOTUNIQ 76 /* Name not unique on network */
+#define EBADFD 77 /* File descriptor in bad state */
+#define EREMCHG 78 /* Remote address changed */
+#define ELIBACC 79 /* Can not access a needed shared library */
+#define ELIBBAD 80 /* Accessing a corrupted shared library */
+#define ELIBSCN 81 /* .lib section in a.out corrupted */
+#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
+#define ELIBEXEC 83 /* Cannot exec a shared library directly */
+#define EILSEQ 84 /* Illegal byte sequence */
+#define ERESTART 85 /* Interrupted system call should be restarted */
+#define ESTRPIPE 86 /* Streams pipe error */
+#define EUSERS 87 /* Too many users */
+#define ENOTSOCK 88 /* Socket operation on non-socket */
+#define EDESTADDRREQ 89 /* Destination address required */
+#define EMSGSIZE 90 /* Message too long */
+#define EPROTOTYPE 91 /* Protocol wrong type for socket */
+#define ENOPROTOOPT 92 /* Protocol not available */
+#define EPROTONOSUPPORT 93 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
+#define EPFNOSUPPORT 96 /* Protocol family not supported */
+#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
+#define EADDRINUSE 98 /* Address already in use */
+#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
+#define ENETDOWN 100 /* Network is down */
+#define ENETUNREACH 101 /* Network is unreachable */
+#define ENETRESET 102 /* Network dropped connection because of reset */
+#define ECONNABORTED 103 /* Software caused connection abort */
+#define ECONNRESET 104 /* Connection reset by peer */
+#define ENOBUFS 105 /* No buffer space available */
+#define EISCONN 106 /* Transport endpoint is already connected */
+#define ENOTCONN 107 /* Transport endpoint is not connected */
+#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
+#define ETOOMANYREFS 109 /* Too many references: cannot splice */
+#define ETIMEDOUT 110 /* Connection timed out */
+#define ECONNREFUSED 111 /* Connection refused */
+#define EHOSTDOWN 112 /* Host is down */
+#define EHOSTUNREACH 113 /* No route to host */
+#define EALREADY 114 /* Operation already in progress */
+#define EINPROGRESS 115 /* Operation now in progress */
+#define ESTALE 116 /* Stale file handle */
+#define EUCLEAN 117 /* Structure needs cleaning */
+#define ENOTNAM 118 /* Not a XENIX named type file */
+#define ENAVAIL 119 /* No XENIX semaphores available */
+#define EISNAM 120 /* Is a named type file */
+#define EREMOTEIO 121 /* Remote I/O error */
+#define EDQUOT 122 /* Quota exceeded */
+
+#define ENOMEDIUM 123 /* No medium found */
+#define EMEDIUMTYPE 124 /* Wrong medium type */
+#define ECANCELED 125 /* Operation Canceled */
+#define ENOKEY 126 /* Required key not available */
+#define EKEYEXPIRED 127 /* Key has expired */
+#define EKEYREVOKED 128 /* Key has been revoked */
+#define EKEYREJECTED 129 /* Key was rejected by service */
+
+/* for robust mutexes */
+#define EOWNERDEAD 130 /* Owner died */
+#define ENOTRECOVERABLE 131 /* State not recoverable */
+
+#define ERFKILL 132 /* Operation not possible due to RF-kill */
+
+#define EHWPOISON 133 /* Memory page has hardware error */
+
+#endif
diff --git a/lib/freebl/leancrypto/ext_headers.h b/lib/freebl/leancrypto/ext_headers.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ext_headers.h
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef EXT_HEADERS_H
+#define EXT_HEADERS_H
+
+/******************************************************************************
+ * Generic Definitions
+ ******************************************************************************/
+#ifdef _WIN32
+#define LC_PURE
+#else
+#define LC_PURE __attribute__((pure))
+#endif
+
+/**
+ * @brief Return the size of a member variable of a data structure
+ *
+ * @param [in] struct data structure containing the member variable
+ * @param [in] member member variable name whose size shall be obtained
+ *
+ * @return size of the variable
+ */
+#define lc_member_size(struct, member) (sizeof(((struct *)0)->member))
+
+#ifdef LINUX_KERNEL
+/******************************************************************************
+ * Linux Kernel
+ ******************************************************************************/
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+/* POSIX Support */
+unsigned long getauxval(unsigned long type);
+
+static inline int
+mlock(const void *ptr, size_t len)
+{
+ (void)ptr;
+ (void)len;
+ return 0;
+}
+
+extern const int errno;
+
+static inline pid_t
+getpid(void)
+{
+ return 0;
+}
+
+#define restrict
+
+#define printf printk
+
+#ifndef assert
+#define assert(x) WARN_ON(!(x))
+#endif
+
+#define PRIu64 "lu"
+
+#define LC_DEFINE_CONSTRUCTOR(_func) void _func(void)
+#define LC_DEFINE_DESTRUCTOR(_func) void _func(void)
+
+#define SYSV_ABI
+
+typedef s64 time64_t;
+
+static inline int
+lc_get_time(time64_t *time_since_epoch)
+{
+ if (!time_since_epoch)
+ return -EINVAL;
+
+ *time_since_epoch = (time64_t)(jiffies / HZ);
+
+ return 0;
+}
+
+#define LC_FIPS_RODATA_SECTION
+
+#elif (defined(LC_EFI_ENVIRONMENT))
+/******************************************************************************
+ * UEFI support
+ ******************************************************************************/
+
+/* POSIX Support */
+#include <efi/efi.h>
+#include <efi/efilib.h>
+
+#include "errno_private.h"
+
+#define LC_DEFINE_CONSTRUCTOR(_func) \
+ void __attribute__((constructor)) _func(void)
+#define LC_DEFINE_DESTRUCTOR(_func) void __attribute__((destructor)) _func(void)
+
+#if !defined __ILP32__
+#define __WORDSIZE 64
+#else
+#define __WORDSIZE 32
+#endif
+
+typedef int pid_t;
+typedef long time_t;
+typedef long long time64_t;
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER)
+#endif
+
+#if __WORDSIZE == 64
+
+typedef unsigned long uintptr_t;
+
+#ifndef _SIZE_T
+typedef unsigned long size_t;
+#define _SIZE_T
+#endif
+
+#ifndef _SSIZE_T
+typedef long ssize_t;
+#define _SSIZE_T
+#endif
+
+#elif __WORDSIZE == 32
+
+#ifndef _UINTPTR_T
+typedef unsigned int uintptr_t;
+#define _UINTPTR_T
+#endif
+
+#error
+#ifndef _SIZE_T
+typedef unsigned int size_t;
+#define _SIZE_T
+#endif
+
+#ifndef _SSIZE_T
+typedef int ssize_t;
+#define _SSIZE_T
+#endif
+
+#endif
+
+#include "lc_memcpy_secure.h"
+
+void *memset(void *d, int c, unsigned long long n);
+
+static inline int
+mlock(const void *ptr, size_t len)
+{
+ (void)ptr;
+ (void)len;
+ return 0;
+}
+
+static inline pid_t
+getpid(void)
+{
+ return 0;
+}
+
+static inline int
+snprintf(char *restrict str, size_t size,
+ const char *restrict format, ...)
+{
+ (void)format;
+ if (size) {
+ memset(str, 0, size);
+ return (int)size - 1;
+ }
+ return 0;
+}
+
+static inline void *
+memcpy(void *d, const void *s, size_t n)
+{
+ return lc_memcpy_secure(d, n, s, n);
+}
+
+static inline size_t
+strlen(const char *str)
+{
+ size_t len = 0;
+
+ while (*str != '\0') {
+ str++;
+ len++;
+ }
+
+ return len;
+}
+
+static inline int
+lc_get_time(time64_t *time_since_epoch)
+{
+ if (!time_since_epoch)
+ return -EINVAL;
+
+ *time_since_epoch = -1;
+
+ return -EOPNOTSUPP;
+}
+
+#define SYSV_ABI __attribute__((sysv_abi))
+
+/*
+ * See https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html#Statement-Attributes
+ */
+#if __has_attribute(__fallthrough__)
+#define fallthrough __attribute__((__fallthrough__))
+#else
+#define fallthrough \
+ do { \
+ } while (0)
+#endif
+
+#ifndef assert
+#define assert(x) \
+ if (x) { \
+ Exit(EFI_ABORTED, 0, NULL); \
+ }
+#endif
+
+#ifndef INT_MAX
+#define INT_MAX 2147483647
+#endif
+
+#define stdout NULL
+
+#define printf(...) Print(L##__VA_ARGS__)
+
+#undef errno
+#define errno errno_private
+static const int errno_private = 0;
+
+#define LC_FIPS_RODATA_SECTION
+
+#define noinline __attribute__((__noinline__))
+
+#elif (defined(__CYGWIN__) || defined(_WIN32))
+/******************************************************************************
+ * Windows
+ ******************************************************************************/
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)
+
+#define LC_DEFINE_CONSTRUCTOR(_func) \
+ void __attribute__((constructor)) _func(void)
+#define LC_DEFINE_DESTRUCTOR(_func) void __attribute__((destructor)) _func(void)
+
+#else
+
+/* not used in our case */
+#define LC_DEFINE_CONSTRUCTOR(_func) void _func(void)
+#define LC_DEFINE_DESTRUCTOR(_func) void _func(void)
+
+#endif
+
+/*
+ * Replace GCC-specific alternative keywords
+ * see https://gcc.gnu.org/onlinedocs/gcc/Alternate-Keywords.html
+ */
+#ifndef __GNUC__
+#define __asm__ asm
+#define __volatile__ volatile
+#endif
+
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200112L
+#endif
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#ifndef MB_LEN_MAX
+#define MB_LEN_MAX 16
+#endif
+
+static inline int
+mlock(const void *ptr, size_t len)
+{
+ (void)ptr;
+ (void)len;
+ return 0;
+}
+
+#define SYSV_ABI __attribute__((sysv_abi))
+
+/*
+ * See https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html#Statement-Attributes
+ */
+#ifdef _WIN32
+#define fallthrough \
+ do { \
+ } while (0)
+#else
+#if __has_attribute(__fallthrough__)
+#define fallthrough __attribute__((__fallthrough__))
+#else
+#define fallthrough \
+ do { \
+ } while (0)
+#endif
+#endif
+
+typedef int64_t time64_t;
+
+static inline int
+lc_get_time(time64_t *time_since_epoch)
+{
+ struct timespec tp = { 0 };
+
+ /* don't need lc_get_time for ml_dsa support */
+ return -EINVAL;
+}
+
+#define LC_FIPS_RODATA_SECTION
+
+#define noinline __attribute__((__noinline__))
+
+#else /* LINUX_KERNEL */
+/******************************************************************************
+ * POSIX
+ ******************************************************************************/
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)
+
+#define LC_DEFINE_CONSTRUCTOR(_func) \
+ void __attribute__((constructor)) _func(void)
+#define LC_DEFINE_DESTRUCTOR(_func) void __attribute__((destructor)) _func(void)
+
+#else
+
+#error "Constructor / destructor not defined for compiler"
+
+#endif
+
+/*
+ * Replace GCC-specific alternative keywords
+ * see https://gcc.gnu.org/onlinedocs/gcc/Alternate-Keywords.html
+ */
+#ifndef __GNUC__
+#define __asm__ asm
+#define __volatile__ volatile
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef MB_LEN_MAX
+#define MB_LEN_MAX 16
+#endif
+
+#define SYSV_ABI
+
+/*
+ * See https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html#Statement-Attributes
+ */
+/* GCC 4 doesn't support has_attributes() fallthrough, just define
+ * the null case */
+#define fallthrough \
+ do { \
+ } while (0)
+
+typedef int64_t time64_t;
+
+static inline int
+lc_get_time(time64_t *time_since_epoch)
+{
+ struct timespec tp = { 0 };
+
+ if (!time_since_epoch)
+ return -EINVAL;
+
+ if (clock_gettime(CLOCK_REALTIME, &tp) == 0) {
+ *time_since_epoch = tp.tv_sec;
+ return 0;
+ }
+
+ *time_since_epoch = (time64_t)-1;
+ return -errno;
+}
+
+/*
+ * FIPS 140 integrity check cannot check the .rodata section. Thus move all
+ * relevant data to teh fips_rodata section.
+ */
+#if defined __ELF__
+#define LC_FIPS_RODATA_SECTION_NAME_START __start_fips_rodata
+#define LC_FIPS_RODATA_SECTION_NAME_STOP __stop_fips_rodata
+#define LC_FIPS_RODATA_SECTION_NAME "fips_rodata"
+#define LC_FIPS_RODATA_SECTION \
+ __attribute__((section(LC_FIPS_RODATA_SECTION_NAME)))
+#else
+#define LC_FIPS_RODATA_SECTION
+#endif
+
+#define noinline __attribute__((__noinline__))
+
+#endif /* LINUX_KERNEL */
+
+/******************************************************************************
+ * Generic Definitions after all includes are present
+ ******************************************************************************/
+
+#ifndef ENOPKG
+#define ENOPKG 254 /* Package not installed */
+#endif
+#ifndef EKEYREJECTED
+#define EKEYREJECTED 253 /* Key was rejected by service */
+#endif
+#ifndef ENOKEY
+#define ENOKEY 252 /* Key not found */
+#endif
+
+#endif /* EXT_HEADERS_H */
diff --git a/lib/freebl/leancrypto/fetch_ml_dsa.sh b/lib/freebl/leancrypto/fetch_ml_dsa.sh
new file mode 100755
--- /dev/null
+++ b/lib/freebl/leancrypto/fetch_ml_dsa.sh
@@ -0,0 +1,451 @@
+#!/bin/sh
+declare -a versions
+declare -a modes
+declare -a type
+versions=(ml_dsa_44 ml_dsa_65 ml_dsa_87)
+modes=(2 3 5)
+types=(44 65 87)
+keep=0
+clean=0
+update=0
+clean_gen=0
+branch="master"
+
+library=leancrypto
+git_url=https://github.com/smuellerDD/leancrypto
+ml_dsa_src=ml-dsa/src
+#cpufeatures.h
+#test_helper.h
+
+header_gen_list=( lc_dilithium_44.h lc_dilithium_65.h lc_dilithium_87.h)
+header_gen_internal_list=( lc_memory_support.h )
+header_internal_list=( alignment.h atomic_bool.h atomic.h binhexbin.h
+ bitshift_be.h bitshift.h bitshift_le.h bool.h build_bug_on.h
+ compare.h conv_be_le.h errno_private-base.h errno_private.h
+ ext_headers.h fips_mode.h helper.h initialization.h lc_init.h
+ lc_memcmp_secure.h lc_memcpy_secure.h lc_memory_support.h.in
+ lc_memset_secure.h lc_status.h left_encode.h math_helper.h
+ mutex_w.h null_buffer.h ret_checkers.h rotate.h
+ sidechannel_resistantce.h signature_domain_separation.h
+ small_stack_support.h timecop.h visibility.h xor256.h xor.h )
+header_api_list=(dilithium_type.h lc_dilithium.h)
+header_list=( dilithium_debug.h dilithium_ntt.h dilithium_pack.h dilithium_pct.h
+ dilithium_poly.h dilithium_poly_c.h dilithium_poly_common.h
+ dilithium_polyvec.h dilithium_polyvec_c.h dilithium_reduce.h
+ dilithium_rounding.h dilithium_service_helpers.h
+ dilithium_signature_c.h dilithium_signature_impl.h dilithium_zetas.h)
+internal_list=(signature_domain_separation.c)
+common_list=(dilithium_api.c dilithium_zetas.c)
+specific_list=( dilithium_ntt.c dilithium_poly.c dilithium_rounding.c
+ dilithium_signature_c.c dilithium_signature_helper.c)
+
+
+process_versions()
+{
+ echo "process_version($3)" >&2
+ local -n vers=$1
+ local -n mods=$2
+ declare -p vers
+ declare -p mods
+ vers=()
+ mods=()
+ for i in ${3//;/ }
+ do
+ ver=${i%%:*}
+ mod=${i##*:}
+ echo "spec=/$i/ ver=/$ver/ mod=/$mod/" >&2
+ if [[ "$ver" = "" || "$mod" = "" ]]; then
+ echo "invalid version spec \"$i\"" >&2
+ return 1
+ fi
+ vers+=($ver)
+ mods+=($mod)
+ done
+ return 0
+}
+
+usage()
+{
+ echo "usage: ${0##*/} [--keep_intermediate] [--clean_library]" >&2
+ echo " [--version list_of_versions_and_modes]" >&2
+ echo " [--update] [--branch]" >&2
+ echo "--keep_intermediate don't delete intermediate files used to generate headers" >&2
+ echo "--clean_library remove old library directory before starting" >&2
+ echo "--clean_generated remove generated files before starting" >&2
+ echo "--versions ';' separated list of versions and modes. of the form:" >&2
+ echo " version:mode. example:" >&2
+ echo " \"ml_dsa_44_ref:2;ml_dsa_65_avx:3;ml_dsa_65_ref:3\"" >&2
+ echo "--update if liboq directory exists, update it" >&2
+ echo "--branch select the git branch to use" >&2
+ exit 1
+}
+
+
+while true ; do
+ case "$1" in
+ --keep_intermediate|-k) keep=1; shift
+ ;;
+ --clean_leancrypto|-C)
+ clean=1; shift
+ ;;
+ --clean_generated|-c)
+ clean_gen=1; shift
+ ;;
+ -versions|-v)
+ process_versions versions modes "$2"
+ if (( $? != 0 || ${#modes[@]} != ${#versions[@]} )); then
+ echo "not all versions have a mode \"$2\"" >&2
+ echo "number modes=${#modes}, number versions=${#versions}" >&2
+ echo "versions:${versions[*]}" >&2
+ echo "modes:${modes[*]}" >&2
+ usage
+ fi
+ shift 2
+ ;;
+ --update|-u)
+ update=1; shift
+ ;;
+ --branch|-b)
+ branch="$2"; shift 2
+ ;;
+ "")
+ break
+ ;;
+ *)
+ echo "$0: Unknown option: \"$1\"" >&2
+ usage
+ esac
+done
+
+
+# first fetch and builds leancrypto
+top=$(pwd)
+echo "--------------- fetching $library"
+if [ -d $library ]; then
+ if (( $clean == 1 )); then
+ rm -rf $library
+ git clone -b main $giturl
+ elif (( $update == 1 )); then
+ (cd $library ; git checkout main ; git pull)
+ fi
+else
+ git clone -b main $giturl
+fi
+cd $library
+git checkout $branch
+if [ $? != 0 ]; then
+ echo "branch \"$branch\" not found" >&2
+ echo "valid branches and tags are: " >&2
+ git branch -l >&2
+ git tag -l >&2
+ exit 1
+fi
+libdir=$(pwd)
+ml_dsa_dir=${libdir}/${ml_dsa_src}
+if [ ! -d $ml_dsa_dir ]; then
+ echo "ml_dsa not avaliable on this branch or tag ${branch}" >&2
+ exit 1
+fi
+
+# now set up everything
+if (( clean_gen == 1 )); then
+ (cd $top ; rm -f ml_dsa_* fips202.h randombytes.h $library_git_version.txt )
+fi
+rm -rf build
+mkdir build
+meson setup build -Ddilithium_ed25519=disabled -Ddilithium_ed448=disabled
+cd build
+mkdir sed_scripts
+mkdir generated_code
+build=$(pwd)
+sed_scripts=${build}/sed_scripts
+generated_code=${build}/generated_code
+cd ${libdir}
+
+# these are used to allow is to create 'max' defines
+# we find the value from each of the different lengths and then we
+# keep the maximum, so we can define our max value in terms of that value
+pub_size=0
+pub_define="UNKNOWN"
+priv_size=0
+priv_define="UNKNOWN"
+sig_size=0
+sig_define="UNKNOWN"
+seed_size=0
+seed_define="UNKNOWN"
+
+# ml_dsa_api.h is a completely generated header. It basically resolves the defines in the various
+# xxxx_sign.h headers, which uses macros redefine all the functions, but they use the same macro and
+# function names, so the we can't have a single file that includes all the definitions. library itself
+# handles this by creating their own c stubs for each of the functions. We've already name deconflicted
+# the filenames, so since we have this program we can safely create a proper header file.
+#cat > ${generated_code}/ml_dsa_api.h << __EoF__
+#ifndef ML_DSA_API_H
+#define ML_DSA_API_H
+#// This is a generated file from the various XXX_sign.h files
+#include <stddef.h>
+#include "ml_dsa_apit.h"
+#__EoF__
+cat > ${generated_code}/ml_dsa_apit.h << __EoF__
+// This is a generated file from the various XXX_sign.h files
+#ifndef ML_DSA_APIT_H
+#define ML_DSA_APIT_H
+
+// to make the function defines work
+#ifndef RNDBYTES
+#define RNDBYTES 32
+#endif
+__EoF__
+
+for i in ${commonlist[@]}
+do
+ cp ${ml_dsa_dir}/$i ${generated_code}/
+done
+for file in ${header_internal_list[@]}
+do
+ cp ${libdir}/internal/api/${file} ${generated_code}/${file}
+done
+for file in ${header_gen_internal_list=[@]}
+do
+ cp ${libdir}/build/internal/api/${file} ${generated_code}/${file}
+done
+for file in ${header_gen_list[@]}
+do
+ cp ${libdir}/build/ml-dsa/api/${file} ${generated_code}/${file}
+done
+for file in ${header_api_list[@]}
+do
+ cp ${ml_dsa_dir}/../api/${file} ${generated_code}/${file}
+done
+for file in ${header_list[@]}
+do
+ cp ${ml_dsa_dir}/${file} ${generated_code}/${file}
+done
+for file in ${internal_list[@]}
+do
+ cp ${libdir}/internal/src/${file} ${generated_code}/${file}
+done
+for file in ${common_list[@]}
+do
+ target_file=${file#dilithium}
+ cp ${ml_dsa_dir}/${file} ${generated_code}/mldsa${target_file}
+done
+# now process the each of the versions we are woring on.
+for i in ${!versions[@]}
+do
+ sig=${versions[$i]}
+ mode=${modes[$i]}
+ #ls *.h | sed "s/\(.*\)/s;\1;${sig}_\1;/" > ${sed_scripts}/header_rename_${sig}.sed
+ #echo "s;<oqs/oqs.h>;\"blapi.h\";" >> ${sed_scripts}/header_rename_${sig}.sed
+ #echo "s;OQS_API;;g" >> ${sed_scripts}/header_rename_${sig}.sed
+ echo "#define LC_DILITHIUM_TYPE_${types[$i]} 1" > ${generated_code}/${sig}_def_header.h
+
+ echo "------------------------- Processing $sig mode=$mode ... "
+ for file in ${specific_list[@]}
+ do
+ target_file=${file#dilithium}
+ echo -n -e "fixup ${file} to ${sig}_${file}\r"
+ tmp=${generated_code}/${sig}${file}_tmp
+ cat ${generated_code}/${sig}_def_header.h ${ml_dsa_dir}/${file} > ${tmp}
+ #sed -f ${sed_scripts}/header_rename_${sig}.sed ${file} > ${tmp}
+ #unifdef -DLC_DILITHIUM_MODE=${mode} ${tmp} > ${generated_code}/${sig}_${file}
+ cp ${tmp} ${generated_code}/${sig}${target_file}
+ rm ${tmp}
+ done
+ echo ""
+ cd ${generated_code}
+ #process ${sig}_sign.h to get all the function defines into our ml_dsa_api.h
+# echo "" >> ${generated_code}/ml_dsa_api.h
+# echo "// from ${sig}_sign.h " >> ${generated_code}/ml_dsa_api.h
+#grep -v '^#' ${sig}_sign.h | grep -v "^$" | sed -e "s/crypto_sign/pqcrystals_${sig}/" >> ${generated_code}/ml_dsa_api.h
+
+ # now let's get unrolled defines for the various sizes (keys, signatures, seeds). We use a C program
+ # and make C evaluate the final value of several defines which are calculated on the fly.
+# echo "#include \"${sig}_params.h\"" > ./${sig}_extract_defines.c
+# echo "const char *sign_ver=\"${sig^^}\";" >> ./${sig}_extract_defines.c
+# echo "const char *l_sign_ver=\"${sig}\";" >> ./${sig}_extract_defines.c
+# cat >> ./${sig}_extract_defines.c << __EoF__
+##include <stdio.h>
+#int main(int argc, char **arv) {
+# printf("\n");
+# printf("// from %s_sign.h\n", l_sign_ver);
+# printf("#define %s_PUBLICKEY_BYTES %d\n", sign_ver, CRYPTO_PUBLICKEYBYTES);
+# printf("#define %s_PRIVATEKEY_BYTES %d\n", sign_ver, CRYPTO_SECRETKEYBYTES);
+# printf("#define %s_SIGNATURE_BYTES %d\n", sign_ver, CRYPTO_BYTES);
+# printf("#define %s_SEED_BYTES %d\n", sign_ver, SEEDBYTES);
+#}
+#__EoF__
+# cc -o ${sig}_extract_defines ${sig}_extract_defines.c
+# ./${sig}_extract_defines >> ${generated_code}/ml_dsa_apit.h
+# new_size=$(./${sig}_extract_defines | grep PUBLICKEY | awk '{ print $NF }')
+# if (( new_size > pub_size )); then
+# pub_size=$new_size
+# pub_define=${sig^^}
+# fi
+# new_size=$(./${sig}_extract_defines | grep PRIVATEKEY | awk '{ print $NF }')
+# if (( new_size > priv_size )); then
+# priv_size=$new_size
+# priv_define=${sig^^}
+# fi
+# new_size=$(./${sig}_extract_defines | grep SIGNATURE | awk '{ print $NF }')
+# if (( new_size > sig_size )); then
+# sig_size=$new_size
+# sig_define=${sig^^}
+# fi
+# new_size=$(./${sig}_extract_defines | grep SEED | awk '{ print $NF }')
+# if (( new_size > seed_size )); then
+# seed_size=$new_size
+# seed_define=${sig^^}
+# fi
+# if (( $keep != 1 )); then
+# rm ${sig}_extract_defines*
+# fi
+ # sigh clang20 knows about type c, but we only have cpp in our
+ # .clang-format file, tell clang to use .cpp
+ for file in *.[c]
+ do
+ base=$(basename $file .c)
+ echo clang-format --assume-filename=${base}.cpp --sort-includes=false -i ${file}
+ cat ${file} | clang-format --assume-filename=${base}.cpp --sort-includes=false >${file}.clang
+ mv ${file}.clang ${file}
+ done
+ for file in *.[h]
+ do
+ clang-format --sort-includes=false -i ${file}
+ done
+ tar cf - . | (cd ${top} ; tar xf -)
+ cd ${libarary}
+done
+
+#if (( $version_found == 0 )); then
+# echo "no requested versions (${versions[*]}) found in this branch ($branch)" >&2
+# echo "valid versions are:" >&2
+# ls ${ml_dsa_dir} | grep pqcrystals | sed 's;pqcrystals-dilithium-standard[_|-];;' >&2
+# exit 1
+#fi
+
+echo "" >> ${generated_code}/ml_dsa_apit.h
+#echo "// create the max defines" >> ${generated_code}/ml_dsa_apit.h
+#echo "#define MAX_MLDSA_REF_PRIVATE_KEY_LEN ${priv_define}_PRIVATEKEY_BYTES" >> ${generated_code}/ml_dsa_apit.h
+#echo "#define MAX_MLDSA_REF_PUBLIC_KEY_LEN ${priv_define}_PUBLICKEY_BYTES" >> ${generated_code}/ml_dsa_apit.h
+#echo "#define MAX_MLDSA_REF_SIGNATURE_LEN ${priv_define}_SIGNATURE_BYTES" >> ${generated_code}/ml_dsa_apit.h
+#echo "#define MAX_MLDSA_REF_SEED_LEN ${priv_define}_SEED_BYTES" >> ${generated_code}/ml_dsa_apit.h
+#echo "#endif /* ML_DSA_API_H */" >> ${generated_code}/ml_dsa_api.h
+echo "#endif /* ML_DSA_APIT_H */" >> ${generated_code}/ml_dsa_apit.h
+
+cp ${generated_code}/ml_dsa_api.h ${top}
+cp ${generated_code}/ml_dsa_apit.h ${top}
+cd ${top}
+echo "------------------------- wrote ml_dsa_apit.h : ... "
+# remember what version we are using
+(cd ${library}; git branch -v) > leancrypto_git_version.txt
+# Now write out the fixed files
+cat > fips202.h << __EoF__
+// SPDX-License-Identifier: MIT
+// NSS SHA3 bindings for ML-DSA liboqs
+
+#ifndef FIPS202_H
+#define FIPS202_H
+
+#include <blapi.h>
+
+#define SHAKE128_RATE 168
+#define shake128 SHAKE_128_HashBuf
+
+#define SHAKE256_RATE SHA3_256_BLOCK_LENGTH
+#define shake256 SHAKE_256_HashBuf
+
+#ifdef NOT_SUPPORTED
+#define SHA3_256_RATE SHA3_256_BLOCK_LENGTH
+#define sha3_256 OQS_SHA3_sha3_256
+#define sha3_256_inc_init OQS_SHA3_sha3_256_inc_init
+#define sha3_256_inc_absorb OQS_SHA3_sha3_256_inc_absorb
+#define sha3_256_inc_finalize OQS_SHA3_sha3_256_inc_finalize
+#define sha3_256_inc_ctx_clone OQS_SHA3_sha3_256_inc_ctx_clone
+#define sha3_256_inc_ctx_release OQS_SHA3_sha3_256_inc_ctx_release
+
+#define SHA3_384_RATE SHA3_384_BLOCK_LENGTH
+#define sha3_384 OQS_SHA3_sha3_384
+#define sha3_384_inc_init OQS_SHA3_sha3_384_inc_init
+#define sha3_384_inc_absorb OQS_SHA3_sha3_384_inc_absorb
+#define sha3_384_inc_finalize OQS_SHA3_sha3_384_inc_finalize
+#define sha3_384_inc_ctx_clone OQS_SHA3_sha3_384_inc_ctx_clone
+#define sha3_384_inc_ctx_release OQS_SHA3_sha3_384_inc_ctx_release
+
+#define SHA3_512_RATE SHA3_512_BLOCK_LENGTH
+#define sha3_512 OQS_SHA3_sha3_512
+#define sha3_512_inc_init OQS_SHA3_sha3_512_inc_init
+#define sha3_512_inc_absorb OQS_SHA3_sha3_512_inc_absorb
+#define sha3_512_inc_finalize OQS_SHA3_sha3_512_inc_finalize
+#define sha3_512_inc_ctx_clone OQS_SHA3_sha3_512_inc_ctx_clone
+#define sha3_512_inc_ctx_release OQS_SHA3_sha3_512_inc_ctx_release
+#endif
+
+typedef SHAKE_128Context *shake128incctx;
+#define shake128_inc_init(ptr) \
+ (*(ptr))=SHAKE_128_NewContext(); \
+ SHAKE_128_Begin(*(ptr))
+#define shake128_inc_absorb(ptr, input, inlen) \
+ SHAKE_128_Absorb(*(ptr), input, inlen)
+#define shake128_inc_finalize(ptr)
+#define shake128_inc_squeeze(output, outlen, ptr) \
+ SHAKE_128_SqueezeEnd(*(ptr), output, outlen)
+#define shake128_inc_ctx_release(ptr) \
+ SHAKE_128_DestroyContext(*(ptr), PR_TRUE)
+#define shake128_inc_ctx_reset(ptr) \
+ SHAKE_128_Begin(ptr)
+#ifdef NOT_SUPPORTED
+#define shake128_inc_ctx_clone OQS_SHA3_shake128_inc_ctx_clone
+#endif
+
+typedef SHAKE_256Context *shake256incctx;
+#define shake256_inc_init(ptr) \
+ (*(ptr))=SHAKE_256_NewContext(); \
+ SHAKE_256_Begin(*(ptr))
+#define shake256_inc_absorb(ptr, input, inlen) \
+ SHAKE_256_Absorb(*(ptr), input, inlen)
+#define shake256_inc_finalize(ptr)
+#define shake256_inc_squeeze(output, outlen, ptr) \
+ SHAKE_256_SqueezeEnd(*(ptr), output, outlen)
+#define shake256_inc_ctx_release(ptr) \
+ SHAKE_256_DestroyContext(*(ptr), PR_TRUE)
+#define shake256_inc_ctx_reset(ptr) \
+ SHAKE_256_Begin(*ptr)
+
+#ifdef NOT_SUPPORTED
+#define shake128_absorb_once OQS_SHA3_shake128_absorb_once
+void OQS_SHA3_shake128_absorb_once(shake128incctx *state, const uint8_t *in, size_t inlen);
+
+#define shake256_absorb_once OQS_SHA3_shake256_absorb_once
+void OQS_SHA3_shake256_absorb_once(shake256incctx *state, const uint8_t *in, size_t inlen);
+#endif
+
+#define shake128_squeezeblocks(OUT, NBLOCKS, STATE) shake128_inc_squeeze(OUT, (NBLOCKS)*SHAKE128_RATE, STATE)
+
+#define shake256_squeezeblocks(OUT, NBLOCKS, STATE) shake256_inc_squeeze(OUT, (NBLOCKS)*SHAKE256_RATE, STATE)
+
+#endif
+__EoF__
+cat > randombytes.h << __EoF__
+// SPDX-License-Identifier: MIT
+// NSS stub for liboqs randombytes.h
+
+#ifndef RANDOMBYTES_H
+#define RANDOMBYTES_H
+
+// run the random number generator through our mldsa code so we can support
+// CKA_SEED (both acquiring it and generating keys from it) and
+// DETERMINISTIC signatures (by returning zeros from the RNG)
+void mldsa_GetRandomBytes(unsigned char *rdn, int bytes);
+#define randombytes mldsa_GetRandomBytes
+
+#endif
+__EoF__
+clang-format -sort-includes=false -i randombytes.h fips202.h ml_dsa_apit.h ml_dsa_api.h
+#output our generated files for review
+echo "ml_dsa_abit.h -----------------"
+cat ml_dsa_apit.h
+echo "git version used -----------------"
+cat oqs_git_version.txt
+#rm -rf ${liboqs}
+exit 0
diff --git a/lib/freebl/leancrypto/fips202.h b/lib/freebl/leancrypto/fips202.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/fips202.h
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: MIT
+// NSS SHA3 bindings for ML-DSA liboqs
+
+#ifndef FIPS202_H
+#define FIPS202_H
+
+#include <blapi.h>
+
+#define SHAKE128_RATE 168
+#define shake128 SHAKE_128_HashBuf
+
+#define SHAKE256_RATE SHA3_256_BLOCK_LENGTH
+#define shake256 SHAKE_256_HashBuf
+
+#ifdef NOT_SUPPORTED
+#define SHA3_256_RATE SHA3_256_BLOCK_LENGTH
+#define sha3_256 OQS_SHA3_sha3_256
+#define sha3_256_inc_init OQS_SHA3_sha3_256_inc_init
+#define sha3_256_inc_absorb OQS_SHA3_sha3_256_inc_absorb
+#define sha3_256_inc_finalize OQS_SHA3_sha3_256_inc_finalize
+#define sha3_256_inc_ctx_clone OQS_SHA3_sha3_256_inc_ctx_clone
+#define sha3_256_inc_ctx_release OQS_SHA3_sha3_256_inc_ctx_release
+
+#define SHA3_384_RATE SHA3_384_BLOCK_LENGTH
+#define sha3_384 OQS_SHA3_sha3_384
+#define sha3_384_inc_init OQS_SHA3_sha3_384_inc_init
+#define sha3_384_inc_absorb OQS_SHA3_sha3_384_inc_absorb
+#define sha3_384_inc_finalize OQS_SHA3_sha3_384_inc_finalize
+#define sha3_384_inc_ctx_clone OQS_SHA3_sha3_384_inc_ctx_clone
+#define sha3_384_inc_ctx_release OQS_SHA3_sha3_384_inc_ctx_release
+
+#define SHA3_512_RATE SHA3_512_BLOCK_LENGTH
+#define sha3_512 OQS_SHA3_sha3_512
+#define sha3_512_inc_init OQS_SHA3_sha3_512_inc_init
+#define sha3_512_inc_absorb OQS_SHA3_sha3_512_inc_absorb
+#define sha3_512_inc_finalize OQS_SHA3_sha3_512_inc_finalize
+#define sha3_512_inc_ctx_clone OQS_SHA3_sha3_512_inc_ctx_clone
+#define sha3_512_inc_ctx_release OQS_SHA3_sha3_512_inc_ctx_release
+#endif
+
+typedef SHAKE_128Context *shake128incctx;
+#define shake128_inc_init(ptr) \
+ (*(ptr)) = SHAKE_128_NewContext(); \
+ SHAKE_128_Begin(*(ptr))
+#define shake128_inc_absorb(ptr, input, inlen) SHAKE_128_Absorb(*(ptr), input, inlen)
+#define shake128_inc_finalize(ptr)
+#define shake128_inc_squeeze(output, outlen, ptr) SHAKE_128_SqueezeEnd(*(ptr), output, outlen)
+#define shake128_inc_ctx_release(ptr) SHAKE_128_DestroyContext(*(ptr), PR_TRUE)
+#define shake128_inc_ctx_reset(ptr) SHAKE_128_Begin(ptr)
+#ifdef NOT_SUPPORTED
+#define shake128_inc_ctx_clone OQS_SHA3_shake128_inc_ctx_clone
+#endif
+
+typedef SHAKE_256Context *shake256incctx;
+#define shake256_inc_init(ptr) \
+ (*(ptr)) = SHAKE_256_NewContext(); \
+ SHAKE_256_Begin(*(ptr))
+#define shake256_inc_absorb(ptr, input, inlen) SHAKE_256_Absorb(*(ptr), input, inlen)
+#define shake256_inc_finalize(ptr)
+#define shake256_inc_squeeze(output, outlen, ptr) SHAKE_256_SqueezeEnd(*(ptr), output, outlen)
+#define shake256_inc_ctx_release(ptr) SHAKE_256_DestroyContext(*(ptr), PR_TRUE)
+#define shake256_inc_ctx_reset(ptr) SHAKE_256_Begin(*ptr)
+
+#ifdef NOT_SUPPORTED
+#define shake128_absorb_once OQS_SHA3_shake128_absorb_once
+void OQS_SHA3_shake128_absorb_once(shake128incctx *state, const uint8_t *in, size_t inlen);
+
+#define shake256_absorb_once OQS_SHA3_shake256_absorb_once
+void OQS_SHA3_shake256_absorb_once(shake256incctx *state, const uint8_t *in, size_t inlen);
+#endif
+
+#define shake128_squeezeblocks(OUT, NBLOCKS, STATE) shake128_inc_squeeze(OUT, (NBLOCKS)*SHAKE128_RATE, STATE)
+
+#define shake256_squeezeblocks(OUT, NBLOCKS, STATE) shake256_inc_squeeze(OUT, (NBLOCKS)*SHAKE256_RATE, STATE)
+
+#endif
diff --git a/lib/freebl/leancrypto/fips_mode.h b/lib/freebl/leancrypto/fips_mode.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/fips_mode.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef FIPS_MODE_H
+#define FIPS_MODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Is FIPS 140 Mode enabled?
+ *
+ * return 0 == false, 1 == true
+ */
+int fips140_mode_enabled(void);
+
+#define FIPS140_PCT_LOOP(func) \
+ if (fips140_mode_enabled()) { \
+ unsigned int __i; \
+ int __ret; \
+ \
+ for (__i = 0; __i < 5; __i++) { \
+ __ret = func; \
+ if (!__ret) \
+ return __ret; \
+ } \
+ assert(0); \
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FIPS_MODE_H */
diff --git a/lib/freebl/leancrypto/helper.h b/lib/freebl/leancrypto/helper.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/helper.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef HELPER_H
+#define HELPER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _WIN32
+#ifndef __not_used
+#define __not_used __attribute__((__unused__))
+#endif
+#ifndef __maybe_unused
+#define __maybe_unused __attribute__((__unused__))
+#endif
+#ifndef __always_inline
+#define __always_inline __inline __attribute__((__always_inline__))
+#endif
+#else
+#ifndef __not_used
+#define __not_used
+#endif
+#ifndef __maybe_unused
+#define __maybe_unused
+#endif
+#ifndef __always_inline
+#define __always_inline
+#endif
+#endif
+
+#ifndef likely
+#define likely(x) __builtin_expect(!!(x), 1)
+#endif
+#ifndef unlikely
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#endif
+
+#ifndef LINUX_KERNEL
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+/**
+ * @brief Obtain pointer to data structure when having a pointer to one of
+ * its members.
+ *
+ * Example:
+ * struct foo *val = member_to_struct(&struct->list_entry, struct foo, list_entry);
+ *
+ * @param member the pointer to the member.
+ * @param data_type the data type of the struct the member is part of.
+ * @param member_var the member variable of the struct the member is
+ * referenced with
+ */
+#define member_to_struct(member, data_type, member_var) \
+ (data_type *)((char *)(member) - (char *)&((data_type *)0)->member_var)
+
+/**
+ * @brief Obtain size of a member of a data structure
+ *
+ * @param struct Data structure
+ * @param member Member variable to obtain size from
+ */
+#define member_size(struct, member) (sizeof(((struct *)0)->member))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HELPER_H */
diff --git a/lib/freebl/leancrypto/initialization.h b/lib/freebl/leancrypto/initialization.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/initialization.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef INITIALIZATION_H
+#define INITIALIZATION_H
+
+#include "visibility.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ascon_fastest_impl(void);
+void sha256_fastest_impl(void);
+void sha512_fastest_impl(void);
+void sha3_fastest_impl(void);
+void aes_fastest_impl(void);
+void kyber_riscv_rvv_selector(void);
+void secure_execution_linux(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INITIALIZATION_H */
diff --git a/lib/freebl/leancrypto/lc_dilithium.h b/lib/freebl/leancrypto/lc_dilithium.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_dilithium.h
@@ -0,0 +1,1843 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef LC_DILITHIUM_H
+#define LC_DILITHIUM_H
+
+#include "ext_headers.h"
+
+#include "lc_dilithium_87.h"
+#define LC_DILITHIUM_87_ENABLED
+#include "lc_dilithium_65.h"
+#define LC_DILITHIUM_65_ENABLED
+#include "lc_dilithium_44.h"
+#define LC_DILITHIUM_44_ENABLED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum lc_dilithium_type {
+ /** Unknown key type */
+ LC_DILITHIUM_UNKNOWN,
+ /** Dilithium 87 */
+ LC_DILITHIUM_87,
+ /** Dilithium 65 */
+ LC_DILITHIUM_65,
+ /** Dilithium 44 */
+ LC_DILITHIUM_44,
+};
+
+/** @defgroup Dilithium ML-DSA / CRYSTALS-Dilithium Signature Mechanism
+ *
+ * \note Although the API uses the term "dilithium", the implementation complies
+ * with FIPS 204. Thus the terms Dilithium and ML-DSA are used interchangeably.
+ *
+ * Dilithium API concept
+ *
+ * The Dilithium API is accessible via the following header files with the
+ * mentioned purpose.
+ *
+ * * lc_dilithium.h: This API is the generic API allowing the caller to select
+ * which Dilithium type (Dilithium 87, 65 or 44) are to be used. The selection
+ * is made either with the flag specified during key generation or by matching
+ * the size of the imported data with the different lc_dilithium_*_load API
+ * calls. All remaining APIs take the information about the Dilithium type
+ * from the provided input data.
+ *
+ * This header file only provides inline functions which selectively call
+ * the API provided with the header files below.
+ *
+ * * lc_dilithium_87.h: Direct access to Dilithium 87.
+ *
+ * * lc_dilithium_65.h: Direct access to Dilithium 65.
+ *
+ * * lc_dilithium_44.h: Direct access to Dilithium 44.
+ *
+ * To support the stream mode of the Dilithium signature operation, a
+ * context structure is required. This context structure can be allocated either
+ * on the stack or heap with \p LC_DILITHIUM_CTX_ON_STACK or
+ * \p lc_dilithium_ctx_alloc. The context should be zeroized
+ * and freed (only for heap) with \p lc_dilithium_ctx_zero or
+ * \p lc_dilithium_ctx_zero_free.
+ */
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_sk {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_sk sk_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_sk sk_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_sk sk_44;
+#endif
+ } key;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_pk {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_pk pk_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_pk pk_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_pk pk_44;
+#endif
+ } key;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_sig {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_sig sig_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_sig sig_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_sig sig_44;
+#endif
+ } sig;
+};
+
+/**
+ * @brief Allocate stack memory for the Dilithium stream context and additional
+ * parameter relevant for the signature operation.
+ *
+ * In addition, the memory buffer returned by this allocation contains the space
+ * for an expanded representation of the public key which is required in both,
+ * signature generation and verification. When using this memory, the first
+ * signature operation expands the key and any subsequent operation using this
+ * context will re-use the expanded key which improves performance of the
+ * signature operation significantly.
+ *
+ * As the same expanded structure is used for signature generation and
+ * verification and the structure can be expanded by either operation, it
+ * is perfectly legal to use one context for both operations as the expanded
+ * key can (a) be generated from either the public or the secret key and (b)
+ * it applies to both operations and (c) is identical irrespective it was
+ * generated from the public or secret key.
+ *
+ * The provided context size is sufficiently large to support all ML-DSA key
+ * sizes this library version offers support for.
+ *
+ * \note: ML-DSA AVX2 signature operation uses a completely different
+ * algorithm which does not use a pre-pcomputed expanded key. Thus, if you know
+ * you have AVX2 support, you *may* not need this larger buffer and you *can*
+ * use \p LC_DILITHIUM_CTX_ON_STACK instead.
+ *
+ * \note: The expanded representation only uses public key data. Even when
+ * deriving the expanded representation from a secret key, this data is only
+ * obtained from a part that is considered public. Thus, this memory does not
+ * require special protections. See FIPS 204 section 3.6.3 on the properties
+ * and handling requirements of the  matrix. Further, see the FIPS 204
+ * ML-DSA.Sign_internal and ML-DSA.Verify_internal algorithm specification on
+ * how this  matrix is generated and that the input to the generation is public
+ * data.
+ *
+ * \warning: One instance of the expanded key representation can only ever apply
+ * to one given key (pair). If you want to reuse the context with multiple keys,
+ * you MUST invalidate the potentially present expanded key representation. Such
+ * invalidation is invoked with the method \p lc_dilithium_ctx_drop_ahat. Only
+ * after this invalidation you can use the context with a different key.
+ *
+ * param [in] name Name of the stack variable
+ */
+#ifdef LC_DILITHIUM_87_ENABLED
+#define LC_DILITHIUM_CTX_ON_STACK_AHAT(name) \
+ LC_DILITHIUM_87_CTX_ON_STACK_AHAT(name)
+#elif defined(LC_DILITHIUM_65_ENABLED)
+LC_DILITHIUM_CTX_ON_STACK_AHAT(name)
+LC_DILITHIUM_65_CTX_ON_STACK_AHAT(name)
+#elif defined(LC_DILITHIUM_44_ENABLED)
+LC_DILITHIUM_CTX_ON_STACK_AHAT(name)
+LC_DILITHIUM_44_CTX_ON_STACK_AHAT(name)
+#endif
+
+/**
+ * @ingroup Dilithium
+ * @brief Allocates Dilithium context on heap
+ *
+ * @param [out] ctx Dilithium context pointer
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ctx_alloc(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Allocates Dilithium context on heap with support to keep the internal
+ * representation of the key.
+ *
+ * \note See \p LC_DILITHIUM_CTX_ON_STACK_AHAT for details.
+ *
+ * @param [out] ctx Dilithium context pointer
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ctx_alloc_ahat(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Zeroizes and frees Dilithium context on heap
+ *
+ * @param [out] ctx Dilithium context pointer
+ */
+void lc_dilithium_ctx_zero_free(struct lc_dilithium_ctx *ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Zeroizes Dilithium context either on heap or on stack
+ *
+ * @param [out] ctx Dilithium context pointer
+ */
+void lc_dilithium_ctx_zero(struct lc_dilithium_ctx *ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Mark the Dilithium context to execute ML-DSA.Sign_internal /
+ * ML-DSA.Verify_internal.
+ *
+ * @param [in] ctx Dilithium context
+ */
+void lc_dilithium_ctx_internal(struct lc_dilithium_ctx *ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Set the hash type that was used for pre-hashing the message. The
+ * message digest is used with the HashML-DSA. The message digest
+ * is to be provided via the message pointer in the sign/verify APIs.
+ *
+ * @param [in] ctx Dilithium context
+ * @param [in] hash Hash context referencing the used hash for pre-hashing the
+ * message
+ */
+void lc_dilithium_ctx_hash(struct lc_dilithium_ctx *ctx,
+ const struct lc_hash *hash);
+
+/**
+ * @ingroup Dilithium
+ * @brief Specify the optional user context string to be applied with the
+ * Dilithium signature operation.
+ *
+ * @param [in] ctx Dilithium context
+ * @param [in] userctx User context string
+ * @param [in] userctxlen Size of the user context string
+ */
+void lc_dilithium_ctx_userctx(struct lc_dilithium_ctx *ctx,
+ const uint8_t *userctx, size_t userctxlen);
+
+/**
+ * @ingroup Dilithium
+ * @brief Specify the optional external mu value.
+ *
+ * \note If the external mu is specified, the signature generation /
+ * verification APIs do not require a message. In this case, the message buffer
+ * can be set to NULL.
+ *
+ * \note If both a message and an external mu are provided, the external mu
+ * takes precedence.
+ *
+ * @param [in] ctx Dilithium context
+ * @param [in] external_mu User context string
+ * @param [in] external_mu_len Size of the user context string
+ */
+void lc_dilithium_ctx_external_mu(struct lc_dilithium_ctx *ctx,
+ const uint8_t *external_mu,
+ size_t external_mu_len);
+
+/**
+ * @ingroup Dilithium
+ * @brief Invalidate the expanded key that potentially is stored in the context.
+ *
+ * This call can be executed on a context irrespective it was allocated with
+ * space for the expanded representation or not. Thus, the caller does not need
+ * to track whether the context supports the expanded key.
+ *
+ * @param [in] ctx Dilithium context
+ */
+void lc_dilithium_ctx_drop_ahat(struct lc_dilithium_ctx *ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Obtain Dilithium type from secret key
+ *
+ * @param [in] sk Secret key from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type lc_dilithium_sk_type(const struct lc_dilithium_sk *sk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Obtain Dilithium type from public key
+ *
+ * @param [in] pk Public key from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type lc_dilithium_pk_type(const struct lc_dilithium_pk *pk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Obtain Dilithium type from signature
+ *
+ * @param [in] sig Signature from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type
+lc_dilithium_sig_type(const struct lc_dilithium_sig *sig);
+
+/**
+ * @ingroup Dilithium
+ * @brief Return the size of the Dilithium secret key.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_sk_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup Dilithium
+ * @brief Return the size of the Dilithium public key.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_pk_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup Dilithium
+ * @brief Return the size of the Dilithium signature.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_sig_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup Dilithium
+ * @brief Load a Dilithium secret key provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] sk Secret key to be filled (the caller must have it allocated)
+ * @param [in] src_key Buffer that holds the key to be imported
+ * @param [in] src_key_len Buffer length that holds the key to be imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_sk_load(struct lc_dilithium_sk *sk, const uint8_t *src_key,
+ size_t src_key_len);
+
+/**
+ * @ingroup Dilithium
+ * @brief Load a Dilithium public key provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] pk Secret key to be filled (the caller must have it allocated)
+ * @param [in] src_key Buffer that holds the key to be imported
+ * @param [in] src_key_len Buffer length that holds the key to be imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_pk_load(struct lc_dilithium_pk *pk, const uint8_t *src_key,
+ size_t src_key_len);
+
+/**
+ * @ingroup Dilithium
+ * @brief Load a Dilithium signature provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] sig Secret key to be filled (the caller must have it allocated)
+ * @param [in] src_sig Buffer that holds the signature to be imported
+ * @param [in] src_sig_len Buffer length that holds the signature to be imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_sig_load(struct lc_dilithium_sig *sig, const uint8_t *src_sig,
+ size_t src_sig_len);
+
+/**
+ * @ingroup Dilithium
+ * @brief Obtain the reference to the Dilithium key and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto key, too.
+ *
+ * @param [out] dilithium_key Dilithium key pointer
+ * @param [out] dilithium_key_len Length of the key buffer
+ * @param [in] sk Dilithium secret key from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_sk_ptr(uint8_t **dilithium_key, size_t *dilithium_key_len,
+ struct lc_dilithium_sk *sk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Obtain the reference to the Dilithium key and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto key, too.
+ *
+ * @param [out] dilithium_key Dilithium key pointer
+ * @param [out] dilithium_key_len Length of the key buffer
+ * @param [in] pk Dilithium publi key from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_pk_ptr(uint8_t **dilithium_key, size_t *dilithium_key_len,
+ struct lc_dilithium_pk *pk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Obtain the reference to the Dilithium signature and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto signature,
+ * too.
+ *
+ * @param [out] dilithium_sig Dilithium signature pointer
+ * @param [out] dilithium_sig_len Length of the signature buffer
+ * @param [in] sig Dilithium signature from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_sig_ptr(uint8_t **dilithium_sig, size_t *dilithium_sig_len,
+ struct lc_dilithium_sig *sig);
+
+/**
+ * @ingroup Dilithium
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ * @param [in] dilithium_type type of the Dilithium key to generate
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_keypair(struct lc_dilithium_pk *pk, struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx,
+ enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup Dilithium
+ * @brief Generates Dilithium public and private key from a given seed.
+ *
+ * The idea of the function is the allowance of FIPS 204 to maintain the seed
+ * used to generate a key pair in lieu of maintaining a private key or the
+ * key pair (which used much more memory). The seed must be treated equally
+ * sensitive as a private key.
+ *
+ * The seed is generated by simply obtaining 32 bytes from a properly seeded
+ * DRNG, i.e. the same way as a symmetric key would be generated.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] seed buffer with the seed data which must be exactly 32 bytes
+ * in size
+ * @param [in] seedlen length of the seed buffer
+ * @param [in] dilithium_type type of the Dilithium key to generate
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_keypair_from_seed(struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk,
+ const uint8_t *seed, size_t seedlen,
+ enum lc_dilithium_type dilithium_type);
+
+/**
+ * @brief Pairwise consistency check as per FIPS 140 IG
+ *
+ * This call should be invoked after generating a key pair in FIPS mode
+ *
+ * @param [in] pk Public key
+ * @param [in] sk Secret key
+ *
+ * @return 0 on success, < 0 on error
+ */
+int lc_dilithium_pct(const struct lc_dilithium_pk *pk,
+ const struct lc_dilithium_sk *sk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Computes signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_sign(struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Computes signature woth user context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * Using the ctx structure, the caller can select 3 different types of ML-DSA:
+ *
+ * * ctx->dilithium_prehash_type set to a hash type, HashML-DSA is assumed which
+ * implies that the message m must be exactly digest size (FIPS 204 section
+ * 5.4)
+ *
+ * * ctx->ml_dsa_internal set to 1, the ML-DSA.Sign_internal and
+ * .Verify_internal are executed (FIPS 204 chapter 6)
+ *
+ * * both aforementioned parameter set to NULL / 0, ML-DSA.Sign and
+ * ML-DSA.Verify are executed (FIPS 204 sections 5.2 and 5.3)
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_sign_ctx(struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Initializes a signature operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_update and lc_dilithium_sign_final.
+ *
+ * @param [in,out] ctx pointer Dilithium context
+ * @param [in] sk pointer to bit-packed secret key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_sign_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_init and lc_dilithium_sign_final.
+ *
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_sign_update(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+
+/**
+ * @ingroup Dilithium
+ * @brief Computes signature
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_sign_final(struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Verifies signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_verify(const struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Verifies signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_verify_ctx(const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Initializes a signature verification operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_update and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in,out] ctx pointer to an allocated Dilithium context
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_verify_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_init and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_verify_update(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+
+/**
+ * @ingroup Dilithium
+ * @brief Verifies signature
+ *
+ * @param [in] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_verify_final(const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk);
+
+/****************************** Dilithium ED25510 *****************************/
+
+#ifdef LC_DILITHIUM_ED25519_SIG
+
+/** @defgroup HybridDilithium ML-DSA / CRYSTALS-Dilithium Hybrid Signature Mechanism
+ *
+ * The Dilithium hybrid API performs signature operations with Dilithium and
+ * the classic ED25519 algorithm at the same time. The API is identical to
+ * the Dilithium API and can be used as a drop-in replacement.
+ *
+ * ED25519ph is used for the hybrid signature operation compliant to
+ * RFC8032 using a NULL context. This approach is taken to support the
+ * stream mode operation with init / update / final.
+ *
+ * To support the stream mode of the Dilithium signature operation, a
+ * context structure is required. This context structure can be allocated either
+ * on the stack or heap with \p LC_DILITHIUM_ED25519_CTX_ON_STACK or
+ * \p lc_dilithium_ed25519_ctx_alloc. The context should be zeroized
+ * and freed (only for heap) with \p lc_dilithium_ed25519_ctx_zero or
+ * \p lc_dilithium_ed25519_ctx_zero_free.
+ */
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_ed25519_sk {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_ed25519_sk sk_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_ed25519_sk sk_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_ed25519_sk sk_44;
+#endif
+ } key;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_ed25519_pk {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_ed25519_pk pk_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_ed25519_pk pk_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_ed25519_pk pk_44;
+#endif
+ } key;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_ed25519_sig {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_ed25519_sig sig_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_ed25519_sig sig_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_ed25519_sig sig_44;
+#endif
+ } sig;
+};
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Allocates Dilithium-ED25519 context on heap
+ *
+ * @param [out] ctx Dilithium-ED25519 context pointer
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_ctx_alloc(struct lc_dilithium_ed25519_ctx **ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Zeroizes and frees Dilithium-ED25519 context on heap
+ *
+ * @param [out] ctx Dilithium-ED25519 context pointer
+ */
+void lc_dilithium_ed25519_ctx_zero_free(struct lc_dilithium_ed25519_ctx *ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Zeroizes Dilithium-ED25519 context either on heap or on stack
+ *
+ * @param [out] ctx Dilithium-ED25519 context pointer
+ */
+void lc_dilithium_ed25519_ctx_zero(struct lc_dilithium_ed25519_ctx *ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Set the hash type that was used for pre-hashing the message. The
+ * message digest ist used with the HashML-DSA. The message digest
+ * is to be provided via the message pointer in the sign/verify APIs.
+ *
+ * @param [in] ctx Dilithium-ED25519 context
+ * @param [in] hash Hash context referencing the used hash for pre-hashing the
+ * message
+ */
+void lc_dilithium_ed25519_ctx_hash(struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_hash *hash);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Mark the Dilithium context to execute ML-DSA.Sign_internal /
+ * ML-DSA.Verify_internal.
+ *
+ * @param [in] ctx Dilithium-ED25519 context
+ */
+void lc_dilithium_ed25519_ctx_internal(struct lc_dilithium_ed25519_ctx *ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Specify the optional user context string to be applied with the
+ * Dilithium-ED25519 signature operation.
+ *
+ * \warning The operation of the HashComposite-ML-DSA operation clears out
+ * this context during processing. If this context is reused, the caller MUST
+ * set the cotext again.
+ *
+ * @param [in] ctx Dilithium-ED25519 context
+ * @param [in] userctx User context string
+ * @param [in] userctxlen Size of the user context string
+ */
+void lc_dilithium_ed25519_ctx_userctx(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *userctx,
+ size_t userctxlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Specify the optional randomizer to be applied with the
+ * Dilithium-ED25519 signature operation.
+ *
+ * @param [in] ctx Dilithium-ED25519 context
+ * @param [in] randomizer Randomizer
+ * @param [in] randomizerlen Size of randomizer
+ */
+void lc_dilithium_ed25519_ctx_randomizer(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *randomizer,
+ size_t randomizerlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain Dilithium type from secret key
+ *
+ * @param [in] sk Secret key from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type
+lc_dilithium_ed25519_sk_type(const struct lc_dilithium_ed25519_sk *sk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain Dilithium type from public key
+ *
+ * @param [in] pk Public key from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type
+lc_dilithium_ed25519_pk_type(const struct lc_dilithium_ed25519_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain Dilithium type from signature
+ *
+ * @param [in] sig Signature from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type
+lc_dilithium_ed25519_sig_type(const struct lc_dilithium_ed25519_sig *sig);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Return the size of the Dilithium secret key.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_ed25519_sk_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Return the size of the Dilithium public key.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_ed25519_pk_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Return the size of the Dilithium signature.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_ed25519_sig_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Load a Dilithium secret key provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] sk Secret key to be filled (the caller must have it allocated)
+ * @param [in] dilithium_src_key Buffer that holds the Dilithium key to be
+ * imported
+ * @param [in] dilithium_src_key_len Buffer length that holds the key to be
+ * imported
+ * @param [in] ed25519_src_key Buffer that holds the ED25519 key to be imported
+ * @param [in] ed25519_src_key_len Buffer length that holds the key to be
+ * imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_ed25519_sk_load(struct lc_dilithium_ed25519_sk *sk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed25519_src_key,
+ size_t ed25519_src_key_len);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Load a Dilithium public key provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] pk Secret key to be filled (the caller must have it allocated)
+ * @param [in] dilithium_src_key Buffer that holds the Dilithium key to be
+ * imported
+ * @param [in] dilithium_src_key_len Buffer length that holds the key to be
+ * imported
+ * @param [in] ed25519_src_key Buffer that holds the ED25519 key to be imported
+ * @param [in] ed25519_src_key_len Buffer length that holds the key to be
+ * imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_ed25519_pk_load(struct lc_dilithium_ed25519_pk *pk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed25519_src_key,
+ size_t ed25519_src_key_len);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Load a Dilithium signature provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] sig Secret key to be filled (the caller must have it allocated)
+ * @param [in] dilithium_src_sig Buffer that holds the Dilithium signature to be
+ * imported
+ * @param [in] dilithium_src_sig_len Buffer length that holds the Dilithium
+ * signature to be imported
+ * @param [in] ed25519_src_sig Buffer that holds the ED25519 signature to be
+ * imported
+ * @param [in] ed25519_src_sig_len Buffer length that holds the ED25519
+ * signature to be imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_ed25519_sig_load(struct lc_dilithium_ed25519_sig *sig,
+ const uint8_t *dilithium_src_sig,
+ size_t dilithium_src_sig_len,
+ const uint8_t *ed25519_src_sig,
+ size_t ed25519_src_sig_len);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain the reference to the Dilithium key and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto key, too.
+ *
+ * @param [out] dilithium_key Dilithium key pointer
+ * @param [out] dilithium_key_len Length of the key buffer
+ * @param [out] ed25519_key ED25519 key pointer
+ * @param [out] ed25519_key_len ED25519 of the key buffer
+ * @param [in] sk Dilithium secret key from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_ed25519_sk_ptr(uint8_t **dilithium_key,
+ size_t *dilithium_key_len,
+ uint8_t **ed25519_key, size_t *ed25519_key_len,
+ struct lc_dilithium_ed25519_sk *sk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain the reference to the Dilithium key and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto key, too.
+ *
+ * @param [out] dilithium_key Dilithium key pointer
+ * @param [out] dilithium_key_len Length of the key buffer
+ * @param [out] ed25519_key ED25519 key pointer
+ * @param [out] ed25519_key_len ED25519 of the key buffer
+ * @param [in] pk Dilithium publi key from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_ed25519_pk_ptr(uint8_t **dilithium_key,
+ size_t *dilithium_key_len,
+ uint8_t **ed25519_key, size_t *ed25519_key_len,
+ struct lc_dilithium_ed25519_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain the reference to the Dilithium signature and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto signature,
+ * too.
+ *
+ * @param [out] dilithium_sig Dilithium signature pointer
+ * @param [out] dilithium_sig_len Length of the signature buffer
+ * @param [out] ed25519_sig ED25519 signature pointer
+ * @param [out] ed25519_sig_len ED25519 of the signature buffer
+ * @param [in] sig Dilithium signature from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_ed25519_sig_ptr(uint8_t **dilithium_sig,
+ size_t *dilithium_sig_len,
+ uint8_t **ed25519_sig, size_t *ed25519_sig_len,
+ struct lc_dilithium_ed25519_sig *sig);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ * @param [in] dilithium_type type of the Dilithium key to generate
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_keypair(struct lc_dilithium_ed25519_pk *pk,
+ struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx,
+ enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Computes signature in one shot
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_sign(struct lc_dilithium_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ * If the caller specifies a hash algorithm as pre-hash algorithm in the context
+ * via \p lc_dilithium_ctx_hash then *only* the ML-DSA part is affected and
+ * changed into a HashML-DSA which implies that the resulting operation is still
+ * Composite-ML-DSA but with a HashML-DSA used internally - i.e. the resulting
+ * algorithm does not comply to any standard. Therefore, it is best to not
+ * use this method.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_sign_ctx(struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Initializes signature operation in stream mode
+ *
+ * \note The stream API provides the algorithm of HashComposite-ML-DSA as
+ * outlined in
+ * https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html.
+ * The reason is that ED25519 cannot operate in stream mode and thus must be
+ * turned into using a pre-hashed message.
+ *
+ * @param [in] ctx Dilithium-ED25519 context pointer
+ * @param [in] sk pointer to bit-packed secret key
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_sign_init(struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_sk *sk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Updates signature in stream mode
+ *
+ * @param [in] ctx Dilithium-ED25519 context pointer
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_sign_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Computes signature in stream mode
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx Dilithium-ED25519 context pointer
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_sign_final(struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Verifies signature in one shot
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_ed25519_verify(const struct lc_dilithium_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed25519_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Verifies signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ * If the caller specifies a hash algorithm as pre-hash algorithm in the context
+ * via \p lc_dilithium_ctx_hash then *only* the ML-DSA part is affected and
+ * changed into a HashML-DSA which implies that the resulting operation is still
+ * Composite-ML-DSA but with a HashML-DSA used internally - i.e. the resulting
+ * algorithm does not comply to any standard. Therefore, it is best to not
+ * use this method.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_ed25519_verify_ctx(const struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed25519_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Initializes signature verification operation in stream mode
+ *
+ * \note The stream API provides the algorithm of HashComposite-ML-DSA as
+ * outlined in
+ * https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html.
+ * The reason is that ED25519 cannot operate in stream mode and thus must be
+ * turned into using a pre-hashed message.
+ *
+ * @param [in] ctx Dilithium-ED25519 context pointer
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_verify_init(struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Updates signature verification in stream mode
+ *
+ * @param [in] ctx Dilithium-ED25519 context pointer
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_verify_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Verifies signature in stream mode
+ *
+ * @param [in] sig pointer to input signatur
+ * @param [in] ctx Dilithium-ED25519 context pointer
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_ed25519_verify_final(const struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_pk *pk);
+
+#endif /* LC_DILITHIUM_ED25519_SIG */
+
+/****************************** Dilithium ED25510 *****************************/
+
+#ifdef LC_DILITHIUM_ED448_SIG
+
+/** @defgroup HybridDilithium ML-DSA / CRYSTALS-Dilithium Hybrid Signature Mechanism
+ *
+ * The Dilithium hybrid API performs signature operations with Dilithium and
+ * the classic ED448 algorithm at the same time. The API is identical to
+ * the Dilithium API and can be used as a drop-in replacement.
+ *
+ * ED448ph is used for the hybrid signature operation compliant to
+ * RFC8032 using a NULL context. This approach is taken to support the
+ * stream mode operation with init / update / final.
+ *
+ * To support the stream mode of the Dilithium signature operation, a
+ * context structure is required. This context structure can be allocated either
+ * on the stack or heap with \p LC_DILITHIUM_ED448_CTX_ON_STACK or
+ * \p lc_dilithium_ed448_ctx_alloc. The context should be zeroized
+ * and freed (only for heap) with \p lc_dilithium_ed448_ctx_zero or
+ * \p lc_dilithium_ed448_ctx_zero_free.
+ *
+ * \note The APIs for ML-DSA ED25519 and ED448 are identical. You can switch
+ * between both by simply applying a global search and replace of 25519 <-> 448.
+ */
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_ed448_sk {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_ed448_sk sk_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_ed448_sk sk_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_ed448_sk sk_44;
+#endif
+ } key;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_ed448_pk {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_ed448_pk pk_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_ed448_pk pk_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_ed448_pk pk_44;
+#endif
+ } key;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_ed448_sig {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_ed448_sig sig_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_ed448_sig sig_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_ed448_sig sig_44;
+#endif
+ } sig;
+};
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Allocates Dilithium-ED448 context on heap
+ *
+ * @param [out] ctx Dilithium-ED448 context pointer
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_ctx_alloc(struct lc_dilithium_ed448_ctx **ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Zeroizes and frees Dilithium-ED448 context on heap
+ *
+ * @param [out] ctx Dilithium-ED448 context pointer
+ */
+void lc_dilithium_ed448_ctx_zero_free(struct lc_dilithium_ed448_ctx *ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Zeroizes Dilithium-ED448 context either on heap or on stack
+ *
+ * @param [out] ctx Dilithium-ED448 context pointer
+ */
+void lc_dilithium_ed448_ctx_zero(struct lc_dilithium_ed448_ctx *ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Set the hash type that was used for pre-hashing the message. The
+ * message digest ist used with the HashML-DSA. The message digest
+ * is to be provided via the message pointer in the sign/verify APIs.
+ *
+ * @param [in] ctx Dilithium-ED448 context
+ * @param [in] hash Hash context referencing the used hash for pre-hashing the
+ * message
+ */
+void lc_dilithium_ed448_ctx_hash(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_hash *hash);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Mark the Dilithium context to execute ML-DSA.Sign_internal /
+ * ML-DSA.Verify_internal.
+ *
+ * @param [in] ctx Dilithium-ED448 context
+ */
+void lc_dilithium_ed448_ctx_internal(struct lc_dilithium_ed448_ctx *ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Specify the optional user context string to be applied with the
+ * Dilithium-ED448 signature operation.
+ *
+ * \warning The operation of the HashComposite-ML-DSA operation clears out
+ * this context during processing. If this context is reused, the caller MUST
+ * set the cotext again.
+ *
+ * @param [in] ctx Dilithium-ED448 context
+ * @param [in] userctx User context string
+ * @param [in] userctxlen Size of the user context string
+ */
+void lc_dilithium_ed448_ctx_userctx(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *userctx, size_t userctxlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Specify the optional randomizer to be applied with the
+ * Dilithium-ED25519 signature operation.
+ *
+ * @param [in] ctx Dilithium-ED25519 context
+ * @param [in] randomizer Randomizer
+ * @param [in] randomizerlen Size of randomizer
+ */
+void lc_dilithium_ed448_ctx_randomizer(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *randomizer,
+ size_t randomizerlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain Dilithium type from secret key
+ *
+ * @param [in] sk Secret key from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type
+lc_dilithium_ed448_sk_type(const struct lc_dilithium_ed448_sk *sk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain Dilithium type from public key
+ *
+ * @param [in] pk Public key from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type
+lc_dilithium_ed448_pk_type(const struct lc_dilithium_ed448_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain Dilithium type from signature
+ *
+ * @param [in] sig Signature from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type
+lc_dilithium_ed448_sig_type(const struct lc_dilithium_ed448_sig *sig);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Return the size of the Dilithium secret key.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_ed448_sk_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Return the size of the Dilithium public key.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_ed448_pk_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Return the size of the Dilithium signature.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_ed448_sig_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Load a Dilithium secret key provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] sk Secret key to be filled (the caller must have it allocated)
+ * @param [in] dilithium_src_key Buffer that holds the Dilithium key to be
+ * imported
+ * @param [in] dilithium_src_key_len Buffer length that holds the key to be
+ * imported
+ * @param [in] ed448_src_key Buffer that holds the ED448 key to be imported
+ * @param [in] ed448_src_key_len Buffer length that holds the key to be
+ * imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_ed448_sk_load(struct lc_dilithium_ed448_sk *sk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed448_src_key,
+ size_t ed448_src_key_len);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Load a Dilithium public key provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] pk Secret key to be filled (the caller must have it allocated)
+ * @param [in] dilithium_src_key Buffer that holds the Dilithium key to be
+ * imported
+ * @param [in] dilithium_src_key_len Buffer length that holds the key to be
+ * imported
+ * @param [in] ed448_src_key Buffer that holds the ED448 key to be imported
+ * @param [in] ed448_src_key_len Buffer length that holds the key to be
+ * imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_ed448_pk_load(struct lc_dilithium_ed448_pk *pk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed448_src_key,
+ size_t ed448_src_key_len);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Load a Dilithium signature provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] sig Secret key to be filled (the caller must have it allocated)
+ * @param [in] dilithium_src_sig Buffer that holds the Dilithium signature to be
+ * imported
+ * @param [in] dilithium_src_sig_len Buffer length that holds the Dilithium
+ * signature to be imported
+ * @param [in] ed448_src_sig Buffer that holds the ED448 signature to be
+ * imported
+ * @param [in] ed448_src_sig_len Buffer length that holds the ED448
+ * signature to be imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_ed448_sig_load(struct lc_dilithium_ed448_sig *sig,
+ const uint8_t *dilithium_src_sig,
+ size_t dilithium_src_sig_len,
+ const uint8_t *ed448_src_sig,
+ size_t ed448_src_sig_len);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain the reference to the Dilithium key and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto key, too.
+ *
+ * @param [out] dilithium_key Dilithium key pointer
+ * @param [out] dilithium_key_len Length of the key buffer
+ * @param [out] ed448_key ED448 key pointer
+ * @param [out] ed448_key_len ED448 of the key buffer
+ * @param [in] sk Dilithium secret key from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_ed448_sk_ptr(uint8_t **dilithium_key,
+ size_t *dilithium_key_len, uint8_t **ed448_key,
+ size_t *ed448_key_len,
+ struct lc_dilithium_ed448_sk *sk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain the reference to the Dilithium key and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto key, too.
+ *
+ * @param [out] dilithium_key Dilithium key pointer
+ * @param [out] dilithium_key_len Length of the key buffer
+ * @param [out] ed448_key ED448 key pointer
+ * @param [out] ed448_key_len ED448 of the key buffer
+ * @param [in] pk Dilithium publi key from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_ed448_pk_ptr(uint8_t **dilithium_key,
+ size_t *dilithium_key_len, uint8_t **ed448_key,
+ size_t *ed448_key_len,
+ struct lc_dilithium_ed448_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain the reference to the Dilithium signature and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto signature,
+ * too.
+ *
+ * @param [out] dilithium_sig Dilithium signature pointer
+ * @param [out] dilithium_sig_len Length of the signature buffer
+ * @param [out] ed448_sig ED448 signature pointer
+ * @param [out] ed448_sig_len ED448 of the signature buffer
+ * @param [in] sig Dilithium signature from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_ed448_sig_ptr(uint8_t **dilithium_sig,
+ size_t *dilithium_sig_len, uint8_t **ed448_sig,
+ size_t *ed448_sig_len,
+ struct lc_dilithium_ed448_sig *sig);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ * @param [in] dilithium_type type of the Dilithium key to generate
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_keypair(struct lc_dilithium_ed448_pk *pk,
+ struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx,
+ enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Computes signature in one shot
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_sign(struct lc_dilithium_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ * If the caller specifies a hash algorithm as pre-hash algorithm in the context
+ * via \p lc_dilithium_ctx_hash then *only* the ML-DSA part is affected and
+ * changed into a HashML-DSA which implies that the resulting operation is still
+ * Composite-ML-DSA but with a HashML-DSA used internally - i.e. the resulting
+ * algorithm does not comply to any standard. Therefore, it is best to not
+ * use this method.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_sign_ctx(struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Initializes signature operation in stream mode
+ *
+ * \note The stream API provides the algorithm of HashComposite-ML-DSA as
+ * outlined in
+ * https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html.
+ * The reason is that ED448 cannot operate in stream mode and thus must be
+ * turned into using a pre-hashed message.
+ *
+ * @param [in] ctx Dilithium-ED448 context pointer
+ * @param [in] sk pointer to bit-packed secret key
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_sign_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_sk *sk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Updates signature in stream mode
+ *
+ * @param [in] ctx Dilithium-ED448 context pointer
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_sign_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Computes signature in stream mode
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx Dilithium-ED448 context pointer
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_sign_final(struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Verifies signature in one shot
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_ed448_verify(const struct lc_dilithium_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed448_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Verifies signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ * If the caller specifies a hash algorithm as pre-hash algorithm in the context
+ * via \p lc_dilithium_ctx_hash then *only* the ML-DSA part is affected and
+ * changed into a HashML-DSA which implies that the resulting operation is still
+ * Composite-ML-DSA but with a HashML-DSA used internally - i.e. the resulting
+ * algorithm does not comply to any standard. Therefore, it is best to not
+ * use this method.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_ed448_verify_ctx(const struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed448_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Initializes signature verification operation in stream mode
+ *
+ * \note The stream API provides the algorithm of HashComposite-ML-DSA as
+ * outlined in
+ * https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html.
+ * The reason is that ED448 cannot operate in stream mode and thus must be
+ * turned into using a pre-hashed message.
+ *
+ * @param [in] ctx Dilithium-ED448 context pointer
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_verify_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Updates signature verification in stream mode
+ *
+ * @param [in] ctx Dilithium-ED448 context pointer
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_verify_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Verifies signature in stream mode
+ *
+ * @param [in] sig pointer to input signatur
+ * @param [in] ctx Dilithium-ED448 context pointer
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_ed448_verify_final(const struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_pk *pk);
+
+#endif /* LC_DILITHIUM_ED448_SIG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LC_DILITHIUM_H */
diff --git a/lib/freebl/leancrypto/lc_dilithium_44.h b/lib/freebl/leancrypto/lc_dilithium_44.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_dilithium_44.h
@@ -0,0 +1,1136 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef LC_DILITHIUM_44_H
+#define LC_DILITHIUM_44_H
+
+#ifndef __ASSEMBLER__
+
+#include "ext_headers.h"
+#include "lc_hash.h"
+#include "lc_rng.h"
+#include "lc_sha3.h"
+#include "lc_sha512.h"
+
+#endif /* __ASSEMBLER__ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+/*
+ * Dilithium Security Levels
+ * 2 -> 192 bits of security strength
+ * 3 -> 225 bits of security strength
+ * 5 -> 257 bits of security strength
+ */
+#define LC_DILITHIUM_MODE 2
+
+#define LC_DILITHIUM_SEEDBYTES 32
+#define LC_DILITHIUM_CRHBYTES 64
+#define LC_DILITHIUM_TRBYTES 64
+#define LC_DILITHIUM_RNDBYTES 32
+#define LC_DILITHIUM_N 256
+#define LC_DILITHIUM_Q 8380417
+#define LC_DILITHIUM_D 13
+#define LC_DILITHIUM_ROOT_OF_UNITY 1753
+
+#if LC_DILITHIUM_MODE == 2
+#define LC_DILITHIUM_NIST_CATEGORY 1
+#define LC_DILITHIUM_LAMBDA 128
+#define LC_DILITHIUM_K 4
+#define LC_DILITHIUM_L 4
+#define LC_DILITHIUM_ETA 2
+#define LC_DILITHIUM_TAU 39
+#define LC_DILITHIUM_BETA 78
+#define LC_DILITHIUM_GAMMA1 (1 << 17)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 88)
+#define LC_DILITHIUM_OMEGA 80
+
+#elif LC_DILITHIUM_MODE == 3
+#define LC_DILITHIUM_NIST_CATEGORY 3
+#define LC_DILITHIUM_LAMBDA 192
+#define LC_DILITHIUM_K 6
+#define LC_DILITHIUM_L 5
+#define LC_DILITHIUM_ETA 4
+#define LC_DILITHIUM_TAU 49
+#define LC_DILITHIUM_BETA 196
+#define LC_DILITHIUM_GAMMA1 (1 << 19)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 32)
+#define LC_DILITHIUM_OMEGA 55
+
+#elif LC_DILITHIUM_MODE == 5
+#define LC_DILITHIUM_NIST_CATEGORY 5
+#define LC_DILITHIUM_LAMBDA 256
+#define LC_DILITHIUM_K 8
+#define LC_DILITHIUM_L 7
+#define LC_DILITHIUM_ETA 2
+#define LC_DILITHIUM_TAU 60
+#define LC_DILITHIUM_BETA 120
+#define LC_DILITHIUM_GAMMA1 (1 << 19)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 32)
+#define LC_DILITHIUM_OMEGA 75
+
+#endif
+
+#define LC_DILITHIUM_CTILDE_BYTES (LC_DILITHIUM_LAMBDA * 2 / 8)
+#define LC_DILITHIUM_POLYT1_PACKEDBYTES 320
+#define LC_DILITHIUM_POLYT0_PACKEDBYTES 416
+#define LC_DILITHIUM_POLYVECH_PACKEDBYTES (LC_DILITHIUM_OMEGA + LC_DILITHIUM_K)
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+#define LC_DILITHIUM_POLYZ_PACKEDBYTES 576
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+#define LC_DILITHIUM_POLYZ_PACKEDBYTES 640
+#endif
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+#define LC_DILITHIUM_POLYW1_PACKEDBYTES 192
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+#define LC_DILITHIUM_POLYW1_PACKEDBYTES 128
+#endif
+
+#if LC_DILITHIUM_ETA == 2
+#define LC_DILITHIUM_POLYETA_PACKEDBYTES 96
+#elif LC_DILITHIUM_ETA == 4
+#define LC_DILITHIUM_POLYETA_PACKEDBYTES 128
+#endif
+
+/*
+ * Sizes of the different Dilithium buffer types.
+ *
+ * WARNING: Do not use these defines in your code. If you need the sizes of
+ * the different variable sizes, use sizeof of the different variable structs or
+ * use the different *_size functions documented below to retrieve the data size
+ * of a particular Dilithium component.
+ */
+#define LC_DILITHIUM_PUBLICKEYBYTES \
+ (LC_DILITHIUM_SEEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYT1_PACKEDBYTES)
+#define LC_DILITHIUM_SECRETKEYBYTES \
+ (2 * LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_TRBYTES + \
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYETA_PACKEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYETA_PACKEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYT0_PACKEDBYTES)
+
+#define LC_DILITHIUM_CRYPTO_BYTES \
+ (LC_DILITHIUM_CTILDE_BYTES + \
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYZ_PACKEDBYTES + \
+ LC_DILITHIUM_POLYVECH_PACKEDBYTES)
+/// \endcond
+
+#ifndef __ASSEMBLER__
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_44_sk {
+ uint8_t sk[LC_DILITHIUM_SECRETKEYBYTES];
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_44_pk {
+ uint8_t pk[LC_DILITHIUM_PUBLICKEYBYTES];
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_44_sig {
+ uint8_t sig[LC_DILITHIUM_CRYPTO_BYTES];
+};
+
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+struct lc_dilithium_ctx {
+ /**
+ * @brief Hash context used internally to the library - it should not
+ * be touched by the user
+ */
+ struct lc_hash_ctx dilithium_hash_ctx;
+
+ /**
+ * @brief State memory of the hash context used internally to the
+ * library - it should not be touched by the user
+ */
+ uint8_t shake_state[LC_SHA3_STATE_SIZE_ALIGN(LC_SHA3_256_CTX_SIZE)];
+
+ /**
+ * @brief When using HashML-DSA, set the hash reference used for the
+ * hash operation. Allowed values are lc_sha256, lc_sha512, lc_sha3_256,
+ * lc_sha3_384, lc_sha3_512, lc_shake128 and lc_shake256. Note, the
+ * actual message digest operation can be performed external to
+ * leancrypto. This parameter only shall indicate the used hash
+ * operation.
+ *
+ * \note Use \p lc_dilithium_ctx_hash or
+ * \p lc_dilithium_ed25519_ctx_hash to set this value.
+ */
+ const struct lc_hash *dilithium_prehash_type;
+
+ /**
+ * @brief length of the user context (allowed range between 0 and 255
+ * bytes)
+ *
+ * \note Use \p lc_dilithium_ctx_userctx or
+ * \p lc_dilithium_ed25519_ctx_userctx to set this value.
+ */
+ size_t userctxlen;
+
+ /**
+ * @brief buffer with a caller-specified context string
+ *
+ * \note Use \p lc_dilithium_ctx_userctx or
+ * \p lc_dilithium_ed25519_ctx_userctx to set this value.
+ */
+ const uint8_t *userctx;
+
+ /**
+ * @brief Pointer to the AHat buffer. This can be provided by the caller
+ * or it must be NULL otherwise.
+ *
+ * \note Use \p LC_DILITHIUM_CTX_ON_STACK_AHAT to provide memory for
+ * storing AHat in the caller context and thus make the signature
+ * operation much faster starting with the 2nd use of the key (pair).
+ */
+ void *ahat;
+ unsigned short ahat_size;
+
+ /**
+ * @brief Pointer to the external mu.
+ *
+ * If set, the signature operation will use the provided mu instead of
+ * the message. In this case, the message pointer to the signature
+ * generation or verification can be NULL.
+ */
+ const uint8_t *external_mu;
+ size_t external_mu_len;
+
+ /**
+ * @brief Pointer to the randomizer
+ *
+ * This is used for the Composite signature: For the discussion of the
+ * randomizer, see https://lamps-wg.github.io/draft-composite-sigs/draft-ietf-lamps-pq-composite-sigs.html
+ */
+ const uint8_t *randomizer;
+ size_t randomizerlen;
+
+ /**
+ * @brief NIST category required for composite signatures
+ *
+ * The domain separation logic depends on the selection of the right
+ * OID for the "Domain" data.
+ */
+ unsigned int nist_category;
+
+ /**
+ * @brief When set to true, only the ML-DSA.Sign_internal or
+ * ML-DSA.Verify_internal are performed (see FIPS 204 chapter 6).
+ * Otherwise the ML-DSA.Sign / ML-DSA.Verify (see FIPS chapter 5) is
+ * applied.
+ *
+ * \note Use \p lc_dilithium_ctx_internal or
+ * \p lc_dilithium_ed25519_ctx_internal to set this value.
+ *
+ * \warning Only set this value to true if you exactly know what you are
+ * doing!.
+ */
+ unsigned int ml_dsa_internal : 1;
+
+ /**
+ * @brief Was aHat already filled? This is used and set internally.
+ */
+ unsigned int ahat_expanded : 1;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+
+/*
+ * The alignment is based on largest alignment of a polyvecl typedef - this is
+ * the AVX2 definition.
+ */
+#define LC_DILITHIUM_AHAT_ALIGNMENT (32)
+
+/*
+ * Padding between struct lc_dilithium_ctx and AHat buffer to ensure AHat buffer
+ * is aligned to LC_DILITHIUM_AHAT_ALIGNMENT
+ */
+#define LC_DILITHIUM_44_AHAT_PAD \
+ (LC_DILITHIUM_AHAT_ALIGNMENT - \
+ (sizeof(struct lc_dilithium_ctx) % LC_DILITHIUM_AHAT_ALIGNMENT))
+
+/* Size of the AHat matrix for ML-DSA 87 */
+#define LC_DILITHIUM_44_AHAT_SIZE \
+ (256 * sizeof(int32_t) * LC_DILITHIUM_K * LC_DILITHIUM_L)
+
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+#define LC_DILITHIUM_CTX_SIZE sizeof(struct lc_dilithium_ctx)
+
+#define LC_DILITHIUM_CTX_INIT_HASH(name) \
+ LC_SHAKE_256_CTX((&(name)->dilithium_hash_ctx))
+
+#define LC_DILITHIUM_SET_CTX(name) \
+ LC_DILITHIUM_CTX_INIT_HASH(name); \
+ (name)->dilithium_prehash_type = NULL; \
+ (name)->ml_dsa_internal = 0; \
+ (name)->userctxlen = 0; \
+ (name)->userctx = NULL; \
+ (name)->ahat = NULL; \
+ (name)->ahat_size = 0; \
+ (name)->external_mu = NULL; \
+ (name)->external_mu_len = 0; \
+ (name)->randomizer = NULL; \
+ (name)->randomizerlen = 0; \
+ (name)->nist_category = 0;
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium stream context or additional
+ * parameter relevant for the signature operation.
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+#define LC_DILITHIUM_CTX_ON_STACK(name) \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, LC_DILITHIUM_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ctx *name = \
+ (struct lc_dilithium_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(name)
+#endif
+
+/**
+ * @brief Allocate stack memory for the Dilithium stream context and additional
+ * parameter relevant for the signature operation.
+ *
+ * In addition, the memory buffer returned by this allocation contains the space
+ * for an expanded representation of the public key which is required in both,
+ * signature generation and verification. When using this memory, the first
+ * signature operation expands the key and any subsequent operation using this
+ * context will re-use the expanded key which improves performance of the
+ * signature operation significantly.
+ *
+ * As the same expanded structure is used for signature generation and
+ * verification and the structure can be expanded by either operation, it
+ * is perfectly legal to use one context for both operations as the expanded
+ * key can (a) be generated from either the public or the secret key and (b)
+ * it applies to both operations and (c) is identical irrespective it was
+ * generated from the public or secret key.
+ *
+ * \note: ML-DSA AVX2 signature operation uses a completely different
+ * algorithm which does not use a pre-pcomputed expanded key. Thus, if you know
+ * you have AVX2 support, you *may* not need this larger buffer and you *can*
+ * use \p LC_DILITHIUM_CTX_ON_STACK instead.
+ *
+ * \note: The expanded representation only uses public key data. Even when
+ * deriving the expanded representation from a secret key, this data is only
+ * obtained from a part that is considered public. Thus, this memory does not
+ * require special protections. See FIPS 204 section 3.6.3 on the properties
+ * and handling requirements of the  matrix. Further, see the FIPS 204
+ * ML-DSA.Sign_internal and ML-DSA.Verify_internal algorithm specification on
+ * how this  matrix is generated and that the input to the generation is public
+ * data.
+ *
+ * \warning: One instance of the expanded key representation can only ever apply
+ * to one given key (pair). If you want to reuse the context with multiple keys,
+ * you MUST invalidate the potentially present expanded key representation. Such
+ * invalidation is invoked with the method \p lc_dilithium_ctx_drop_ahat. Only
+ * after this invalidation you can use the context with a different key.
+ *
+ * @param [in] name Name of the stack variable
+ */
+#define LC_DILITHIUM_44_CTX_ON_STACK_AHAT(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, \
+ LC_DILITHIUM_CTX_SIZE + \
+ LC_DILITHIUM_44_AHAT_PAD + \
+ LC_DILITHIUM_44_AHAT_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ctx *name = \
+ (struct lc_dilithium_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(name); \
+ name->ahat = (uint8_t *)name + LC_DILITHIUM_CTX_SIZE + \
+ LC_DILITHIUM_44_AHAT_PAD; \
+ name->ahat_expanded = 0; \
+ name->ahat_size = LC_DILITHIUM_44_AHAT_SIZE; \
+ _Pragma("GCC diagnostic pop")
+
+/**
+ * @brief Zeroize Dilithium context allocated with
+ * LC_DILITHIUM_CTX_ON_STACK lc_dilithium_ed25519_alloc
+ *
+ * @param [in] ctx Dilithium context to be zeroized
+ */
+static inline void
+lc_dilithium_44_ctx_zero(struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_hash_zero(&ctx->dilithium_hash_ctx);
+ if (ctx->ahat) {
+ lc_memset_secure(ctx->ahat, 0, ctx->ahat_size);
+ ctx->ahat_expanded = 0;
+ }
+}
+
+/**
+ * @brief Allocate Dilithium stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_44_ctx_alloc(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @brief Allocate Dilithium stream context on heap including additional
+ * parameter relevant for the signature operation.
+ *
+ * \note See \p LC_DILITHIUM_44_CTX_ON_STACK_AHAT for details.
+ *
+ * @param [out] ctx Allocated Dilithium stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_44_ctx_alloc_ahat(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium stream context
+ *
+ * @param [in] ctx Dilithium stream context to be zeroized and freed
+ */
+void lc_dilithium_44_ctx_zero_free(struct lc_dilithium_ctx *ctx);
+
+/**
+ * @brief Return the size of the Dilithium secret key.
+ */
+LC_PURE
+static inline unsigned int
+lc_dilithium_44_sk_size(void)
+{
+ return lc_member_size(struct lc_dilithium_44_sk, sk);
+}
+
+/**
+ * @brief Return the size of the Dilithium public key.
+ */
+LC_PURE
+static inline unsigned int
+lc_dilithium_44_pk_size(void)
+{
+ return lc_member_size(struct lc_dilithium_44_pk, pk);
+}
+
+/**
+ * @brief Return the size of the Dilithium signature.
+ */
+LC_PURE
+static inline unsigned int
+lc_dilithium_44_sig_size(void)
+{
+ return lc_member_size(struct lc_dilithium_44_sig, sig);
+}
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_keypair(struct lc_dilithium_44_pk *pk,
+ struct lc_dilithium_44_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Generates Dilithium public and private key from a given seed.
+ *
+ * The idea of the function is the allowance of FIPS 204 to maintain the seed
+ * used to generate a key pair in lieu of maintaining a private key or the
+ * key pair (which used much more memory). The seed must be treated equally
+ * sensitive as a private key.
+ *
+ * The seed is generated by simply obtaining 32 bytes from a properly seeded
+ * DRNG, i.e. the same way as a symmetric key would be generated.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] seed buffer with the seed data which must be exactly 32 bytes
+ * in size
+ * @param [in] seedlen length of the seed buffer
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_keypair_from_seed(struct lc_dilithium_44_pk *pk,
+ struct lc_dilithium_44_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+/**
+ * @brief Computes ML-DSA signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_sign(struct lc_dilithium_44_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_44_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_sign_ctx(struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_44_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Initializes a signature operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_update and lc_dilithium_sign_final.
+ *
+ * @param [in,out] ctx pointer to an allocated Dilithium context
+ * @param [in] sk pointer to bit-packed secret key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_44_sign_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_sk *sk);
+
+/**
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_init and lc_dilithium_sign_final.
+ *
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_sign_update(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+
+/**
+ * @brief Computes signature
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_sign_final(struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies ML-DSA signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_44_verify(const struct lc_dilithium_44_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_pk *pk);
+
+/**
+ * @brief Verifies signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_44_verify_ctx(const struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen,
+ const struct lc_dilithium_44_pk *pk);
+
+/**
+ * @brief Initializes a signature verification operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_update and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in,out] ctx pointer to an allocated Dilithium context
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_44_verify_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_pk *pk);
+
+/**
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_init and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in,out] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_verify_update(struct lc_dilithium_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+/**
+ * @brief Verifies signature
+ *
+ * @param [in] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_44_verify_final(const struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_pk *pk);
+
+/****************************** Dilithium ED25510 *****************************/
+/* Macro set during leancrypto compile time for target platform */
+#undef LC_DILITHIUM_ED25519_SIG
+#ifdef LC_DILITHIUM_ED25519_SIG
+
+#include "lc_ed25519.h"
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_44_ed25519_sk {
+ struct lc_dilithium_44_sk sk;
+ struct lc_ed25519_sk sk_ed25519;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_44_ed25519_pk {
+ struct lc_dilithium_44_pk pk;
+ struct lc_ed25519_pk pk_ed25519;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_44_ed25519_sig {
+ struct lc_dilithium_44_sig sig;
+ struct lc_ed25519_sig sig_ed25519;
+};
+
+/**
+ * @brief Dilithium stream context
+ *
+ * This structure is used for the init/update/final operation of the
+ * Dilithium-ED25519 hybrid.
+ */
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+struct lc_dilithium_ed25519_ctx {
+ struct lc_dilithium_ctx dilithium_ctx;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+#define LC_DILITHIUM_ED25519_CTX_SIZE sizeof(struct lc_dilithium_ed25519_ctx)
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium-ED25519 stream context
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+#define LC_DILITHIUM_ED25519_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, \
+ LC_DILITHIUM_ED25519_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ed25519_ctx *name = \
+ (struct lc_dilithium_ed25519_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(&(name)->dilithium_ctx); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Zeroize Dilithium-ED25519 context allocated with
+ * LC_DILITHIUM_ED25519_CTX_ON_STACK lc_dilithium_ed25519_alloc
+ *
+ * @param [in] ctx Dilithium-ED25519 context to be zeroized
+ */
+static inline void
+lc_dilithium_44_ed25519_ctx_zero(struct lc_dilithium_ed25519_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_dilithium_44_ctx_zero(&ctx->dilithium_ctx);
+}
+
+/**
+ * @brief Allocate Dilithium-ED25519 stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium-ED25519 stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_44_ed25519_ctx_alloc(struct lc_dilithium_ed25519_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium-ED25519 stream context
+ *
+ * @param [in] ctx Dilithium-ED25519 stream context to be zeroized and freed
+ */
+void lc_dilithium_44_ed25519_ctx_zero_free(struct lc_dilithium_ed25519_ctx *ctx);
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_ed25519_keypair(struct lc_dilithium_44_ed25519_pk *pk,
+ struct lc_dilithium_44_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_ed25519_sign(struct lc_dilithium_44_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_ed25519_sign_ctx(struct lc_dilithium_44_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_44_ed25519_sign_init(
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_44_ed25519_sk *sk);
+
+int lc_dilithium_44_ed25519_sign_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+int lc_dilithium_44_ed25519_sign_final(
+ struct lc_dilithium_44_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_44_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_44_ed25519_verify(const struct lc_dilithium_44_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed25519_pk *pk);
+
+/**
+ * @brief Verifies signature in one shot with Dilithium context
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_44_ed25519_verify_ctx(
+ const struct lc_dilithium_44_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx, const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed25519_pk *pk);
+
+int lc_dilithium_44_ed25519_verify_init(
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_44_ed25519_pk *pk);
+int lc_dilithium_44_ed25519_verify_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+int lc_dilithium_44_ed25519_verify_final(
+ const struct lc_dilithium_44_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_44_ed25519_pk *pk);
+
+#endif /* LC_DILITHIUM_ED25519_SIG */
+
+/****************************** Dilithium ED25510 *****************************/
+/* Macro set during leancrypto compile time for target platform */
+#undef LC_DILITHIUM_ED448_SIG
+#ifdef LC_DILITHIUM_ED448_SIG
+
+#include "lc_ed448.h"
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_44_ed448_sk {
+ struct lc_dilithium_44_sk sk;
+ struct lc_ed448_sk sk_ed448;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_44_ed448_pk {
+ struct lc_dilithium_44_pk pk;
+ struct lc_ed448_pk pk_ed448;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_44_ed448_sig {
+ struct lc_dilithium_44_sig sig;
+ struct lc_ed448_sig sig_ed448;
+};
+
+/**
+ * @brief Dilithium stream context
+ *
+ * This structure is used for the init/update/final operation of the
+ * Dilithium-ED448 hybrid.
+ */
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+struct lc_dilithium_ed448_ctx {
+ struct lc_dilithium_ctx dilithium_ctx;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+#define LC_DILITHIUM_ED448_CTX_SIZE sizeof(struct lc_dilithium_ed448_ctx)
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium-ED448 stream context
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+#define LC_DILITHIUM_ED448_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, LC_DILITHIUM_ED448_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ed448_ctx *name = \
+ (struct lc_dilithium_ed448_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(&(name)->dilithium_ctx); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Zeroize Dilithium-ED448 context allocated with
+ * LC_DILITHIUM_ED448_CTX_ON_STACK lc_dilithium_ed448_alloc
+ *
+ * @param [in] ctx Dilithium-ED448 context to be zeroized
+ */
+static inline void
+lc_dilithium_44_ed448_ctx_zero(struct lc_dilithium_ed448_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_dilithium_44_ctx_zero(&ctx->dilithium_ctx);
+}
+
+/**
+ * @brief Allocate Dilithium-ED448 stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium-ED448 stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_44_ed448_ctx_alloc(struct lc_dilithium_ed448_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium-ED448 stream context
+ *
+ * @param [in] ctx Dilithium-ED448 stream context to be zeroized and freed
+ */
+void lc_dilithium_44_ed448_ctx_zero_free(struct lc_dilithium_ed448_ctx *ctx);
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_ed448_keypair(struct lc_dilithium_44_ed448_pk *pk,
+ struct lc_dilithium_44_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_ed448_sign(struct lc_dilithium_44_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_ed448_sign_ctx(struct lc_dilithium_44_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_44_ed448_sign_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_44_ed448_sk *sk);
+
+int lc_dilithium_44_ed448_sign_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+int lc_dilithium_44_ed448_sign_final(struct lc_dilithium_44_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_44_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_44_ed448_verify(const struct lc_dilithium_44_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed448_pk *pk);
+
+/**
+ * @brief Verifies signature in one shot with Dilithium context
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_44_ed448_verify_ctx(const struct lc_dilithium_44_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed448_pk *pk);
+
+int lc_dilithium_44_ed448_verify_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_44_ed448_pk *pk);
+int lc_dilithium_44_ed448_verify_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+int lc_dilithium_44_ed448_verify_final(
+ const struct lc_dilithium_44_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_44_ed448_pk *pk);
+
+#endif /* LC_DILITHIUM_ED448_SIG */
+
+#endif /* __ASSEMBLER__ */
+
+/*
+ * To allow including the different lc_dilithium_*.h files, these macros need to
+ * be undefined. Only during compilation of leancrypto, these macros remain
+ * defined as this header file is not included multiple times.
+ */
+#ifndef LC_DILITHIUM_INTERNAL
+#undef LC_DILITHIUM_MODE
+#undef LC_DILITHIUM_NIST_CATEGORY
+#undef LC_DILITHIUM_SEEDBYTES
+#undef LC_DILITHIUM_CRHBYTES
+#undef LC_DILITHIUM_TRBYTES
+#undef LC_DILITHIUM_RNDBYTES
+#undef LC_DILITHIUM_N
+#undef LC_DILITHIUM_Q
+#undef LC_DILITHIUM_D
+#undef LC_DILITHIUM_ROOT_OF_UNITY
+#undef LC_DILITHIUM_LAMBDA
+#undef LC_DILITHIUM_K
+#undef LC_DILITHIUM_L
+#undef LC_DILITHIUM_ETA
+#undef LC_DILITHIUM_TAU
+#undef LC_DILITHIUM_BETA
+#undef LC_DILITHIUM_GAMMA1
+#undef LC_DILITHIUM_GAMMA2
+#undef LC_DILITHIUM_OMEGA
+#undef LC_DILITHIUM_CTILDE_BYTES
+#undef LC_DILITHIUM_POLYT1_PACKEDBYTES
+#undef LC_DILITHIUM_POLYT0_PACKEDBYTES
+#undef LC_DILITHIUM_POLYVECH_PACKEDBYTES
+#undef LC_DILITHIUM_POLYZ_PACKEDBYTES
+#undef LC_DILITHIUM_POLYW1_PACKEDBYTES
+#undef LC_DILITHIUM_POLYETA_PACKEDBYTES
+#undef LC_DILITHIUM_PUBLICKEYBYTES
+#undef LC_DILITHIUM_SECRETKEYBYTES
+#undef LC_DILITHIUM_CRYPTO_BYTES
+#endif /* LC_DILITHIUM_INTERNAL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LC_DILITHIUM_44_H */
diff --git a/lib/freebl/leancrypto/lc_dilithium_65.h b/lib/freebl/leancrypto/lc_dilithium_65.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_dilithium_65.h
@@ -0,0 +1,1136 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef LC_DILITHIUM_65_H
+#define LC_DILITHIUM_65_H
+
+#ifndef __ASSEMBLER__
+
+#include "ext_headers.h"
+#include "lc_hash.h"
+#include "lc_rng.h"
+#include "lc_sha3.h"
+#include "lc_sha512.h"
+
+#endif /* __ASSEMBLER__ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+/*
+ * Dilithium Security Levels
+ * 2 -> 192 bits of security strength
+ * 3 -> 225 bits of security strength
+ * 5 -> 257 bits of security strength
+ */
+#define LC_DILITHIUM_MODE 3
+
+#define LC_DILITHIUM_SEEDBYTES 32
+#define LC_DILITHIUM_CRHBYTES 64
+#define LC_DILITHIUM_TRBYTES 64
+#define LC_DILITHIUM_RNDBYTES 32
+#define LC_DILITHIUM_N 256
+#define LC_DILITHIUM_Q 8380417
+#define LC_DILITHIUM_D 13
+#define LC_DILITHIUM_ROOT_OF_UNITY 1753
+
+#if LC_DILITHIUM_MODE == 2
+#define LC_DILITHIUM_NIST_CATEGORY 1
+#define LC_DILITHIUM_LAMBDA 128
+#define LC_DILITHIUM_K 4
+#define LC_DILITHIUM_L 4
+#define LC_DILITHIUM_ETA 2
+#define LC_DILITHIUM_TAU 39
+#define LC_DILITHIUM_BETA 78
+#define LC_DILITHIUM_GAMMA1 (1 << 17)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 88)
+#define LC_DILITHIUM_OMEGA 80
+
+#elif LC_DILITHIUM_MODE == 3
+#define LC_DILITHIUM_NIST_CATEGORY 3
+#define LC_DILITHIUM_LAMBDA 192
+#define LC_DILITHIUM_K 6
+#define LC_DILITHIUM_L 5
+#define LC_DILITHIUM_ETA 4
+#define LC_DILITHIUM_TAU 49
+#define LC_DILITHIUM_BETA 196
+#define LC_DILITHIUM_GAMMA1 (1 << 19)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 32)
+#define LC_DILITHIUM_OMEGA 55
+
+#elif LC_DILITHIUM_MODE == 5
+#define LC_DILITHIUM_NIST_CATEGORY 5
+#define LC_DILITHIUM_LAMBDA 256
+#define LC_DILITHIUM_K 8
+#define LC_DILITHIUM_L 7
+#define LC_DILITHIUM_ETA 2
+#define LC_DILITHIUM_TAU 60
+#define LC_DILITHIUM_BETA 120
+#define LC_DILITHIUM_GAMMA1 (1 << 19)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 32)
+#define LC_DILITHIUM_OMEGA 75
+
+#endif
+
+#define LC_DILITHIUM_CTILDE_BYTES (LC_DILITHIUM_LAMBDA * 2 / 8)
+#define LC_DILITHIUM_POLYT1_PACKEDBYTES 320
+#define LC_DILITHIUM_POLYT0_PACKEDBYTES 416
+#define LC_DILITHIUM_POLYVECH_PACKEDBYTES (LC_DILITHIUM_OMEGA + LC_DILITHIUM_K)
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+#define LC_DILITHIUM_POLYZ_PACKEDBYTES 576
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+#define LC_DILITHIUM_POLYZ_PACKEDBYTES 640
+#endif
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+#define LC_DILITHIUM_POLYW1_PACKEDBYTES 192
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+#define LC_DILITHIUM_POLYW1_PACKEDBYTES 128
+#endif
+
+#if LC_DILITHIUM_ETA == 2
+#define LC_DILITHIUM_POLYETA_PACKEDBYTES 96
+#elif LC_DILITHIUM_ETA == 4
+#define LC_DILITHIUM_POLYETA_PACKEDBYTES 128
+#endif
+
+/*
+ * Sizes of the different Dilithium buffer types.
+ *
+ * WARNING: Do not use these defines in your code. If you need the sizes of
+ * the different variable sizes, use sizeof of the different variable structs or
+ * use the different *_size functions documented below to retrieve the data size
+ * of a particular Dilithium component.
+ */
+#define LC_DILITHIUM_PUBLICKEYBYTES \
+ (LC_DILITHIUM_SEEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYT1_PACKEDBYTES)
+#define LC_DILITHIUM_SECRETKEYBYTES \
+ (2 * LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_TRBYTES + \
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYETA_PACKEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYETA_PACKEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYT0_PACKEDBYTES)
+
+#define LC_DILITHIUM_CRYPTO_BYTES \
+ (LC_DILITHIUM_CTILDE_BYTES + \
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYZ_PACKEDBYTES + \
+ LC_DILITHIUM_POLYVECH_PACKEDBYTES)
+/// \endcond
+
+#ifndef __ASSEMBLER__
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_65_sk {
+ uint8_t sk[LC_DILITHIUM_SECRETKEYBYTES];
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_65_pk {
+ uint8_t pk[LC_DILITHIUM_PUBLICKEYBYTES];
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_65_sig {
+ uint8_t sig[LC_DILITHIUM_CRYPTO_BYTES];
+};
+
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+struct lc_dilithium_ctx {
+ /**
+ * @brief Hash context used internally to the library - it should not
+ * be touched by the user
+ */
+ struct lc_hash_ctx dilithium_hash_ctx;
+
+ /**
+ * @brief State memory of the hash context used internally to the
+ * library - it should not be touched by the user
+ */
+ uint8_t shake_state[LC_SHA3_STATE_SIZE_ALIGN(LC_SHA3_256_CTX_SIZE)];
+
+ /**
+ * @brief When using HashML-DSA, set the hash reference used for the
+ * hash operation. Allowed values are lc_sha256, lc_sha512, lc_sha3_256,
+ * lc_sha3_384, lc_sha3_512, lc_shake128 and lc_shake256. Note, the
+ * actual message digest operation can be performed external to
+ * leancrypto. This parameter only shall indicate the used hash
+ * operation.
+ *
+ * \note Use \p lc_dilithium_ctx_hash or
+ * \p lc_dilithium_ed25519_ctx_hash to set this value.
+ */
+ const struct lc_hash *dilithium_prehash_type;
+
+ /**
+ * @brief length of the user context (allowed range between 0 and 255
+ * bytes)
+ *
+ * \note Use \p lc_dilithium_ctx_userctx or
+ * \p lc_dilithium_ed25519_ctx_userctx to set this value.
+ */
+ size_t userctxlen;
+
+ /**
+ * @brief buffer with a caller-specified context string
+ *
+ * \note Use \p lc_dilithium_ctx_userctx or
+ * \p lc_dilithium_ed25519_ctx_userctx to set this value.
+ */
+ const uint8_t *userctx;
+
+ /**
+ * @brief Pointer to the AHat buffer. This can be provided by the caller
+ * or it must be NULL otherwise.
+ *
+ * \note Use \p LC_DILITHIUM_CTX_ON_STACK_AHAT to provide memory for
+ * storing AHat in the caller context and thus make the signature
+ * operation much faster starting with the 2nd use of the key (pair).
+ */
+ void *ahat;
+ unsigned short ahat_size;
+
+ /**
+ * @brief Pointer to the external mu.
+ *
+ * If set, the signature operation will use the provided mu instead of
+ * the message. In this case, the message pointer to the signature
+ * generation or verification can be NULL.
+ */
+ const uint8_t *external_mu;
+ size_t external_mu_len;
+
+ /**
+ * @brief Pointer to the randomizer
+ *
+ * This is used for the Composite signature: For the discussion of the
+ * randomizer, see https://lamps-wg.github.io/draft-composite-sigs/draft-ietf-lamps-pq-composite-sigs.html
+ */
+ const uint8_t *randomizer;
+ size_t randomizerlen;
+
+ /**
+ * @brief NIST category required for composite signatures
+ *
+ * The domain separation logic depends on the selection of the right
+ * OID for the "Domain" data.
+ */
+ unsigned int nist_category;
+
+ /**
+ * @brief When set to true, only the ML-DSA.Sign_internal or
+ * ML-DSA.Verify_internal are performed (see FIPS 204 chapter 6).
+ * Otherwise the ML-DSA.Sign / ML-DSA.Verify (see FIPS chapter 5) is
+ * applied.
+ *
+ * \note Use \p lc_dilithium_ctx_internal or
+ * \p lc_dilithium_ed25519_ctx_internal to set this value.
+ *
+ * \warning Only set this value to true if you exactly know what you are
+ * doing!.
+ */
+ unsigned int ml_dsa_internal : 1;
+
+ /**
+ * @brief Was aHat already filled? This is used and set internally.
+ */
+ unsigned int ahat_expanded : 1;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+
+/*
+ * The alignment is based on largest alignment of a polyvecl typedef - this is
+ * the AVX2 definition.
+ */
+#define LC_DILITHIUM_AHAT_ALIGNMENT (32)
+
+/*
+ * Padding between struct lc_dilithium_ctx and AHat buffer to ensure AHat buffer
+ * is aligned to LC_DILITHIUM_AHAT_ALIGNMENT
+ */
+#define LC_DILITHIUM_65_AHAT_PAD \
+ (LC_DILITHIUM_AHAT_ALIGNMENT - \
+ (sizeof(struct lc_dilithium_ctx) % LC_DILITHIUM_AHAT_ALIGNMENT))
+
+/* Size of the AHat matrix for ML-DSA 87 */
+#define LC_DILITHIUM_65_AHAT_SIZE \
+ (256 * sizeof(int32_t) * LC_DILITHIUM_K * LC_DILITHIUM_L)
+
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+#define LC_DILITHIUM_CTX_SIZE sizeof(struct lc_dilithium_ctx)
+
+#define LC_DILITHIUM_CTX_INIT_HASH(name) \
+ LC_SHAKE_256_CTX((&(name)->dilithium_hash_ctx))
+
+#define LC_DILITHIUM_SET_CTX(name) \
+ LC_DILITHIUM_CTX_INIT_HASH(name); \
+ (name)->dilithium_prehash_type = NULL; \
+ (name)->ml_dsa_internal = 0; \
+ (name)->userctxlen = 0; \
+ (name)->userctx = NULL; \
+ (name)->ahat = NULL; \
+ (name)->ahat_size = 0; \
+ (name)->external_mu = NULL; \
+ (name)->external_mu_len = 0; \
+ (name)->randomizer = NULL; \
+ (name)->randomizerlen = 0; \
+ (name)->nist_category = 0;
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium stream context or additional
+ * parameter relevant for the signature operation.
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+#define LC_DILITHIUM_CTX_ON_STACK(name) \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, LC_DILITHIUM_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ctx *name = \
+ (struct lc_dilithium_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(name)
+#endif
+
+/**
+ * @brief Allocate stack memory for the Dilithium stream context and additional
+ * parameter relevant for the signature operation.
+ *
+ * In addition, the memory buffer returned by this allocation contains the space
+ * for an expanded representation of the public key which is required in both,
+ * signature generation and verification. When using this memory, the first
+ * signature operation expands the key and any subsequent operation using this
+ * context will re-use the expanded key which improves performance of the
+ * signature operation significantly.
+ *
+ * As the same expanded structure is used for signature generation and
+ * verification and the structure can be expanded by either operation, it
+ * is perfectly legal to use one context for both operations as the expanded
+ * key can (a) be generated from either the public or the secret key and (b)
+ * it applies to both operations and (c) is identical irrespective it was
+ * generated from the public or secret key.
+ *
+ * \note: ML-DSA AVX2 signature operation uses a completely different
+ * algorithm which does not use a pre-pcomputed expanded key. Thus, if you know
+ * you have AVX2 support, you *may* not need this larger buffer and you *can*
+ * use \p LC_DILITHIUM_CTX_ON_STACK instead.
+ *
+ * \note: The expanded representation only uses public key data. Even when
+ * deriving the expanded representation from a secret key, this data is only
+ * obtained from a part that is considered public. Thus, this memory does not
+ * require special protections. See FIPS 204 section 3.6.3 on the properties
+ * and handling requirements of the  matrix. Further, see the FIPS 204
+ * ML-DSA.Sign_internal and ML-DSA.Verify_internal algorithm specification on
+ * how this  matrix is generated and that the input to the generation is public
+ * data.
+ *
+ * \warning: One instance of the expanded key representation can only ever apply
+ * to one given key (pair). If you want to reuse the context with multiple keys,
+ * you MUST invalidate the potentially present expanded key representation. Such
+ * invalidation is invoked with the method \p lc_dilithium_ctx_drop_ahat. Only
+ * after this invalidation you can use the context with a different key.
+ *
+ * @param [in] name Name of the stack variable
+ */
+#define LC_DILITHIUM_65_CTX_ON_STACK_AHAT(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, \
+ LC_DILITHIUM_CTX_SIZE + \
+ LC_DILITHIUM_65_AHAT_PAD + \
+ LC_DILITHIUM_65_AHAT_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ctx *name = \
+ (struct lc_dilithium_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(name); \
+ name->ahat = (uint8_t *)name + LC_DILITHIUM_CTX_SIZE + \
+ LC_DILITHIUM_65_AHAT_PAD; \
+ name->ahat_expanded = 0; \
+ name->ahat_size = LC_DILITHIUM_65_AHAT_SIZE; \
+ _Pragma("GCC diagnostic pop")
+
+/**
+ * @brief Zeroize Dilithium context allocated with
+ * LC_DILITHIUM_CTX_ON_STACK lc_dilithium_ed25519_alloc
+ *
+ * @param [in] ctx Dilithium context to be zeroized
+ */
+static inline void
+lc_dilithium_65_ctx_zero(struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_hash_zero(&ctx->dilithium_hash_ctx);
+ if (ctx->ahat) {
+ lc_memset_secure(ctx->ahat, 0, ctx->ahat_size);
+ ctx->ahat_expanded = 0;
+ }
+}
+
+/**
+ * @brief Allocate Dilithium stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_65_ctx_alloc(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @brief Allocate Dilithium stream context on heap including additional
+ * parameter relevant for the signature operation.
+ *
+ * \note See \p LC_DILITHIUM_65_CTX_ON_STACK_AHAT for details.
+ *
+ * @param [out] ctx Allocated Dilithium stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_65_ctx_alloc_ahat(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium stream context
+ *
+ * @param [in] ctx Dilithium stream context to be zeroized and freed
+ */
+void lc_dilithium_65_ctx_zero_free(struct lc_dilithium_ctx *ctx);
+
+/**
+ * @brief Return the size of the Dilithium secret key.
+ */
+LC_PURE
+static inline unsigned int
+lc_dilithium_65_sk_size(void)
+{
+ return lc_member_size(struct lc_dilithium_65_sk, sk);
+}
+
+/**
+ * @brief Return the size of the Dilithium public key.
+ */
+LC_PURE
+static inline unsigned int
+lc_dilithium_65_pk_size(void)
+{
+ return lc_member_size(struct lc_dilithium_65_pk, pk);
+}
+
+/**
+ * @brief Return the size of the Dilithium signature.
+ */
+LC_PURE
+static inline unsigned int
+lc_dilithium_65_sig_size(void)
+{
+ return lc_member_size(struct lc_dilithium_65_sig, sig);
+}
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_keypair(struct lc_dilithium_65_pk *pk,
+ struct lc_dilithium_65_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Generates Dilithium public and private key from a given seed.
+ *
+ * The idea of the function is the allowance of FIPS 204 to maintain the seed
+ * used to generate a key pair in lieu of maintaining a private key or the
+ * key pair (which used much more memory). The seed must be treated equally
+ * sensitive as a private key.
+ *
+ * The seed is generated by simply obtaining 32 bytes from a properly seeded
+ * DRNG, i.e. the same way as a symmetric key would be generated.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] seed buffer with the seed data which must be exactly 32 bytes
+ * in size
+ * @param [in] seedlen length of the seed buffer
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_keypair_from_seed(struct lc_dilithium_65_pk *pk,
+ struct lc_dilithium_65_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+/**
+ * @brief Computes ML-DSA signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_sign(struct lc_dilithium_65_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_65_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_sign_ctx(struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_65_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Initializes a signature operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_update and lc_dilithium_sign_final.
+ *
+ * @param [in,out] ctx pointer to an allocated Dilithium context
+ * @param [in] sk pointer to bit-packed secret key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_65_sign_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_sk *sk);
+
+/**
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_init and lc_dilithium_sign_final.
+ *
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_sign_update(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+
+/**
+ * @brief Computes signature
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_sign_final(struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies ML-DSA signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_65_verify(const struct lc_dilithium_65_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_pk *pk);
+
+/**
+ * @brief Verifies signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_65_verify_ctx(const struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen,
+ const struct lc_dilithium_65_pk *pk);
+
+/**
+ * @brief Initializes a signature verification operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_update and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in,out] ctx pointer to an allocated Dilithium context
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_65_verify_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_pk *pk);
+
+/**
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_init and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in,out] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_verify_update(struct lc_dilithium_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+/**
+ * @brief Verifies signature
+ *
+ * @param [in] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_65_verify_final(const struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_pk *pk);
+
+/****************************** Dilithium ED25510 *****************************/
+/* Macro set during leancrypto compile time for target platform */
+#undef LC_DILITHIUM_ED25519_SIG
+#ifdef LC_DILITHIUM_ED25519_SIG
+
+#include "lc_ed25519.h"
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_65_ed25519_sk {
+ struct lc_dilithium_65_sk sk;
+ struct lc_ed25519_sk sk_ed25519;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_65_ed25519_pk {
+ struct lc_dilithium_65_pk pk;
+ struct lc_ed25519_pk pk_ed25519;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_65_ed25519_sig {
+ struct lc_dilithium_65_sig sig;
+ struct lc_ed25519_sig sig_ed25519;
+};
+
+/**
+ * @brief Dilithium stream context
+ *
+ * This structure is used for the init/update/final operation of the
+ * Dilithium-ED25519 hybrid.
+ */
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+struct lc_dilithium_ed25519_ctx {
+ struct lc_dilithium_ctx dilithium_ctx;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+#define LC_DILITHIUM_ED25519_CTX_SIZE sizeof(struct lc_dilithium_ed25519_ctx)
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium-ED25519 stream context
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+#define LC_DILITHIUM_ED25519_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, \
+ LC_DILITHIUM_ED25519_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ed25519_ctx *name = \
+ (struct lc_dilithium_ed25519_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(&(name)->dilithium_ctx); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Zeroize Dilithium-ED25519 context allocated with
+ * LC_DILITHIUM_ED25519_CTX_ON_STACK lc_dilithium_ed25519_alloc
+ *
+ * @param [in] ctx Dilithium-ED25519 context to be zeroized
+ */
+static inline void
+lc_dilithium_65_ed25519_ctx_zero(struct lc_dilithium_ed25519_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_dilithium_65_ctx_zero(&ctx->dilithium_ctx);
+}
+
+/**
+ * @brief Allocate Dilithium-ED25519 stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium-ED25519 stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_65_ed25519_ctx_alloc(struct lc_dilithium_ed25519_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium-ED25519 stream context
+ *
+ * @param [in] ctx Dilithium-ED25519 stream context to be zeroized and freed
+ */
+void lc_dilithium_65_ed25519_ctx_zero_free(struct lc_dilithium_ed25519_ctx *ctx);
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_ed25519_keypair(struct lc_dilithium_65_ed25519_pk *pk,
+ struct lc_dilithium_65_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_ed25519_sign(struct lc_dilithium_65_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_ed25519_sign_ctx(struct lc_dilithium_65_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_65_ed25519_sign_init(
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_65_ed25519_sk *sk);
+
+int lc_dilithium_65_ed25519_sign_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+int lc_dilithium_65_ed25519_sign_final(
+ struct lc_dilithium_65_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_65_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_65_ed25519_verify(const struct lc_dilithium_65_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed25519_pk *pk);
+
+/**
+ * @brief Verifies signature in one shot with Dilithium context
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_65_ed25519_verify_ctx(
+ const struct lc_dilithium_65_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx, const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed25519_pk *pk);
+
+int lc_dilithium_65_ed25519_verify_init(
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_65_ed25519_pk *pk);
+int lc_dilithium_65_ed25519_verify_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+int lc_dilithium_65_ed25519_verify_final(
+ const struct lc_dilithium_65_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_65_ed25519_pk *pk);
+
+#endif /* LC_DILITHIUM_ED25519_SIG */
+
+/****************************** Dilithium ED25510 *****************************/
+/* Macro set during leancrypto compile time for target platform */
+#undef LC_DILITHIUM_ED448_SIG
+#ifdef LC_DILITHIUM_ED448_SIG
+
+#include "lc_ed448.h"
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_65_ed448_sk {
+ struct lc_dilithium_65_sk sk;
+ struct lc_ed448_sk sk_ed448;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_65_ed448_pk {
+ struct lc_dilithium_65_pk pk;
+ struct lc_ed448_pk pk_ed448;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_65_ed448_sig {
+ struct lc_dilithium_65_sig sig;
+ struct lc_ed448_sig sig_ed448;
+};
+
+/**
+ * @brief Dilithium stream context
+ *
+ * This structure is used for the init/update/final operation of the
+ * Dilithium-ED448 hybrid.
+ */
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+struct lc_dilithium_ed448_ctx {
+ struct lc_dilithium_ctx dilithium_ctx;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+#define LC_DILITHIUM_ED448_CTX_SIZE sizeof(struct lc_dilithium_ed448_ctx)
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium-ED448 stream context
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+#define LC_DILITHIUM_ED448_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, LC_DILITHIUM_ED448_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ed448_ctx *name = \
+ (struct lc_dilithium_ed448_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(&(name)->dilithium_ctx); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Zeroize Dilithium-ED448 context allocated with
+ * LC_DILITHIUM_ED448_CTX_ON_STACK lc_dilithium_ed448_alloc
+ *
+ * @param [in] ctx Dilithium-ED448 context to be zeroized
+ */
+static inline void
+lc_dilithium_65_ed448_ctx_zero(struct lc_dilithium_ed448_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_dilithium_65_ctx_zero(&ctx->dilithium_ctx);
+}
+
+/**
+ * @brief Allocate Dilithium-ED448 stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium-ED448 stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_65_ed448_ctx_alloc(struct lc_dilithium_ed448_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium-ED448 stream context
+ *
+ * @param [in] ctx Dilithium-ED448 stream context to be zeroized and freed
+ */
+void lc_dilithium_65_ed448_ctx_zero_free(struct lc_dilithium_ed448_ctx *ctx);
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_ed448_keypair(struct lc_dilithium_65_ed448_pk *pk,
+ struct lc_dilithium_65_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_ed448_sign(struct lc_dilithium_65_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_ed448_sign_ctx(struct lc_dilithium_65_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_65_ed448_sign_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_65_ed448_sk *sk);
+
+int lc_dilithium_65_ed448_sign_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+int lc_dilithium_65_ed448_sign_final(struct lc_dilithium_65_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_65_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_65_ed448_verify(const struct lc_dilithium_65_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed448_pk *pk);
+
+/**
+ * @brief Verifies signature in one shot with Dilithium context
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_65_ed448_verify_ctx(const struct lc_dilithium_65_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed448_pk *pk);
+
+int lc_dilithium_65_ed448_verify_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_65_ed448_pk *pk);
+int lc_dilithium_65_ed448_verify_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+int lc_dilithium_65_ed448_verify_final(
+ const struct lc_dilithium_65_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_65_ed448_pk *pk);
+
+#endif /* LC_DILITHIUM_ED448_SIG */
+
+#endif /* __ASSEMBLER__ */
+
+/*
+ * To allow including the different lc_dilithium_*.h files, these macros need to
+ * be undefined. Only during compilation of leancrypto, these macros remain
+ * defined as this header file is not included multiple times.
+ */
+#ifndef LC_DILITHIUM_INTERNAL
+#undef LC_DILITHIUM_MODE
+#undef LC_DILITHIUM_NIST_CATEGORY
+#undef LC_DILITHIUM_SEEDBYTES
+#undef LC_DILITHIUM_CRHBYTES
+#undef LC_DILITHIUM_TRBYTES
+#undef LC_DILITHIUM_RNDBYTES
+#undef LC_DILITHIUM_N
+#undef LC_DILITHIUM_Q
+#undef LC_DILITHIUM_D
+#undef LC_DILITHIUM_ROOT_OF_UNITY
+#undef LC_DILITHIUM_LAMBDA
+#undef LC_DILITHIUM_K
+#undef LC_DILITHIUM_L
+#undef LC_DILITHIUM_ETA
+#undef LC_DILITHIUM_TAU
+#undef LC_DILITHIUM_BETA
+#undef LC_DILITHIUM_GAMMA1
+#undef LC_DILITHIUM_GAMMA2
+#undef LC_DILITHIUM_OMEGA
+#undef LC_DILITHIUM_CTILDE_BYTES
+#undef LC_DILITHIUM_POLYT1_PACKEDBYTES
+#undef LC_DILITHIUM_POLYT0_PACKEDBYTES
+#undef LC_DILITHIUM_POLYVECH_PACKEDBYTES
+#undef LC_DILITHIUM_POLYZ_PACKEDBYTES
+#undef LC_DILITHIUM_POLYW1_PACKEDBYTES
+#undef LC_DILITHIUM_POLYETA_PACKEDBYTES
+#undef LC_DILITHIUM_PUBLICKEYBYTES
+#undef LC_DILITHIUM_SECRETKEYBYTES
+#undef LC_DILITHIUM_CRYPTO_BYTES
+#endif /* LC_DILITHIUM_INTERNAL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LC_DILITHIUM_65_H */
diff --git a/lib/freebl/leancrypto/lc_dilithium_87.h b/lib/freebl/leancrypto/lc_dilithium_87.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_dilithium_87.h
@@ -0,0 +1,1136 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef LC_DILITHIUM_87_H
+#define LC_DILITHIUM_87_H
+
+#ifndef __ASSEMBLER__
+
+#include "ext_headers.h"
+#include "lc_hash.h"
+#include "lc_rng.h"
+#include "lc_sha3.h"
+#include "lc_sha512.h"
+
+#endif /* __ASSEMBLER__ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+/*
+ * Dilithium Security Levels
+ * 2 -> 192 bits of security strength
+ * 3 -> 225 bits of security strength
+ * 5 -> 257 bits of security strength
+ */
+#define LC_DILITHIUM_MODE 5
+
+#define LC_DILITHIUM_SEEDBYTES 32
+#define LC_DILITHIUM_CRHBYTES 64
+#define LC_DILITHIUM_TRBYTES 64
+#define LC_DILITHIUM_RNDBYTES 32
+#define LC_DILITHIUM_N 256
+#define LC_DILITHIUM_Q 8380417
+#define LC_DILITHIUM_D 13
+#define LC_DILITHIUM_ROOT_OF_UNITY 1753
+
+#if LC_DILITHIUM_MODE == 2
+#define LC_DILITHIUM_NIST_CATEGORY 1
+#define LC_DILITHIUM_LAMBDA 128
+#define LC_DILITHIUM_K 4
+#define LC_DILITHIUM_L 4
+#define LC_DILITHIUM_ETA 2
+#define LC_DILITHIUM_TAU 39
+#define LC_DILITHIUM_BETA 78
+#define LC_DILITHIUM_GAMMA1 (1 << 17)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 88)
+#define LC_DILITHIUM_OMEGA 80
+
+#elif LC_DILITHIUM_MODE == 3
+#define LC_DILITHIUM_NIST_CATEGORY 3
+#define LC_DILITHIUM_LAMBDA 192
+#define LC_DILITHIUM_K 6
+#define LC_DILITHIUM_L 5
+#define LC_DILITHIUM_ETA 4
+#define LC_DILITHIUM_TAU 49
+#define LC_DILITHIUM_BETA 196
+#define LC_DILITHIUM_GAMMA1 (1 << 19)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 32)
+#define LC_DILITHIUM_OMEGA 55
+
+#elif LC_DILITHIUM_MODE == 5
+#define LC_DILITHIUM_NIST_CATEGORY 5
+#define LC_DILITHIUM_LAMBDA 256
+#define LC_DILITHIUM_K 8
+#define LC_DILITHIUM_L 7
+#define LC_DILITHIUM_ETA 2
+#define LC_DILITHIUM_TAU 60
+#define LC_DILITHIUM_BETA 120
+#define LC_DILITHIUM_GAMMA1 (1 << 19)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 32)
+#define LC_DILITHIUM_OMEGA 75
+
+#endif
+
+#define LC_DILITHIUM_CTILDE_BYTES (LC_DILITHIUM_LAMBDA * 2 / 8)
+#define LC_DILITHIUM_POLYT1_PACKEDBYTES 320
+#define LC_DILITHIUM_POLYT0_PACKEDBYTES 416
+#define LC_DILITHIUM_POLYVECH_PACKEDBYTES (LC_DILITHIUM_OMEGA + LC_DILITHIUM_K)
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+#define LC_DILITHIUM_POLYZ_PACKEDBYTES 576
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+#define LC_DILITHIUM_POLYZ_PACKEDBYTES 640
+#endif
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+#define LC_DILITHIUM_POLYW1_PACKEDBYTES 192
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+#define LC_DILITHIUM_POLYW1_PACKEDBYTES 128
+#endif
+
+#if LC_DILITHIUM_ETA == 2
+#define LC_DILITHIUM_POLYETA_PACKEDBYTES 96
+#elif LC_DILITHIUM_ETA == 4
+#define LC_DILITHIUM_POLYETA_PACKEDBYTES 128
+#endif
+
+/*
+ * Sizes of the different Dilithium buffer types.
+ *
+ * WARNING: Do not use these defines in your code. If you need the sizes of
+ * the different variable sizes, use sizeof of the different variable structs or
+ * use the different *_size functions documented below to retrieve the data size
+ * of a particular Dilithium component.
+ */
+#define LC_DILITHIUM_PUBLICKEYBYTES \
+ (LC_DILITHIUM_SEEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYT1_PACKEDBYTES)
+#define LC_DILITHIUM_SECRETKEYBYTES \
+ (2 * LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_TRBYTES + \
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYETA_PACKEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYETA_PACKEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYT0_PACKEDBYTES)
+
+#define LC_DILITHIUM_CRYPTO_BYTES \
+ (LC_DILITHIUM_CTILDE_BYTES + \
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYZ_PACKEDBYTES + \
+ LC_DILITHIUM_POLYVECH_PACKEDBYTES)
+/// \endcond
+
+#ifndef __ASSEMBLER__
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_87_sk {
+ uint8_t sk[LC_DILITHIUM_SECRETKEYBYTES];
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_87_pk {
+ uint8_t pk[LC_DILITHIUM_PUBLICKEYBYTES];
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_87_sig {
+ uint8_t sig[LC_DILITHIUM_CRYPTO_BYTES];
+};
+
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+struct lc_dilithium_ctx {
+ /**
+ * @brief Hash context used internally to the library - it should not
+ * be touched by the user
+ */
+ struct lc_hash_ctx dilithium_hash_ctx;
+
+ /**
+ * @brief State memory of the hash context used internally to the
+ * library - it should not be touched by the user
+ */
+ uint8_t shake_state[LC_SHA3_STATE_SIZE_ALIGN(LC_SHA3_256_CTX_SIZE)];
+
+ /**
+ * @brief When using HashML-DSA, set the hash reference used for the
+ * hash operation. Allowed values are lc_sha256, lc_sha512, lc_sha3_256,
+ * lc_sha3_384, lc_sha3_512, lc_shake128 and lc_shake256. Note, the
+ * actual message digest operation can be performed external to
+ * leancrypto. This parameter only shall indicate the used hash
+ * operation.
+ *
+ * \note Use \p lc_dilithium_ctx_hash or
+ * \p lc_dilithium_ed25519_ctx_hash to set this value.
+ */
+ const struct lc_hash *dilithium_prehash_type;
+
+ /**
+ * @brief length of the user context (allowed range between 0 and 255
+ * bytes)
+ *
+ * \note Use \p lc_dilithium_ctx_userctx or
+ * \p lc_dilithium_ed25519_ctx_userctx to set this value.
+ */
+ size_t userctxlen;
+
+ /**
+ * @brief buffer with a caller-specified context string
+ *
+ * \note Use \p lc_dilithium_ctx_userctx or
+ * \p lc_dilithium_ed25519_ctx_userctx to set this value.
+ */
+ const uint8_t *userctx;
+
+ /**
+ * @brief Pointer to the AHat buffer. This can be provided by the caller
+ * or it must be NULL otherwise.
+ *
+ * \note Use \p LC_DILITHIUM_CTX_ON_STACK_AHAT to provide memory for
+ * storing AHat in the caller context and thus make the signature
+ * operation much faster starting with the 2nd use of the key (pair).
+ */
+ void *ahat;
+ unsigned short ahat_size;
+
+ /**
+ * @brief Pointer to the external mu.
+ *
+ * If set, the signature operation will use the provided mu instead of
+ * the message. In this case, the message pointer to the signature
+ * generation or verification can be NULL.
+ */
+ const uint8_t *external_mu;
+ size_t external_mu_len;
+
+ /**
+ * @brief Pointer to the randomizer
+ *
+ * This is used for the Composite signature: For the discussion of the
+ * randomizer, see https://lamps-wg.github.io/draft-composite-sigs/draft-ietf-lamps-pq-composite-sigs.html
+ */
+ const uint8_t *randomizer;
+ size_t randomizerlen;
+
+ /**
+ * @brief NIST category required for composite signatures
+ *
+ * The domain separation logic depends on the selection of the right
+ * OID for the "Domain" data.
+ */
+ unsigned int nist_category;
+
+ /**
+ * @brief When set to true, only the ML-DSA.Sign_internal or
+ * ML-DSA.Verify_internal are performed (see FIPS 204 chapter 6).
+ * Otherwise the ML-DSA.Sign / ML-DSA.Verify (see FIPS chapter 5) is
+ * applied.
+ *
+ * \note Use \p lc_dilithium_ctx_internal or
+ * \p lc_dilithium_ed25519_ctx_internal to set this value.
+ *
+ * \warning Only set this value to true if you exactly know what you are
+ * doing!.
+ */
+ unsigned int ml_dsa_internal : 1;
+
+ /**
+ * @brief Was aHat already filled? This is used and set internally.
+ */
+ unsigned int ahat_expanded : 1;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+
+/*
+ * The alignment is based on largest alignment of a polyvecl typedef - this is
+ * the AVX2 definition.
+ */
+#define LC_DILITHIUM_AHAT_ALIGNMENT (32)
+
+/*
+ * Padding between struct lc_dilithium_ctx and AHat buffer to ensure AHat buffer
+ * is aligned to LC_DILITHIUM_AHAT_ALIGNMENT
+ */
+#define LC_DILITHIUM_87_AHAT_PAD \
+ (LC_DILITHIUM_AHAT_ALIGNMENT - \
+ (sizeof(struct lc_dilithium_ctx) % LC_DILITHIUM_AHAT_ALIGNMENT))
+
+/* Size of the AHat matrix for ML-DSA 87 */
+#define LC_DILITHIUM_87_AHAT_SIZE \
+ (256 * sizeof(int32_t) * LC_DILITHIUM_K * LC_DILITHIUM_L)
+
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+#define LC_DILITHIUM_CTX_SIZE sizeof(struct lc_dilithium_ctx)
+
+#define LC_DILITHIUM_CTX_INIT_HASH(name) \
+ LC_SHAKE_256_CTX((&(name)->dilithium_hash_ctx))
+
+#define LC_DILITHIUM_SET_CTX(name) \
+ LC_DILITHIUM_CTX_INIT_HASH(name); \
+ (name)->dilithium_prehash_type = NULL; \
+ (name)->ml_dsa_internal = 0; \
+ (name)->userctxlen = 0; \
+ (name)->userctx = NULL; \
+ (name)->ahat = NULL; \
+ (name)->ahat_size = 0; \
+ (name)->external_mu = NULL; \
+ (name)->external_mu_len = 0; \
+ (name)->randomizer = NULL; \
+ (name)->randomizerlen = 0; \
+ (name)->nist_category = 0;
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium stream context or additional
+ * parameter relevant for the signature operation.
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+#define LC_DILITHIUM_CTX_ON_STACK(name) \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, LC_DILITHIUM_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ctx *name = \
+ (struct lc_dilithium_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(name)
+#endif
+
+/**
+ * @brief Allocate stack memory for the Dilithium stream context and additional
+ * parameter relevant for the signature operation.
+ *
+ * In addition, the memory buffer returned by this allocation contains the space
+ * for an expanded representation of the public key which is required in both,
+ * signature generation and verification. When using this memory, the first
+ * signature operation expands the key and any subsequent operation using this
+ * context will re-use the expanded key which improves performance of the
+ * signature operation significantly.
+ *
+ * As the same expanded structure is used for signature generation and
+ * verification and the structure can be expanded by either operation, it
+ * is perfectly legal to use one context for both operations as the expanded
+ * key can (a) be generated from either the public or the secret key and (b)
+ * it applies to both operations and (c) is identical irrespective it was
+ * generated from the public or secret key.
+ *
+ * \note: ML-DSA AVX2 signature operation uses a completely different
+ * algorithm which does not use a pre-pcomputed expanded key. Thus, if you know
+ * you have AVX2 support, you *may* not need this larger buffer and you *can*
+ * use \p LC_DILITHIUM_CTX_ON_STACK instead.
+ *
+ * \note: The expanded representation only uses public key data. Even when
+ * deriving the expanded representation from a secret key, this data is only
+ * obtained from a part that is considered public. Thus, this memory does not
+ * require special protections. See FIPS 204 section 3.6.3 on the properties
+ * and handling requirements of the  matrix. Further, see the FIPS 204
+ * ML-DSA.Sign_internal and ML-DSA.Verify_internal algorithm specification on
+ * how this  matrix is generated and that the input to the generation is public
+ * data.
+ *
+ * \warning: One instance of the expanded key representation can only ever apply
+ * to one given key (pair). If you want to reuse the context with multiple keys,
+ * you MUST invalidate the potentially present expanded key representation. Such
+ * invalidation is invoked with the method \p lc_dilithium_ctx_drop_ahat. Only
+ * after this invalidation you can use the context with a different key.
+ *
+ * @param [in] name Name of the stack variable
+ */
+#define LC_DILITHIUM_87_CTX_ON_STACK_AHAT(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, \
+ LC_DILITHIUM_CTX_SIZE + \
+ LC_DILITHIUM_87_AHAT_PAD + \
+ LC_DILITHIUM_87_AHAT_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ctx *name = \
+ (struct lc_dilithium_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(name); \
+ name->ahat = (uint8_t *)name + LC_DILITHIUM_CTX_SIZE + \
+ LC_DILITHIUM_87_AHAT_PAD; \
+ name->ahat_expanded = 0; \
+ name->ahat_size = LC_DILITHIUM_87_AHAT_SIZE; \
+ _Pragma("GCC diagnostic pop")
+
+/**
+ * @brief Zeroize Dilithium context allocated with
+ * LC_DILITHIUM_CTX_ON_STACK lc_dilithium_ed25519_alloc
+ *
+ * @param [in] ctx Dilithium context to be zeroized
+ */
+static inline void
+lc_dilithium_87_ctx_zero(struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_hash_zero(&ctx->dilithium_hash_ctx);
+ if (ctx->ahat) {
+ lc_memset_secure(ctx->ahat, 0, ctx->ahat_size);
+ ctx->ahat_expanded = 0;
+ }
+}
+
+/**
+ * @brief Allocate Dilithium stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_87_ctx_alloc(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @brief Allocate Dilithium stream context on heap including additional
+ * parameter relevant for the signature operation.
+ *
+ * \note See \p LC_DILITHIUM_87_CTX_ON_STACK_AHAT for details.
+ *
+ * @param [out] ctx Allocated Dilithium stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_87_ctx_alloc_ahat(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium stream context
+ *
+ * @param [in] ctx Dilithium stream context to be zeroized and freed
+ */
+void lc_dilithium_87_ctx_zero_free(struct lc_dilithium_ctx *ctx);
+
+/**
+ * @brief Return the size of the Dilithium secret key.
+ */
+LC_PURE
+static inline unsigned int
+lc_dilithium_87_sk_size(void)
+{
+ return lc_member_size(struct lc_dilithium_87_sk, sk);
+}
+
+/**
+ * @brief Return the size of the Dilithium public key.
+ */
+LC_PURE
+static inline unsigned int
+lc_dilithium_87_pk_size(void)
+{
+ return lc_member_size(struct lc_dilithium_87_pk, pk);
+}
+
+/**
+ * @brief Return the size of the Dilithium signature.
+ */
+LC_PURE
+static inline unsigned int
+lc_dilithium_87_sig_size(void)
+{
+ return lc_member_size(struct lc_dilithium_87_sig, sig);
+}
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_keypair(struct lc_dilithium_87_pk *pk,
+ struct lc_dilithium_87_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Generates Dilithium public and private key from a given seed.
+ *
+ * The idea of the function is the allowance of FIPS 204 to maintain the seed
+ * used to generate a key pair in lieu of maintaining a private key or the
+ * key pair (which used much more memory). The seed must be treated equally
+ * sensitive as a private key.
+ *
+ * The seed is generated by simply obtaining 32 bytes from a properly seeded
+ * DRNG, i.e. the same way as a symmetric key would be generated.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] seed buffer with the seed data which must be exactly 32 bytes
+ * in size
+ * @param [in] seedlen length of the seed buffer
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_keypair_from_seed(struct lc_dilithium_87_pk *pk,
+ struct lc_dilithium_87_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+/**
+ * @brief Computes ML-DSA signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_sign(struct lc_dilithium_87_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_87_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_sign_ctx(struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_87_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Initializes a signature operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_update and lc_dilithium_sign_final.
+ *
+ * @param [in,out] ctx pointer to an allocated Dilithium context
+ * @param [in] sk pointer to bit-packed secret key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_87_sign_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_sk *sk);
+
+/**
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_init and lc_dilithium_sign_final.
+ *
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_sign_update(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+
+/**
+ * @brief Computes signature
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_sign_final(struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies ML-DSA signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_87_verify(const struct lc_dilithium_87_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_pk *pk);
+
+/**
+ * @brief Verifies signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_87_verify_ctx(const struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen,
+ const struct lc_dilithium_87_pk *pk);
+
+/**
+ * @brief Initializes a signature verification operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_update and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in,out] ctx pointer to an allocated Dilithium context
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_87_verify_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_pk *pk);
+
+/**
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_init and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in,out] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_verify_update(struct lc_dilithium_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+/**
+ * @brief Verifies signature
+ *
+ * @param [in] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_87_verify_final(const struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_pk *pk);
+
+/****************************** Dilithium ED25510 *****************************/
+/* Macro set during leancrypto compile time for target platform */
+#undef LC_DILITHIUM_ED25519_SIG
+#ifdef LC_DILITHIUM_ED25519_SIG
+
+#include "lc_ed25519.h"
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_87_ed25519_sk {
+ struct lc_dilithium_87_sk sk;
+ struct lc_ed25519_sk sk_ed25519;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_87_ed25519_pk {
+ struct lc_dilithium_87_pk pk;
+ struct lc_ed25519_pk pk_ed25519;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_87_ed25519_sig {
+ struct lc_dilithium_87_sig sig;
+ struct lc_ed25519_sig sig_ed25519;
+};
+
+/**
+ * @brief Dilithium stream context
+ *
+ * This structure is used for the init/update/final operation of the
+ * Dilithium-ED25519 hybrid.
+ */
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+struct lc_dilithium_ed25519_ctx {
+ struct lc_dilithium_ctx dilithium_ctx;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+#define LC_DILITHIUM_ED25519_CTX_SIZE sizeof(struct lc_dilithium_ed25519_ctx)
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium-ED25519 stream context
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+#define LC_DILITHIUM_ED25519_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, \
+ LC_DILITHIUM_ED25519_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ed25519_ctx *name = \
+ (struct lc_dilithium_ed25519_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(&(name)->dilithium_ctx); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Zeroize Dilithium-ED25519 context allocated with
+ * LC_DILITHIUM_ED25519_CTX_ON_STACK lc_dilithium_ed25519_alloc
+ *
+ * @param [in] ctx Dilithium-ED25519 context to be zeroized
+ */
+static inline void
+lc_dilithium_87_ed25519_ctx_zero(struct lc_dilithium_ed25519_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_dilithium_87_ctx_zero(&ctx->dilithium_ctx);
+}
+
+/**
+ * @brief Allocate Dilithium-ED25519 stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium-ED25519 stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_87_ed25519_ctx_alloc(struct lc_dilithium_ed25519_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium-ED25519 stream context
+ *
+ * @param [in] ctx Dilithium-ED25519 stream context to be zeroized and freed
+ */
+void lc_dilithium_87_ed25519_ctx_zero_free(struct lc_dilithium_ed25519_ctx *ctx);
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_ed25519_keypair(struct lc_dilithium_87_ed25519_pk *pk,
+ struct lc_dilithium_87_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_ed25519_sign(struct lc_dilithium_87_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_ed25519_sign_ctx(struct lc_dilithium_87_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_87_ed25519_sign_init(
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_87_ed25519_sk *sk);
+
+int lc_dilithium_87_ed25519_sign_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+int lc_dilithium_87_ed25519_sign_final(
+ struct lc_dilithium_87_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_87_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_87_ed25519_verify(const struct lc_dilithium_87_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed25519_pk *pk);
+
+/**
+ * @brief Verifies signature in one shot with Dilithium context
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_87_ed25519_verify_ctx(
+ const struct lc_dilithium_87_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx, const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed25519_pk *pk);
+
+int lc_dilithium_87_ed25519_verify_init(
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_87_ed25519_pk *pk);
+int lc_dilithium_87_ed25519_verify_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+int lc_dilithium_87_ed25519_verify_final(
+ const struct lc_dilithium_87_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_87_ed25519_pk *pk);
+
+#endif /* LC_DILITHIUM_ED25519_SIG */
+
+/****************************** Dilithium ED25510 *****************************/
+/* Macro set during leancrypto compile time for target platform */
+#undef LC_DILITHIUM_ED448_SIG
+#ifdef LC_DILITHIUM_ED448_SIG
+
+#include "lc_ed448.h"
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_87_ed448_sk {
+ struct lc_dilithium_87_sk sk;
+ struct lc_ed448_sk sk_ed448;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_87_ed448_pk {
+ struct lc_dilithium_87_pk pk;
+ struct lc_ed448_pk pk_ed448;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_87_ed448_sig {
+ struct lc_dilithium_87_sig sig;
+ struct lc_ed448_sig sig_ed448;
+};
+
+/**
+ * @brief Dilithium stream context
+ *
+ * This structure is used for the init/update/final operation of the
+ * Dilithium-ED448 hybrid.
+ */
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+struct lc_dilithium_ed448_ctx {
+ struct lc_dilithium_ctx dilithium_ctx;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+#define LC_DILITHIUM_ED448_CTX_SIZE sizeof(struct lc_dilithium_ed448_ctx)
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium-ED448 stream context
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+#define LC_DILITHIUM_ED448_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, LC_DILITHIUM_ED448_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ed448_ctx *name = \
+ (struct lc_dilithium_ed448_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(&(name)->dilithium_ctx); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Zeroize Dilithium-ED448 context allocated with
+ * LC_DILITHIUM_ED448_CTX_ON_STACK lc_dilithium_ed448_alloc
+ *
+ * @param [in] ctx Dilithium-ED448 context to be zeroized
+ */
+static inline void
+lc_dilithium_87_ed448_ctx_zero(struct lc_dilithium_ed448_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_dilithium_87_ctx_zero(&ctx->dilithium_ctx);
+}
+
+/**
+ * @brief Allocate Dilithium-ED448 stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium-ED448 stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_87_ed448_ctx_alloc(struct lc_dilithium_ed448_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium-ED448 stream context
+ *
+ * @param [in] ctx Dilithium-ED448 stream context to be zeroized and freed
+ */
+void lc_dilithium_87_ed448_ctx_zero_free(struct lc_dilithium_ed448_ctx *ctx);
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_ed448_keypair(struct lc_dilithium_87_ed448_pk *pk,
+ struct lc_dilithium_87_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_ed448_sign(struct lc_dilithium_87_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_ed448_sign_ctx(struct lc_dilithium_87_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_87_ed448_sign_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_87_ed448_sk *sk);
+
+int lc_dilithium_87_ed448_sign_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+int lc_dilithium_87_ed448_sign_final(struct lc_dilithium_87_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_87_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_87_ed448_verify(const struct lc_dilithium_87_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed448_pk *pk);
+
+/**
+ * @brief Verifies signature in one shot with Dilithium context
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_87_ed448_verify_ctx(const struct lc_dilithium_87_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed448_pk *pk);
+
+int lc_dilithium_87_ed448_verify_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_87_ed448_pk *pk);
+int lc_dilithium_87_ed448_verify_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+int lc_dilithium_87_ed448_verify_final(
+ const struct lc_dilithium_87_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_87_ed448_pk *pk);
+
+#endif /* LC_DILITHIUM_ED448_SIG */
+
+#endif /* __ASSEMBLER__ */
+
+/*
+ * To allow including the different lc_dilithium_*.h files, these macros need to
+ * be undefined. Only during compilation of leancrypto, these macros remain
+ * defined as this header file is not included multiple times.
+ */
+#ifndef LC_DILITHIUM_INTERNAL
+#undef LC_DILITHIUM_MODE
+#undef LC_DILITHIUM_NIST_CATEGORY
+#undef LC_DILITHIUM_SEEDBYTES
+#undef LC_DILITHIUM_CRHBYTES
+#undef LC_DILITHIUM_TRBYTES
+#undef LC_DILITHIUM_RNDBYTES
+#undef LC_DILITHIUM_N
+#undef LC_DILITHIUM_Q
+#undef LC_DILITHIUM_D
+#undef LC_DILITHIUM_ROOT_OF_UNITY
+#undef LC_DILITHIUM_LAMBDA
+#undef LC_DILITHIUM_K
+#undef LC_DILITHIUM_L
+#undef LC_DILITHIUM_ETA
+#undef LC_DILITHIUM_TAU
+#undef LC_DILITHIUM_BETA
+#undef LC_DILITHIUM_GAMMA1
+#undef LC_DILITHIUM_GAMMA2
+#undef LC_DILITHIUM_OMEGA
+#undef LC_DILITHIUM_CTILDE_BYTES
+#undef LC_DILITHIUM_POLYT1_PACKEDBYTES
+#undef LC_DILITHIUM_POLYT0_PACKEDBYTES
+#undef LC_DILITHIUM_POLYVECH_PACKEDBYTES
+#undef LC_DILITHIUM_POLYZ_PACKEDBYTES
+#undef LC_DILITHIUM_POLYW1_PACKEDBYTES
+#undef LC_DILITHIUM_POLYETA_PACKEDBYTES
+#undef LC_DILITHIUM_PUBLICKEYBYTES
+#undef LC_DILITHIUM_SECRETKEYBYTES
+#undef LC_DILITHIUM_CRYPTO_BYTES
+#endif /* LC_DILITHIUM_INTERNAL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LC_DILITHIUM_87_H */
diff --git a/lib/freebl/leancrypto/lc_hash.h b/lib/freebl/leancrypto/lc_hash.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_hash.h
@@ -0,0 +1,15 @@
+#include "hasht.h"
+#include "blapit.h"
+#include "lc_memset_secure.h" /* sigh the original included it,
+ * so some files expect it to be there already */
+#include "lc_memory_support.h"
+/* sigh Stephen doesn seem to believe in typedef, so
+ * just stuff our context pointer into a struct */
+#define LC_SHA3_256_CTX_SIZE (SHA3_256_BLOCK_LENGTH)
+#define LC_SHA3_STATE_SIZE_ALIGN(x) (x)
+
+#define LC_SHA3_512_SIZE_DIGEST SHA3_512_LENGTH
+
+#ifndef LC_HASH_COMMON_ALIGNMENT
+#define LC_HASH_COMMON_ALIGNMENT 64
+#endif
diff --git a/lib/freebl/leancrypto/lc_init.h b/lib/freebl/leancrypto/lc_init.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_init.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef LC_INIT_H
+#define LC_INIT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Initialization of leancrypto
+ *
+ * This function invokes all necessary initialization functions required at
+ * the loading time of leancrypto. However, this function is only needed for
+ * environments without a constructor functionality such as the Linux kernel
+ * or the EFI environment.
+ *
+ * For regular environments such as Linux, this function is not required to be
+ * called. But it does not hurt to be called.
+ *
+ * \note If this function is called, no other leancrypto service must be offered
+ * as this function may alter the global leancrypto state.
+ *
+ * @param [in] flags currently unused
+ *
+ * @return 0 on success, < 0 on error
+ */
+int lc_init(unsigned int flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LC_INIT_H */
diff --git a/lib/freebl/leancrypto/lc_memcmp_secure.h b/lib/freebl/leancrypto/lc_memcmp_secure.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_memcmp_secure.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef MEMCMP_SECURE_H
+#define MEMCMP_SECURE_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Constant-time memcmp implementation
+ *
+ * @param s1 First string
+ * @param s1n Size of first string
+ * @param s2 Second string
+ * @param s2n Size of second string
+ *
+ * @return 0 on string match, != 0 when strings do not match
+ */
+int lc_memcmp_secure(const void *s1, size_t s1n, const void *s2, size_t s2n);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MEMCMP_SECURE_H */
diff --git a/lib/freebl/leancrypto/lc_memcpy_secure.h b/lib/freebl/leancrypto/lc_memcpy_secure.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_memcpy_secure.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef MEMCPY_SECURE_H
+#define MEMCPY_SECURE_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Leancrypto-internal memcpy
+ *
+ * @param d Destination buffer
+ * @param dn Size of destination buffer
+ * @param s Source buffer
+ * @param sn Size of source buffer
+ */
+void *lc_memcpy_secure(void *d, size_t dn, const void *s, size_t sn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MEMCPY_SECURE_H */
diff --git a/lib/freebl/leancrypto/lc_memory_support.h b/lib/freebl/leancrypto/lc_memory_support.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_memory_support.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef LC_MEMORY_SUPPORT_H
+#define LC_MEMORY_SUPPORT_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Default memory alignment */
+#define LC_MEM_COMMON_ALIGNMENT (8)
+
+/**
+ * @brief Allocate aligned stack memory
+ *
+ * The variable can be casted to any structure
+ *
+ * @param name variable name
+ * @param size size of the buffer
+ * @param alignment alignment of the buffer
+ */
+#ifdef _WIN32
+#define LC_ALIGNED_BUFFER(name, size, alignment) \
+ uint64_t name[(size + sizeof(uint64_t) - 1) / sizeof(uint64_t)]
+#else
+#define LC_ALIGNED_BUFFER(name, size, alignment) \
+ uint64_t name[(size + sizeof(uint64_t) - 1) / sizeof(uint64_t)] \
+ __attribute__((aligned(alignment)))
+#endif
+
+/* Helpers to align a pointer */
+#define LC_ALIGNMENT_MASK(alignment) (alignment - 1)
+#define LC_ALIGN_APPLY(x, mask) (((x) + (mask)) & ~(mask))
+#define LC_ALIGN(x, a) LC_ALIGN_APPLY((x), (uintptr_t)(a))
+
+/**
+ * @brief Align pointer interpreted as 64 bit variable
+ *
+ * @param p pointer
+ * @param a alignment
+ */
+#define LC_ALIGN_PTR_64(p, a) ((uint64_t *)LC_ALIGN((uintptr_t)(p), (a)));
+
+/**
+ * @brief Align pointer interpreted as 32 bit variable
+ *
+ * @param p pointer
+ * @param a alignment
+ */
+#define LC_ALIGN_PTR_32(p, a) ((uint32_t *)LC_ALIGN((uintptr_t)(p), (a)))
+
+/**
+ * @brief Align pointer interpreted as 16 bit variable
+ *
+ * @param p pointer
+ * @param a alignment
+ */
+#define LC_ALIGN_PTR_16(p, a) ((uint16_t *)LC_ALIGN((uintptr_t)(p), (a)))
+
+/**
+ * @brief Align pointer interpreted as 8 bit variable
+ *
+ * @param p pointer
+ * @param a alignment
+ */
+#define LC_ALIGN_PTR_8(p, a) ((uint8_t *)LC_ALIGN((uintptr_t)(p), (a)))
+
+/**
+ * Proper memory alignment value when using XOR
+ */
+#define LC_XOR_MIN_ALIGNMENT(min, requested) \
+ ((min < requested) ? (requested) : (min))
+
+/* Macros set during leancrypto compile time for target platform */
+#define LC_DEF_HOST_X86_64
+#undef LC_DEF_HOST_ARM32_NEON
+#undef LC_DEF_HOST_AARCH64
+
+#ifdef LC_DEF_HOST_X86_64
+
+/*
+ * The load of data into __m256i does not require alignment, the store
+ * requires 64 bit alignment by using _mm_storel_pd / _mm_storeh_pd.
+ */
+#define LC_XOR_AVX2_ALIGNMENT (sizeof(uint64_t))
+#define LC_XOR_ALIGNMENT(min) LC_XOR_MIN_ALIGNMENT(min, LC_XOR_AVX2_ALIGNMENT)
+
+#elif (defined(LC_DEF_HOST_ARM32_NEON) || defined(LC_DEF_HOST_AARCH64)) && \
+ !defined(LINUX_KERNEL)
+
+/*
+ * The load of data into uint64x2_t requires 64 bit alignment, the store
+ * requires 64 bit alignment.
+ */
+#define LC_XOR_NEON_ALIGNMENT (sizeof(uint64_t))
+#define LC_XOR_ALIGNMENT(min) LC_XOR_MIN_ALIGNMENT(min, LC_XOR_NEON_ALIGNMENT)
+
+#else
+
+#define LC_XOR_ALIGNMENT(min) LC_XOR_MIN_ALIGNMENT(min, (sizeof(uint64_t)))
+
+#endif
+
+/**
+ * @brief allocate aligned memory up to 8 bytes alignment
+ *
+ * @param [out] memptr pointer to the newly allocated memory
+ * @param [in] alignment alignment of the memory
+ * @param [in] size size of the memory buffer
+ */
+int lc_alloc_aligned(void **memptr, size_t alignment, size_t size);
+
+/**
+ * @brief allocate aligned memory up to 8 bytes alignment with additional
+ * security precautions
+ *
+ * @param [out] memptr pointer to the newly allocated memory
+ * @param [in] alignment alignment of the memory
+ * @param [in] size size of the memory buffer
+ */
+int lc_alloc_aligned_secure(void **memptr, size_t alignment, size_t size);
+
+/**
+ * @brief allocate aligned memory with arbitrary alignment
+ *
+ * @param [out] memptr pointer to the newly allocated memory
+ * @param [in] alignment alignment of the memory
+ * @param [in] size size of the memory buffer
+ */
+int lc_alloc_high_aligned(void **memptr, size_t alignment, size_t size);
+
+/**
+ * @brief free the memory allocated with lc_alloc_aligned
+ *
+ * The memory is NOT zeroized.
+ *
+ * @param [in] ptr memory pointer to free
+ */
+void lc_free(void *ptr);
+
+/**
+ * @brief free the memory allocated with lc_alloc_high_aligned
+ *
+ * The memory is NOT zeroized.
+ *
+ * @param [in] ptr memory pointer to free
+ * @param [in] size size of the memory to free
+ */
+void lc_free_high_aligned(void *ptr, size_t size);
+
+/**
+ * @brief Check if memory pointer is aligned to given alignment mask
+ *
+ * @param [in] ptr memory pointer to check
+ * @param [in] alignmask alignment mask to check for
+ *
+ * @return 1 if pointer is aligned, 0 if not aligned
+ */
+static inline int
+lc_mem_aligned(const uint8_t *ptr, uint32_t alignmask)
+{
+ if ((uintptr_t)ptr & alignmask)
+ return 0;
+ return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LC_MEMORY_SUPPORT_H */
diff --git a/lib/freebl/leancrypto/lc_memset_secure.h b/lib/freebl/leancrypto/lc_memset_secure.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_memset_secure.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef LC_MEMSET_SECURE_H
+#define LC_MEMSET_SECURE_H
+
+#include "ext_headers.h"
+
+/*
+ * Tested following code:
+ *
+ * (1) __asm__ __volatile__("" : "=r" (s) : "0" (s));
+ * (2) __asm__ __volatile__("": : :"memory");
+ * (3) __asm__ __volatile__("" : "=r" (s) : "0" (s) : "memory");
+ * (4) __asm__ __volatile__("" : : "r" (s) : "memory");
+ *
+ * Requred result:
+ *
+ * gcc -O3: objdump -d shows the following:
+ *
+ * 0000000000400440 <main>:
+ * ...
+ * 400469: 48 c7 04 24 00 00 00 movq $0x0,(%rsp)
+ * 400470: 00
+ * 400471: 48 c7 44 24 08 00 00 movq $0x0,0x8(%rsp)
+ * 400478: 00 00
+ * 40047a: c7 44 24 10 00 00 00 movl $0x0,0x10(%rsp)
+ * 400481: 00
+ *
+ * clang -O3: objdump -d shows the following:
+ *
+ * 0000000000400590 <main>:
+ * ...
+ * 4005c3: c7 44 24 10 00 00 00 movl $0x0,0x10(%rsp)
+ * 4005ca: 00
+ *
+ *
+ * Test results:
+ *
+ * The following table marks an X when the aforementioned movq/movl code is
+ * present (or an invocation of memset@plt) in the object code
+ * (i.e. the code we want). Contrary, the table marks - where the code is not
+ * present (i.e. the code we do not want):
+ *
+ * | BARRIER | (1) | (2) | (3) | (4)
+ * ---------+----------+ | | |
+ * Compiler | | | | |
+ * =========+==========+=======================
+ * | | | |
+ * gcc -O0 | X | X | X | X
+ * | | | |
+ * gcc -O2 | - | X | X | X
+ * | | | |
+ * gcc -O3 | - | X | X | X
+ * | | | |
+ * clang -00 | X | X | X | X
+ * | | | |
+ * clang -02 | X | - | X | X
+ * | | | |
+ * clang -03 | - | - | X | X
+ */
+
+#ifndef _WIN32
+static inline void
+lc_memset_secure(void *s, int c, size_t n)
+{
+ memset(s, c, n);
+ __asm__ __volatile__(""
+ :
+ : "r"(s)
+ : "memory");
+}
+#else
+static inline void
+lc_memset_secure(void *s, int c, size_t n)
+{
+ if (c == 0) {
+ PORT_SafeZero(s, n);
+ }
+ memset(s, c, n);
+}
+#endif
+
+#if 0
+#include <stdio.h>
+
+int main(int argc, char *argv[])
+{
+ char buf[20];
+
+ snprintf(buf, sizeof(buf) - 1, "test");
+ printf("%s\n", buf);
+
+ memset_secure(buf, 0, sizeof(buf));
+ return 0;
+}
+#endif
+
+#endif /* LC_MEMSET_SECURE_H */
diff --git a/lib/freebl/leancrypto/lc_rng.h b/lib/freebl/leancrypto/lc_rng.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_rng.h
@@ -0,0 +1,58 @@
+#ifndef LC_RNG_H
+#define LC_RNG_H 1
+#include <blapi.h>
+#include <secrng.h>
+
+struct lc_rng_ctx;
+extern struct lc_rng_ctx *lc_seeded_rng;
+
+/* just enough of the rng_context to make the code happy.
+ * in the end, we just use our NSS internal RNG */
+struct lc_static_rng_data {
+ const unsigned char *seed;
+ size_t seedlen;
+};
+
+struct lc_rng_ctx {
+ struct lc_static_rng_data *dummy;
+};
+
+static inline int
+lc_rng_generate(struct lc_rng_ctx *rng,
+ unsigned char *addinput,
+ size_t addlen,
+ unsigned char *out,
+ size_t outlen)
+{
+ size_t len;
+ if (rng->dummy != NULL) {
+ if (outlen > rng->dummy->seedlen) {
+ return -1;
+ }
+ PORT_Memcpy(out, rng->dummy->seed, outlen);
+ return 0;
+ }
+ if (addlen != 0) {
+ RNG_RandomUpdate(addinput, addlen);
+ }
+ len = RNG_SystemRNG(out, outlen);
+ if (len != outlen) {
+ return -1;
+ }
+ return 0;
+}
+
+#define lc_rng_seed(rng, seed, seedlen, pers, perslen) \
+ { \
+ if (pers_len != 0) { \
+ RNG_RandomUpdate(pers, perslen); \
+ } \
+ RNG_SystemRNG(seed, seedlen); \
+ }
+#define lc_rng_check(rng)
+
+#define LC_STATIC_DRNG_ON_STACK(sdrng, state) \
+ struct lc_rng_ctx sdrng; \
+ sdrng.dummy = state;
+
+#endif
diff --git a/lib/freebl/leancrypto/lc_sha256.h b/lib/freebl/leancrypto/lc_sha256.h
new file mode 100644
diff --git a/lib/freebl/leancrypto/lc_sha3.h b/lib/freebl/leancrypto/lc_sha3.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_sha3.h
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: MIT
+// NSS SHA3 bindings for ML-DSA leancrypto
+
+#ifndef LC_SHA3__H
+#define LC_SHA3__H
+#include <blapi.h>
+
+#define LC_SHA3_SIZE_RATE(bits) ((1600 - 2 * bits) >> 3)
+
+#define LC_SHAKE_128_SIZE_BLOCK LC_SHA3_SIZE_RATE(128)
+#define LC_SHAKE_256_SIZE_BLOCK LC_SHA3_SIZE_RATE(256)
+
+/*#define SHAKE128_RATE 168
+#define shake128 SHAKE_128_HashBuf
+
+#define SHAKE256_RATE SHA3_256_BLOCK_LENGTH
+#define shake256 SHAKE_256_HashBuf */
+
+#define LC_HASH_CTX_ON_STACK(name, type_) \
+ struct lc_hash_ctx _##name; \
+ _##name.hash = type_; \
+ _##name.buf = NULL; \
+ _##name.stream = false; \
+ _##name.u.ctx_ptr = NULL; \
+ struct lc_hash_ctx *name = &_##name;
+
+#define LC_HASH_SET_CTX(name, type_) \
+ name->hash = type_;
+
+#define LC_SHAKE_256_CTX(name) \
+ LC_HASH_SET_CTX(name, lc_shake256);
+
+typedef enum {
+ lc_shake128,
+ lc_shake256,
+} sha3Type;
+
+/* sigh, we buffer eKEverything because we can't correctly do multiple
+ * finals correctly. In cases where we know we are not going to
+ * do multiple finals, set the streaming bool */
+struct lc_hash_ctx {
+ sha3Type hash;
+ bool stream;
+ union {
+ SHAKE_256Context *shake256_ctx;
+ SHAKE_128Context *shake128_ctx;
+ void *ctx_ptr;
+ } u;
+ size_t digestSize;
+ size_t current_input;
+ size_t current_output;
+ unsigned char *buf;
+ size_t buf_size;
+ unsigned char buf_space[2048];
+ unsigned char buf2_space[2048];
+};
+
+#define lc_xof(type, in, inlen, out, outlen) \
+ switch (type) { \
+ case lc_shake128: \
+ SHAKE_128_HashBuf(out, outlen, in, inlen); \
+ break; \
+ case lc_shake256: \
+ SHAKE_256_HashBuf(out, outlen, in, inlen); \
+ break; \
+ default: \
+ assert(0); \
+ }
+
+static inline void
+lc_hash_init(struct lc_hash_ctx *ptr)
+{
+ if (ptr->stream) {
+ /* if we already have a context, just reset it, This is
+ * what the caller wanted, saving a destroy and create */
+ if (ptr->u.ctx_ptr == NULL) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ ptr->u.shake128_ctx = SHAKE_128_NewContext();
+ break;
+ case lc_shake256:
+ ptr->u.shake256_ctx = SHAKE_256_NewContext();
+ break;
+ }
+ }
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_Begin(ptr->u.shake128_ctx);
+ break;
+ case lc_shake256:
+ SHAKE_256_Begin(ptr->u.shake256_ctx);
+ break;
+ }
+ return;
+ }
+ /* we can be called with an active buffer, do and implicit reset here
+ * and free that buffer before we set up the next one */
+ if (ptr->buf && ptr->buf != ptr->buf_space) {
+ memset(ptr->buf, 0, ptr->current_input);
+ free(ptr->buf);
+ }
+ ptr->digestSize = 0;
+ ptr->current_input = 0;
+ ptr->current_output = 0;
+ ptr->buf_size = sizeof(ptr->buf_space);
+ ptr->buf = &ptr->buf_space[0];
+}
+
+static inline void
+lc_hash_update(struct lc_hash_ctx *ptr, const unsigned char *input, size_t inLen)
+{
+ if (inLen == 0) {
+ return;
+ } /* why were we even called with a NULL buffer? */
+ if (ptr->stream) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_Absorb(ptr->u.shake128_ctx, input, inLen);
+ break;
+ case lc_shake256:
+ SHAKE_256_Absorb(ptr->u.shake256_ctx, input, inLen);
+ break;
+ }
+ return;
+ }
+ if (ptr->current_input + inLen > ptr->buf_size) {
+ int len = ptr->current_input + inLen + 2048;
+ unsigned char *newBuf;
+ if (ptr->buf_size == sizeof(ptr->buf_space)) {
+ newBuf = calloc(1, len);
+ if (newBuf) {
+ memcpy(newBuf, ptr->buf, ptr->buf_size);
+ memset(ptr->buf_space, 0, sizeof(ptr->buf_space));
+ }
+ } else {
+ newBuf = realloc(ptr->buf, len);
+ memset(ptr->buf_space + ptr->current_input, 0, len - ptr->current_input);
+ }
+ if (!newBuf) {
+ return;
+ }
+ ptr->buf = newBuf;
+ ptr->buf_size = len;
+ }
+ memcpy(ptr->buf + ptr->current_input, input, inLen);
+ ptr->current_input += inLen;
+}
+
+#define lc_hash_set_digestsize(ptr, len) ((ptr)->digestSize = (len))
+static inline void
+lc_hash_final(struct lc_hash_ctx *ptr, unsigned char *output)
+{
+ size_t outLen = ptr->digestSize;
+ if (ptr->stream) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_SqueezeEnd(ptr->u.shake128_ctx, output, outLen);
+ break;
+ case lc_shake256:
+ SHAKE_256_SqueezeEnd(ptr->u.shake256_ctx, output, outLen);
+ break;
+ }
+ return;
+ }
+ int len = ptr->current_output + outLen;
+ if (ptr->current_output == 0) {
+ lc_xof(ptr->hash, ptr->buf, ptr->current_input, output, outLen);
+ ptr->current_output += outLen;
+ return;
+ }
+ if (len > sizeof(ptr->buf2_space)) {
+ unsigned char *newBuf = calloc(1, len);
+ if (!newBuf) {
+ memset(output, 0, outLen);
+ return;
+ }
+ lc_xof(ptr->hash, ptr->buf, ptr->current_input, newBuf, len);
+ memcpy(output, newBuf + ptr->current_output, outLen);
+ memset(newBuf, 0, len);
+ free(newBuf);
+ ptr->current_output += outLen;
+ return;
+ }
+ lc_xof(ptr->hash, ptr->buf, ptr->current_input, ptr->buf2_space, len);
+ memcpy(output, &ptr->buf2_space[ptr->current_output], outLen);
+ memset(ptr->buf2_space, 0, len);
+ ptr->current_output += outLen;
+ return;
+}
+
+static inline void
+lc_hash_zero(struct lc_hash_ctx *ptr)
+{
+ if (ptr->stream) {
+ if (ptr->u.ctx_ptr != NULL) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_DestroyContext(ptr->u.shake128_ctx, PR_TRUE);
+ ptr->u.shake128_ctx = NULL;
+ break;
+ case lc_shake256:
+ SHAKE_128_DestroyContext(ptr->u.shake256_ctx, PR_TRUE);
+ ptr->u.shake256_ctx = NULL;
+ break;
+ }
+ }
+ return;
+ }
+ memset(ptr->buf2_space, 0, sizeof(ptr->buf2_space));
+ memset(ptr->buf_space, 0, sizeof(ptr->buf_space));
+ if (ptr->buf != ptr->buf_space) {
+ memset(ptr->buf, 0, ptr->buf_size);
+ free(ptr->buf);
+ ptr->buf = NULL;
+ }
+ lc_hash_init(ptr);
+}
+#endif
diff --git a/lib/freebl/leancrypto/lc_sha512.h b/lib/freebl/leancrypto/lc_sha512.h
new file mode 100644
diff --git a/lib/freebl/leancrypto/lc_status.h b/lib/freebl/leancrypto/lc_status.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_status.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef LC_STATUS_H
+#define LC_STATUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief (Re-)run the self tests
+ *
+ * If the self tests were already executed for a given algorithm, they are
+ * triggered again.
+ */
+void lc_rerun_selftests(void);
+
+/**
+ * @brief Re-run the FIPS 140 integrity test
+ *
+ * \note This API is only present in the FIPS module instance of leancrypto.
+ */
+void lc_fips_integrity_checker(void);
+
+/**
+ * @brief Status information about leancrypto
+ *
+ * @param [in] outbuf Buffer to be filled with status information, allocated by
+ * caller
+ * @param [in] outlen Size of the output buffer
+ */
+void lc_status(char *outbuf, size_t outlen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LC_STATUS_H */
diff --git a/lib/freebl/leancrypto/leap_git_version.txt b/lib/freebl/leancrypto/leap_git_version.txt
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/leap_git_version.txt
@@ -0,0 +1 @@
+* master 70cabeec Poly1305: move function delcarations to internal header
diff --git a/lib/freebl/leancrypto/left_encode.h b/lib/freebl/leancrypto/left_encode.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/left_encode.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef LEFT_ENCODE_H
+#define LEFT_ENCODE_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief - left_encode operation defined in SP800-185
+ */
+unsigned int lc_left_encode(uint8_t *buf, size_t val);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LEFT_ENCODE_H */
diff --git a/lib/freebl/leancrypto/math_helper.h b/lib/freebl/leancrypto/math_helper.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/math_helper.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef MATH_HELPER_H
+#define MATH_HELPER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LINUX_KERNEL
+
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+#endif /* LINUX_KERNEL */
+
+static inline uint8_t
+min_uint8(uint8_t a, uint8_t b)
+{
+ return a < b ? a : b;
+}
+
+static inline uint32_t
+min_uint32(uint32_t a, uint32_t b)
+{
+ return a < b ? a : b;
+}
+
+static inline uint64_t
+min_uint64(uint64_t a, uint64_t b)
+{
+ return a < b ? a : b;
+}
+
+static inline size_t
+min_size(size_t a, size_t b)
+{
+ return a < b ? a : b;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MATH_HELPER_H */
diff --git a/lib/freebl/leancrypto/ml_dsa_44_debug.c b/lib/freebl/leancrypto/ml_dsa_44_debug.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_44_debug.c
@@ -0,0 +1,97 @@
+#define LC_DILITHIUM_TYPE_44 1
+/*
+ * Copyright (C) 2023 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "binhexbin.h"
+
+/* This code is only tuned to the C implementation */
+#include "dilithium_type.h"
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_poly_c.h"
+#include "dilithium_polyvec.h"
+#include "dilithium_polyvec_c.h"
+
+#include "dilithium_debug.h"
+
+void
+dilithium_print_buffer(const uint8_t *buffer, const size_t bufferlen,
+ const char *explanation)
+{
+ bin2print(buffer, bufferlen, stdout, explanation);
+}
+
+void
+dilithium_print_polyvecl_k(polyvecl mat[LC_DILITHIUM_K],
+ const char *explanation)
+{
+ unsigned int i, j, k;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_K; i++) {
+ for (j = 0; j < LC_DILITHIUM_L; j++) {
+ printf("\nK(%u) x L(%u) x N: ", i, j);
+ for (k = 0; k < LC_DILITHIUM_N; k++)
+ printf("0x%.8x ", mat[i].vec[j].coeffs[k]);
+ }
+ }
+ printf("\n");
+}
+
+void
+dilithium_print_polyvecl(polyvecl *polyvec, const char *explanation)
+{
+ unsigned int i, j;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_L; i++) {
+ printf("\nL(%u) x N: ", i);
+ for (j = 0; j < LC_DILITHIUM_N; j++) {
+ printf("%d ", polyvec->vec[i].coeffs[j]);
+ }
+ }
+ printf("\n");
+}
+
+void
+dilithium_print_polyveck(polyveck *polyvec, const char *explanation)
+{
+ unsigned int i, j;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_K; i++) {
+ printf("\nK(%u) x N: ", i);
+ for (j = 0; j < LC_DILITHIUM_N; j++) {
+ printf("%d ", polyvec->vec[i].coeffs[j]);
+ }
+ }
+ printf("\n");
+}
+
+void
+dilithium_print_poly(poly *vec, const char *explanation)
+{
+ unsigned int i;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_N; i++) {
+ printf("%d ", vec->coeffs[i]);
+ }
+ printf("\n");
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_44_def_header.h b/lib/freebl/leancrypto/ml_dsa_44_def_header.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_44_def_header.h
@@ -0,0 +1 @@
+#define LC_DILITHIUM_TYPE_44 1
diff --git a/lib/freebl/leancrypto/ml_dsa_44_ntt.c b/lib/freebl/leancrypto/ml_dsa_44_ntt.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_44_ntt.c
@@ -0,0 +1,94 @@
+#define LC_DILITHIUM_TYPE_44 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_ntt.h"
+#include "dilithium_reduce.h"
+#include "dilithium_zetas.h"
+
+/**
+ * @brief ntt - Forward NTT, in-place. No modular reduction is performed after
+ * additions or subtractions. Output vector is in bitreversed
+ * order.
+ *
+ * @param [in,out] p input/output coefficient array
+ */
+void
+ntt(int32_t a[LC_DILITHIUM_N])
+{
+ unsigned int len, start, j, k;
+ int32_t zeta, t;
+
+ k = 0;
+
+ for (len = 128; len > 0; len >>= 1) {
+ for (start = 0; start < LC_DILITHIUM_N; start = j + len) {
+ zeta = dilithium_zetas[++k];
+ for (j = start; j < start + len; ++j) {
+ t = montgomery_reduce((int64_t)zeta *
+ a[j + len]);
+ a[j + len] = a[j] - t;
+ a[j] = a[j] + t;
+ }
+ }
+ }
+}
+
+/**
+ * @brief invntt_tomont - Inverse NTT and multiplication by Montgomery factor
+ * 2^32. In-place. No modular reductions after additions
+ * or subtractions; input coefficients need to be smaller
+ * than Q in absolute value. Output coefficient are
+ * smaller than Q in absolute value.
+ *
+ * @param [in,out] p input/output coefficient array
+ */
+void
+invntt_tomont(int32_t a[LC_DILITHIUM_N])
+{
+ unsigned int start, len, j, k;
+ int32_t t, zeta;
+ const int32_t f = 41978; // mont^2/256
+
+ k = 256;
+
+ for (len = 1; len < LC_DILITHIUM_N; len <<= 1) {
+ for (start = 0; start < LC_DILITHIUM_N; start = j + len) {
+ zeta = -dilithium_zetas[--k];
+ for (j = start; j < start + len; ++j) {
+ t = a[j];
+ a[j] = t + a[j + len];
+ a[j + len] = t - a[j + len];
+ a[j + len] = montgomery_reduce((int64_t)zeta *
+ a[j + len]);
+ }
+ }
+ }
+
+ for (j = 0; j < LC_DILITHIUM_N; ++j)
+ a[j] = montgomery_reduce((int64_t)f * a[j]);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_44_poly.c b/lib/freebl/leancrypto/ml_dsa_44_poly.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_44_poly.c
@@ -0,0 +1,608 @@
+#define LC_DILITHIUM_TYPE_44 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_service_helpers.h"
+#include "lc_sha3.h"
+#include "timecop.h"
+
+/**
+ * @brief poly_chknorm - Check infinity norm of polynomial against given bound.
+ * Assumes input coefficients were reduced by reduce32().
+ *
+ * @param [in] a pointer to polynomial
+ * @param [in] B norm bound
+ *
+ * @return 0 if norm is strictly smaller than B <= (Q-1)/8 and 1 otherwise.
+ */
+int
+poly_chknorm(const poly *a, int32_t B)
+{
+ unsigned int i;
+ int32_t t;
+
+ if (B > (LC_DILITHIUM_Q - 1) / 8)
+ return 1;
+
+ /*
+ * It is ok to leak which coefficient violates the bound since
+ * the probability for each coefficient *is independent of secret
+ * data but we must not leak the sign of the centralized representative.
+ */
+ for (i = 0; i < LC_DILITHIUM_N; ++i) {
+ /* Absolute value */
+ t = a->coeffs[i] >> 31;
+ t = a->coeffs[i] - (t & 2 * a->coeffs[i]);
+
+ if (t >= B)
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief poly_uniform - Sample polynomial with uniformly random coefficients
+ * in [0,Q-1] by performing rejection sampling on the
+ * output stream of SHAKE128(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed byte array with seed of length LC_DILITHIUM_SEEDBYTES
+ * @param [in] nonce 2-byte nonce
+ */
+void
+poly_uniform(poly *a, const uint8_t seed[LC_DILITHIUM_SEEDBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int i, ctr, off;
+ unsigned int buflen = POLY_UNIFORM_NBLOCKS * LC_SHAKE_128_SIZE_BLOCK;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake128);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_SEEDBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, buflen);
+ lc_hash_final(hash_ctx, buf);
+
+ lc_hash_set_digestsize(hash_ctx, LC_SHAKE_128_SIZE_BLOCK);
+
+ ctr = rej_uniform(a->coeffs, LC_DILITHIUM_N, buf, buflen);
+
+ while (ctr < LC_DILITHIUM_N) {
+ off = buflen % 3;
+ for (i = 0; i < off; ++i)
+ buf[i] = buf[buflen - off + i];
+
+ lc_hash_final(hash_ctx, buf + off);
+ buflen = LC_DILITHIUM_SEEDBYTES + off;
+ ctr += rej_uniform(a->coeffs + ctr, LC_DILITHIUM_N - ctr, buf,
+ buflen);
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief poly_uniform_eta - Sample polynomial with uniformly random
+ * coefficients in [-ETA,ETA] by performing rejection
+ * sampling on the output stream from
+ * SHAKE256(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed byte array with seed of length LC_DILITHIUM_CRHBYTES
+ * @param [in] nonce 2-byte nonce
+ */
+void
+poly_uniform_eta(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int ctr;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CRHBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, POLY_UNIFORM_ETA_BYTES);
+ lc_hash_final(hash_ctx, buf);
+
+ ctr = rej_eta(a->coeffs, LC_DILITHIUM_N, buf, POLY_UNIFORM_ETA_BYTES);
+
+ while (ctr < LC_DILITHIUM_N) {
+ lc_hash_final(hash_ctx, buf);
+
+ ctr += rej_eta(a->coeffs + ctr, LC_DILITHIUM_N - ctr, buf,
+ LC_SHAKE_256_SIZE_BLOCK);
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief poly_uniform_gamma1 - Sample polynomial with uniformly random
+ * coefficients in [-(GAMMA1 - 1), GAMMA1] by
+ * unpacking output stream of
+ * SHAKE256(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed: byte array with seed of length LC_DILITHIUM_CRHBYTES
+ * @param nonce 16-bit nonce
+ */
+void
+poly_uniform_gamma1(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CRHBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, POLY_UNIFORM_GAMMA1_BYTES);
+ lc_hash_final(hash_ctx, ws_buf);
+ lc_hash_zero(hash_ctx);
+
+ polyz_unpack(a, ws_buf);
+}
+
+/**
+ * @brief poly_challenge - Implementation of H. Samples polynomial with TAU
+ * nonzero coefficients in {-1,1} using the output
+ * stream of SHAKE256(seed).
+ *
+ * @param [out] c pointer to output polynomial
+ * @param [in] mu byte array containing seed of length LC_DILITHIUM_CTILDE_BYTES
+ */
+void
+poly_challenge(poly *c, const uint8_t seed[LC_DILITHIUM_CTILDE_BYTES],
+ void *ws_buf)
+{
+ unsigned int i, b, pos;
+ uint64_t signs;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CTILDE_BYTES);
+ lc_hash_set_digestsize(hash_ctx, POLY_CHALLENGE_BYTES);
+ lc_hash_final(hash_ctx, buf);
+
+ signs = 0;
+ for (i = 0; i < 8; ++i)
+ signs |= (uint64_t)buf[i] << 8 * i;
+ pos = 8;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ c->coeffs[i] = 0;
+
+ for (i = LC_DILITHIUM_N - LC_DILITHIUM_TAU; i < LC_DILITHIUM_N; ++i) {
+ do {
+ if (pos >= LC_SHAKE_256_SIZE_BLOCK) {
+ lc_hash_final(hash_ctx, buf);
+ pos = 0;
+ }
+
+ b = buf[pos++];
+ } while (b > i);
+
+ c->coeffs[i] = c->coeffs[b];
+ c->coeffs[b] = 1 - (int32_t)(2 * (signs & 1));
+ signs >>= 1;
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief polyeta_pack - Bit-pack polynomial with coefficients in [-ETA,ETA].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYETA_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyeta_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint8_t t[8];
+
+#if LC_DILITHIUM_ETA == 2
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ t[0] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 0]);
+ t[1] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 1]);
+ t[2] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 2]);
+ t[3] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 3]);
+ t[4] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 4]);
+ t[5] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 5]);
+ t[6] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 6]);
+ t[7] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 7]);
+
+ r[3 * i + 0] =
+ (uint8_t)((t[0] >> 0) | (t[1] << 3) | (t[2] << 6));
+ r[3 * i + 1] = (uint8_t)((t[2] >> 2) | (t[3] << 1) |
+ (t[4] << 4) | (t[5] << 7));
+ r[3 * i + 2] =
+ (uint8_t)((t[5] >> 1) | (t[6] << 2) | (t[7] << 5));
+ }
+#elif LC_DILITHIUM_ETA == 4
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ t[0] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[2 * i + 0]);
+ t[1] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[2 * i + 1]);
+ r[i] = (uint8_t)(t[0] | (t[1] << 4));
+ }
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+}
+
+/**
+ * @brief polyeta_unpack - Unpack polynomial with coefficients in [-ETA,ETA].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void
+polyeta_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_ETA == 2
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ r->coeffs[8 * i + 0] = (a[3 * i + 0] >> 0) & 7;
+ r->coeffs[8 * i + 1] = (a[3 * i + 0] >> 3) & 7;
+ r->coeffs[8 * i + 2] =
+ ((a[3 * i + 0] >> 6) | (a[3 * i + 1] << 2)) & 7;
+ r->coeffs[8 * i + 3] = (a[3 * i + 1] >> 1) & 7;
+ r->coeffs[8 * i + 4] = (a[3 * i + 1] >> 4) & 7;
+ r->coeffs[8 * i + 5] =
+ ((a[3 * i + 1] >> 7) | (a[3 * i + 2] << 1)) & 7;
+ r->coeffs[8 * i + 6] = (a[3 * i + 2] >> 2) & 7;
+ r->coeffs[8 * i + 7] = (a[3 * i + 2] >> 5) & 7;
+
+ r->coeffs[8 * i + 0] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 0];
+ r->coeffs[8 * i + 1] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 1];
+ r->coeffs[8 * i + 2] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 2];
+ r->coeffs[8 * i + 3] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 3];
+ r->coeffs[8 * i + 4] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 4];
+ r->coeffs[8 * i + 5] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 5];
+ r->coeffs[8 * i + 6] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 6];
+ r->coeffs[8 * i + 7] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 7];
+ }
+#elif LC_DILITHIUM_ETA == 4
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ r->coeffs[2 * i + 0] = a[i] & 0x0F;
+ r->coeffs[2 * i + 1] = a[i] >> 4;
+ r->coeffs[2 * i + 0] = LC_DILITHIUM_ETA - r->coeffs[2 * i + 0];
+ r->coeffs[2 * i + 1] = LC_DILITHIUM_ETA - r->coeffs[2 * i + 1];
+ }
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+}
+
+/**
+ * @brief polyt1_pack - Bit-pack polynomial t1 with coefficients fitting in 10
+ * bits. Input coefficients are assumed to be standard
+ * representatives.
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYT1_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyt1_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r[5 * i + 0] = (uint8_t)((a->coeffs[4 * i + 0] >> 0));
+ r[5 * i + 1] = (uint8_t)((a->coeffs[4 * i + 0] >> 8) |
+ (a->coeffs[4 * i + 1] << 2));
+ r[5 * i + 2] = (uint8_t)((a->coeffs[4 * i + 1] >> 6) |
+ (a->coeffs[4 * i + 2] << 4));
+ r[5 * i + 3] = (uint8_t)((a->coeffs[4 * i + 2] >> 4) |
+ (a->coeffs[4 * i + 3] << 6));
+ r[5 * i + 4] = (uint8_t)((a->coeffs[4 * i + 3] >> 2));
+ }
+}
+
+/**
+ * @brief polyt0_pack - Bit-pack polynomial t0 with coefficients in
+ * ]-2^{D-1}, 2^{D-1}].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYT0_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyt0_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint32_t t[8];
+
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ t[0] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 0]);
+ t[1] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 1]);
+ t[2] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 2]);
+ t[3] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 3]);
+ t[4] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 4]);
+ t[5] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 5]);
+ t[6] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 6]);
+ t[7] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 7]);
+
+ r[13 * i + 0] = (uint8_t)(t[0]);
+ r[13 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[13 * i + 1] |= (uint8_t)(t[1] << 5);
+ r[13 * i + 2] = (uint8_t)(t[1] >> 3);
+ r[13 * i + 3] = (uint8_t)(t[1] >> 11);
+ r[13 * i + 3] |= (uint8_t)(t[2] << 2);
+ r[13 * i + 4] = (uint8_t)(t[2] >> 6);
+ r[13 * i + 4] |= (uint8_t)(t[3] << 7);
+ r[13 * i + 5] = (uint8_t)(t[3] >> 1);
+ r[13 * i + 6] = (uint8_t)(t[3] >> 9);
+ r[13 * i + 6] |= (uint8_t)(t[4] << 4);
+ r[13 * i + 7] = (uint8_t)(t[4] >> 4);
+ r[13 * i + 8] = (uint8_t)(t[4] >> 12);
+ r[13 * i + 8] |= (uint8_t)(t[5] << 1);
+ r[13 * i + 9] = (uint8_t)(t[5] >> 7);
+ r[13 * i + 9] |= (uint8_t)(t[6] << 6);
+ r[13 * i + 10] = (uint8_t)(t[6] >> 2);
+ r[13 * i + 11] = (uint8_t)(t[6] >> 10);
+ r[13 * i + 11] |= (uint8_t)(t[7] << 3);
+ r[13 * i + 12] = (uint8_t)(t[7] >> 5);
+ }
+
+ lc_memset_secure(t, 0, sizeof(t));
+}
+
+/**
+ * @brief polyt0_unpack - Unpack polynomial t0 with coefficients in
+ * ]-2^{D-1}, 2^{D-1}].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void
+polyt0_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ r->coeffs[8 * i + 0] = a[13 * i + 0];
+ r->coeffs[8 * i + 0] |= (int32_t)a[13 * i + 1] << 8;
+ r->coeffs[8 * i + 0] &= 0x1FFF;
+
+ r->coeffs[8 * i + 1] = a[13 * i + 1] >> 5;
+ r->coeffs[8 * i + 1] |= (int32_t)a[13 * i + 2] << 3;
+ r->coeffs[8 * i + 1] |= (int32_t)a[13 * i + 3] << 11;
+ r->coeffs[8 * i + 1] &= 0x1FFF;
+
+ r->coeffs[8 * i + 2] = a[13 * i + 3] >> 2;
+ r->coeffs[8 * i + 2] |= (int32_t)a[13 * i + 4] << 6;
+ r->coeffs[8 * i + 2] &= 0x1FFF;
+
+ r->coeffs[8 * i + 3] = a[13 * i + 4] >> 7;
+ r->coeffs[8 * i + 3] |= (int32_t)a[13 * i + 5] << 1;
+ r->coeffs[8 * i + 3] |= (int32_t)a[13 * i + 6] << 9;
+ r->coeffs[8 * i + 3] &= 0x1FFF;
+
+ r->coeffs[8 * i + 4] = a[13 * i + 6] >> 4;
+ r->coeffs[8 * i + 4] |= (int32_t)a[13 * i + 7] << 4;
+ r->coeffs[8 * i + 4] |= (int32_t)a[13 * i + 8] << 12;
+ r->coeffs[8 * i + 4] &= 0x1FFF;
+
+ r->coeffs[8 * i + 5] = a[13 * i + 8] >> 1;
+ r->coeffs[8 * i + 5] |= (int32_t)a[13 * i + 9] << 7;
+ r->coeffs[8 * i + 5] &= 0x1FFF;
+
+ r->coeffs[8 * i + 6] = a[13 * i + 9] >> 6;
+ r->coeffs[8 * i + 6] |= (int32_t)a[13 * i + 10] << 2;
+ r->coeffs[8 * i + 6] |= (int32_t)a[13 * i + 11] << 10;
+ r->coeffs[8 * i + 6] &= 0x1FFF;
+
+ r->coeffs[8 * i + 7] = a[13 * i + 11] >> 3;
+ r->coeffs[8 * i + 7] |= (int32_t)a[13 * i + 12] << 5;
+ r->coeffs[8 * i + 7] &= 0x1FFF;
+
+ r->coeffs[8 * i + 0] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 0];
+ r->coeffs[8 * i + 1] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 1];
+ r->coeffs[8 * i + 2] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 2];
+ r->coeffs[8 * i + 3] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 3];
+ r->coeffs[8 * i + 4] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 4];
+ r->coeffs[8 * i + 5] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 5];
+ r->coeffs[8 * i + 6] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 6];
+ r->coeffs[8 * i + 7] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 7];
+ }
+}
+
+/**
+ * @param polyz_pack - Bit-pack polynomial with coefficients
+ * in [-(GAMMA1 - 1), GAMMA1].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYZ_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyz_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint32_t t[4];
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ t[0] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 0]);
+ t[1] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 1]);
+ t[2] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 2]);
+ t[3] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 3]);
+
+ r[9 * i + 0] = (uint8_t)(t[0]);
+ r[9 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[9 * i + 2] = (uint8_t)(t[0] >> 16);
+ r[9 * i + 2] |= (uint8_t)(t[1] << 2);
+ r[9 * i + 3] = (uint8_t)(t[1] >> 6);
+ r[9 * i + 4] = (uint8_t)(t[1] >> 14);
+ r[9 * i + 4] |= (uint8_t)(t[2] << 4);
+ r[9 * i + 5] = (uint8_t)(t[2] >> 4);
+ r[9 * i + 6] = (uint8_t)(t[2] >> 12);
+ r[9 * i + 6] |= (uint8_t)(t[3] << 6);
+ r[9 * i + 7] = (uint8_t)(t[3] >> 2);
+ r[9 * i + 8] = (uint8_t)(t[3] >> 10);
+ }
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ t[0] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[2 * i + 0]);
+ t[1] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[2 * i + 1]);
+
+ r[5 * i + 0] = (uint8_t)(t[0]);
+ r[5 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[5 * i + 2] = (uint8_t)(t[0] >> 16);
+ r[5 * i + 2] |= (uint8_t)(t[1] << 4);
+ r[5 * i + 3] = (uint8_t)(t[1] >> 4);
+ r[5 * i + 4] = (uint8_t)(t[1] >> 12);
+ }
+#else
+#error "Undefined Gamma"
+#endif
+
+ lc_memset_secure(t, 0, sizeof(t));
+}
+
+/**
+ * @brief polyz_unpack - Unpack polynomial z with coefficients
+ * in [-(GAMMA1 - 1), GAMMA1].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void
+polyz_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r->coeffs[4 * i + 0] = a[9 * i + 0];
+ r->coeffs[4 * i + 0] |= (int32_t)a[9 * i + 1] << 8;
+ r->coeffs[4 * i + 0] |= (int32_t)a[9 * i + 2] << 16;
+ r->coeffs[4 * i + 0] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 1] = a[9 * i + 2] >> 2;
+ r->coeffs[4 * i + 1] |= (int32_t)a[9 * i + 3] << 6;
+ r->coeffs[4 * i + 1] |= (int32_t)a[9 * i + 4] << 14;
+ r->coeffs[4 * i + 1] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 2] = a[9 * i + 4] >> 4;
+ r->coeffs[4 * i + 2] |= (int32_t)a[9 * i + 5] << 4;
+ r->coeffs[4 * i + 2] |= (int32_t)a[9 * i + 6] << 12;
+ r->coeffs[4 * i + 2] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 3] = a[9 * i + 6] >> 6;
+ r->coeffs[4 * i + 3] |= (int32_t)a[9 * i + 7] << 2;
+ r->coeffs[4 * i + 3] |= (int32_t)a[9 * i + 8] << 10;
+ r->coeffs[4 * i + 3] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 0] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 0];
+ r->coeffs[4 * i + 1] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 1];
+ r->coeffs[4 * i + 2] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 2];
+ r->coeffs[4 * i + 3] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 3];
+ }
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ r->coeffs[2 * i + 0] = a[5 * i + 0];
+ r->coeffs[2 * i + 0] |= (int32_t)a[5 * i + 1] << 8;
+ r->coeffs[2 * i + 0] |= (int32_t)a[5 * i + 2] << 16;
+ r->coeffs[2 * i + 0] &= 0xFFFFF;
+
+ r->coeffs[2 * i + 1] = a[5 * i + 2] >> 4;
+ r->coeffs[2 * i + 1] |= (int32_t)a[5 * i + 3] << 4;
+ r->coeffs[2 * i + 1] |= (int32_t)a[5 * i + 4] << 12;
+ r->coeffs[2 * i + 1] &= 0xFFFFF;
+
+ r->coeffs[2 * i + 0] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[2 * i + 0];
+ r->coeffs[2 * i + 1] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[2 * i + 1];
+ }
+#else
+#error "Undefined Gamma"
+#endif
+}
+
+/**
+ * @brief polyw1_pack - Bit-pack polynomial w1 with coefficients in [0,15] or
+ * [0,43]. Input coefficients are assumed to be standard
+ * representatives.
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYW1_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyw1_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r[3 * i + 0] = (uint8_t)(a->coeffs[4 * i + 0]);
+ r[3 * i + 0] |= (uint8_t)(a->coeffs[4 * i + 1] << 6);
+ r[3 * i + 1] = (uint8_t)(a->coeffs[4 * i + 1] >> 2);
+ r[3 * i + 1] |= (uint8_t)(a->coeffs[4 * i + 2] << 4);
+ r[3 * i + 2] = (uint8_t)(a->coeffs[4 * i + 2] >> 4);
+ r[3 * i + 2] |= (uint8_t)(a->coeffs[4 * i + 3] << 2);
+ }
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i)
+ r[i] = (uint8_t)(a->coeffs[2 * i + 0] |
+ (a->coeffs[2 * i + 1] << 4));
+#else
+#error "Undefined Gamma"
+#endif
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_44_rounding.c b/lib/freebl/leancrypto/ml_dsa_44_rounding.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_44_rounding.c
@@ -0,0 +1,134 @@
+#define LC_DILITHIUM_TYPE_44 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_rounding.h"
+
+/**
+ * @brief power2round - For finite field element a, compute a0, a1 such that
+ * a mod^+ Q = a1*2^D + a0 with -2^{D-1} < a0 <= 2^{D-1}.
+ * Assumes a to be standard representative.
+ *
+ * @param [in] a input element
+ * @param [out] a0 pointer to output element a0
+ *
+ * @return a1.
+ */
+int32_t
+power2round(int32_t *a0, int32_t a)
+{
+ int32_t a1;
+
+ a1 = (a + (1 << (LC_DILITHIUM_D - 1)) - 1) >> LC_DILITHIUM_D;
+ *a0 = a - (a1 << LC_DILITHIUM_D);
+ return a1;
+}
+
+/**
+ * @brief decompose - For finite field element a, compute high and low bits a0,
+ * a1 such that a mod^+ Q = a1*ALPHA + a0 with
+ * -ALPHA/2 < a0 <= ALPHA/2 except if a1 = (Q-1)/ALPHA where
+ * we set a1 = 0 and -ALPHA/2 <= a0 = a mod^+ Q - Q < 0.
+ * Assumes a to be standard representative.
+ *
+ * @param [in] a input element
+ * @param [out] a0 pointer to output element a0
+ *
+ * @return a1.
+ */
+int32_t
+decompose(int32_t *a0, int32_t a)
+{
+ int32_t a1;
+
+ a1 = (a + 127) >> 7;
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ a1 = (a1 * 1025 + (1 << 21)) >> 22;
+ a1 &= 15;
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ a1 = (a1 * 11275 + (1 << 23)) >> 24;
+ a1 ^= ((43 - a1) >> 31) & a1;
+#else
+#error "Uknown GAMMA2"
+#endif
+
+ *a0 = a - a1 * 2 * LC_DILITHIUM_GAMMA2;
+ *a0 -= (((LC_DILITHIUM_Q - 1) / 2 - *a0) >> 31) & LC_DILITHIUM_Q;
+
+ return a1;
+}
+
+/**
+ * @brief make_hint - Compute hint bit indicating whether the low bits of the
+ * input element overflow into the high bits.
+ *
+ * @param a0 [in] low bits of input element
+ * @param a1 [in] high bits of input element
+ *
+ * @return 1 if overflow.
+ */
+int32_t
+make_hint(int32_t a0, int32_t a1)
+{
+ if (a0 > LC_DILITHIUM_GAMMA2 || a0 < -LC_DILITHIUM_GAMMA2 ||
+ (a0 == -LC_DILITHIUM_GAMMA2 && a1 != 0))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * @brief use_hint - Correct high bits according to hint.
+ *
+ * @param [in] a input element
+ * @param [in] hint hint bit
+ *
+ * @return corrected high bits.
+ */
+int32_t
+use_hint(int32_t a, int32_t hint)
+{
+ int32_t a0, a1;
+
+ a1 = decompose(&a0, a);
+ if (hint == 0)
+ return a1;
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ if (a0 > 0)
+ return (a1 + 1) & 15;
+ else
+ return (a1 - 1) & 15;
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ if (a0 > 0)
+ return (a1 == 43) ? 0 : a1 + 1;
+ else
+ return (a1 == 0) ? 43 : a1 - 1;
+#else
+#error "Uknown GAMMA2"
+#endif
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_44_signature_c.c b/lib/freebl/leancrypto/ml_dsa_44_signature_c.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_44_signature_c.c
@@ -0,0 +1,131 @@
+#define LC_DILITHIUM_TYPE_44 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_signature_c.h"
+#include "visibility.h"
+
+/* We need once the buffer size to handle the hashing */
+#define LC_POLY_UNIFOR_BUF_SIZE_MULTIPLIER 1
+
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_poly_c.h"
+#include "dilithium_polyvec.h"
+#include "dilithium_polyvec_c.h"
+#include "dilithium_pack.h"
+#include "dilithium_signature_impl.h"
+
+LC_INTERFACE_FUNCTION_4(int, lc_dilithium_keypair_from_seed_c,
+ struct lc_dilithium_pk *pk, struct lc_dilithium_sk *sk,
+ const uint8_t *seed, size_t seedlen)
+{
+ return lc_dilithium_keypair_from_seed_impl(pk, sk, seed, seedlen);
+}
+
+LC_INTERFACE_FUNCTION_3(int, lc_dilithium_keypair_c, struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk, struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_keypair_impl(pk, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION_5(int, lc_dilithium_sign_c, struct lc_dilithium_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_impl(sig, m, mlen, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION_6(int, lc_dilithium_sign_ctx_c,
+ struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_ctx_impl(sig, ctx, m, mlen, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION_2(int, lc_dilithium_sign_init_c,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk)
+{
+ return lc_dilithium_sign_init_impl(ctx, sk);
+}
+
+LC_INTERFACE_FUNCTION_3(int, lc_dilithium_sign_update_c,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ return lc_dilithium_sign_update_impl(ctx, m, mlen);
+}
+
+LC_INTERFACE_FUNCTION_4(int, lc_dilithium_sign_final_c,
+ struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_final_impl(sig, ctx, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION_4(int, lc_dilithium_verify_c,
+ const struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_impl(sig, m, mlen, pk);
+}
+
+LC_INTERFACE_FUNCTION_5(int, lc_dilithium_verify_ctx_c,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_ctx_impl(sig, ctx, m, mlen, pk);
+}
+
+LC_INTERFACE_FUNCTION_2(int, lc_dilithium_verify_init_c,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_init_impl(ctx, pk);
+}
+
+LC_INTERFACE_FUNCTION_3(int, lc_dilithium_verify_update_c,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ return lc_dilithium_verify_update_impl(ctx, m, mlen);
+}
+
+LC_INTERFACE_FUNCTION_3(int, lc_dilithium_verify_final_c,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_final_impl(sig, ctx, pk);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_44_signature_helper.c b/lib/freebl/leancrypto/ml_dsa_44_signature_helper.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_44_signature_helper.c
@@ -0,0 +1,103 @@
+#define LC_DILITHIUM_TYPE_44 1
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "dilithium_type.h"
+#include "visibility.h"
+
+#include "lc_sha3.h"
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc,
+ struct lc_dilithium_ctx **ctx)
+{
+ struct lc_dilithium_ctx *out_ctx = NULL;
+ int ret;
+
+ if (!ctx)
+ return -EINVAL;
+
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE);
+ if (ret)
+ return -ret;
+
+ LC_DILITHIUM_SET_CTX(out_ctx);
+
+ *ctx = out_ctx;
+
+ return 0;
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc_ahat,
+ struct lc_dilithium_ctx **ctx)
+{
+ struct lc_dilithium_ctx *out_ctx = NULL;
+ int ret;
+
+ if (!ctx)
+ return -EINVAL;
+
+#if LC_DILITHIUM_MODE == 2
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_44_AHAT_PAD +
+ LC_DILITHIUM_44_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_44_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_44_AHAT_SIZE;
+#elif LC_DILITHIUM_MODE == 3
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_65_AHAT_PAD +
+ LC_DILITHIUM_65_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_65_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_65_AHAT_SIZE;
+#elif LC_DILITHIUM_MODE == 5
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_87_AHAT_PAD +
+ LC_DILITHIUM_87_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_87_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_87_AHAT_SIZE;
+#endif
+
+ LC_SHAKE_256_CTX((&(out_ctx)->dilithium_hash_ctx));
+
+ *ctx = out_ctx;
+
+ return 0;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_zero_free,
+ struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+ lc_dilithium_ctx_zero(ctx);
+ lc_free(ctx);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_65_debug.c b/lib/freebl/leancrypto/ml_dsa_65_debug.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_65_debug.c
@@ -0,0 +1,97 @@
+#define LC_DILITHIUM_TYPE_65 1
+/*
+ * Copyright (C) 2023 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "binhexbin.h"
+
+/* This code is only tuned to the C implementation */
+#include "dilithium_type.h"
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_poly_c.h"
+#include "dilithium_polyvec.h"
+#include "dilithium_polyvec_c.h"
+
+#include "dilithium_debug.h"
+
+void
+dilithium_print_buffer(const uint8_t *buffer, const size_t bufferlen,
+ const char *explanation)
+{
+ bin2print(buffer, bufferlen, stdout, explanation);
+}
+
+void
+dilithium_print_polyvecl_k(polyvecl mat[LC_DILITHIUM_K],
+ const char *explanation)
+{
+ unsigned int i, j, k;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_K; i++) {
+ for (j = 0; j < LC_DILITHIUM_L; j++) {
+ printf("\nK(%u) x L(%u) x N: ", i, j);
+ for (k = 0; k < LC_DILITHIUM_N; k++)
+ printf("0x%.8x ", mat[i].vec[j].coeffs[k]);
+ }
+ }
+ printf("\n");
+}
+
+void
+dilithium_print_polyvecl(polyvecl *polyvec, const char *explanation)
+{
+ unsigned int i, j;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_L; i++) {
+ printf("\nL(%u) x N: ", i);
+ for (j = 0; j < LC_DILITHIUM_N; j++) {
+ printf("%d ", polyvec->vec[i].coeffs[j]);
+ }
+ }
+ printf("\n");
+}
+
+void
+dilithium_print_polyveck(polyveck *polyvec, const char *explanation)
+{
+ unsigned int i, j;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_K; i++) {
+ printf("\nK(%u) x N: ", i);
+ for (j = 0; j < LC_DILITHIUM_N; j++) {
+ printf("%d ", polyvec->vec[i].coeffs[j]);
+ }
+ }
+ printf("\n");
+}
+
+void
+dilithium_print_poly(poly *vec, const char *explanation)
+{
+ unsigned int i;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_N; i++) {
+ printf("%d ", vec->coeffs[i]);
+ }
+ printf("\n");
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_65_def_header.h b/lib/freebl/leancrypto/ml_dsa_65_def_header.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_65_def_header.h
@@ -0,0 +1 @@
+#define LC_DILITHIUM_TYPE_65 1
diff --git a/lib/freebl/leancrypto/ml_dsa_65_ntt.c b/lib/freebl/leancrypto/ml_dsa_65_ntt.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_65_ntt.c
@@ -0,0 +1,94 @@
+#define LC_DILITHIUM_TYPE_65 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_ntt.h"
+#include "dilithium_reduce.h"
+#include "dilithium_zetas.h"
+
+/**
+ * @brief ntt - Forward NTT, in-place. No modular reduction is performed after
+ * additions or subtractions. Output vector is in bitreversed
+ * order.
+ *
+ * @param [in,out] p input/output coefficient array
+ */
+void
+ntt(int32_t a[LC_DILITHIUM_N])
+{
+ unsigned int len, start, j, k;
+ int32_t zeta, t;
+
+ k = 0;
+
+ for (len = 128; len > 0; len >>= 1) {
+ for (start = 0; start < LC_DILITHIUM_N; start = j + len) {
+ zeta = dilithium_zetas[++k];
+ for (j = start; j < start + len; ++j) {
+ t = montgomery_reduce((int64_t)zeta *
+ a[j + len]);
+ a[j + len] = a[j] - t;
+ a[j] = a[j] + t;
+ }
+ }
+ }
+}
+
+/**
+ * @brief invntt_tomont - Inverse NTT and multiplication by Montgomery factor
+ * 2^32. In-place. No modular reductions after additions
+ * or subtractions; input coefficients need to be smaller
+ * than Q in absolute value. Output coefficient are
+ * smaller than Q in absolute value.
+ *
+ * @param [in,out] p input/output coefficient array
+ */
+void
+invntt_tomont(int32_t a[LC_DILITHIUM_N])
+{
+ unsigned int start, len, j, k;
+ int32_t t, zeta;
+ const int32_t f = 41978; // mont^2/256
+
+ k = 256;
+
+ for (len = 1; len < LC_DILITHIUM_N; len <<= 1) {
+ for (start = 0; start < LC_DILITHIUM_N; start = j + len) {
+ zeta = -dilithium_zetas[--k];
+ for (j = start; j < start + len; ++j) {
+ t = a[j];
+ a[j] = t + a[j + len];
+ a[j + len] = t - a[j + len];
+ a[j + len] = montgomery_reduce((int64_t)zeta *
+ a[j + len]);
+ }
+ }
+ }
+
+ for (j = 0; j < LC_DILITHIUM_N; ++j)
+ a[j] = montgomery_reduce((int64_t)f * a[j]);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_65_poly.c b/lib/freebl/leancrypto/ml_dsa_65_poly.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_65_poly.c
@@ -0,0 +1,608 @@
+#define LC_DILITHIUM_TYPE_65 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_service_helpers.h"
+#include "lc_sha3.h"
+#include "timecop.h"
+
+/**
+ * @brief poly_chknorm - Check infinity norm of polynomial against given bound.
+ * Assumes input coefficients were reduced by reduce32().
+ *
+ * @param [in] a pointer to polynomial
+ * @param [in] B norm bound
+ *
+ * @return 0 if norm is strictly smaller than B <= (Q-1)/8 and 1 otherwise.
+ */
+int
+poly_chknorm(const poly *a, int32_t B)
+{
+ unsigned int i;
+ int32_t t;
+
+ if (B > (LC_DILITHIUM_Q - 1) / 8)
+ return 1;
+
+ /*
+ * It is ok to leak which coefficient violates the bound since
+ * the probability for each coefficient *is independent of secret
+ * data but we must not leak the sign of the centralized representative.
+ */
+ for (i = 0; i < LC_DILITHIUM_N; ++i) {
+ /* Absolute value */
+ t = a->coeffs[i] >> 31;
+ t = a->coeffs[i] - (t & 2 * a->coeffs[i]);
+
+ if (t >= B)
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief poly_uniform - Sample polynomial with uniformly random coefficients
+ * in [0,Q-1] by performing rejection sampling on the
+ * output stream of SHAKE128(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed byte array with seed of length LC_DILITHIUM_SEEDBYTES
+ * @param [in] nonce 2-byte nonce
+ */
+void
+poly_uniform(poly *a, const uint8_t seed[LC_DILITHIUM_SEEDBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int i, ctr, off;
+ unsigned int buflen = POLY_UNIFORM_NBLOCKS * LC_SHAKE_128_SIZE_BLOCK;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake128);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_SEEDBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, buflen);
+ lc_hash_final(hash_ctx, buf);
+
+ lc_hash_set_digestsize(hash_ctx, LC_SHAKE_128_SIZE_BLOCK);
+
+ ctr = rej_uniform(a->coeffs, LC_DILITHIUM_N, buf, buflen);
+
+ while (ctr < LC_DILITHIUM_N) {
+ off = buflen % 3;
+ for (i = 0; i < off; ++i)
+ buf[i] = buf[buflen - off + i];
+
+ lc_hash_final(hash_ctx, buf + off);
+ buflen = LC_DILITHIUM_SEEDBYTES + off;
+ ctr += rej_uniform(a->coeffs + ctr, LC_DILITHIUM_N - ctr, buf,
+ buflen);
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief poly_uniform_eta - Sample polynomial with uniformly random
+ * coefficients in [-ETA,ETA] by performing rejection
+ * sampling on the output stream from
+ * SHAKE256(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed byte array with seed of length LC_DILITHIUM_CRHBYTES
+ * @param [in] nonce 2-byte nonce
+ */
+void
+poly_uniform_eta(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int ctr;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CRHBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, POLY_UNIFORM_ETA_BYTES);
+ lc_hash_final(hash_ctx, buf);
+
+ ctr = rej_eta(a->coeffs, LC_DILITHIUM_N, buf, POLY_UNIFORM_ETA_BYTES);
+
+ while (ctr < LC_DILITHIUM_N) {
+ lc_hash_final(hash_ctx, buf);
+
+ ctr += rej_eta(a->coeffs + ctr, LC_DILITHIUM_N - ctr, buf,
+ LC_SHAKE_256_SIZE_BLOCK);
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief poly_uniform_gamma1 - Sample polynomial with uniformly random
+ * coefficients in [-(GAMMA1 - 1), GAMMA1] by
+ * unpacking output stream of
+ * SHAKE256(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed: byte array with seed of length LC_DILITHIUM_CRHBYTES
+ * @param nonce 16-bit nonce
+ */
+void
+poly_uniform_gamma1(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CRHBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, POLY_UNIFORM_GAMMA1_BYTES);
+ lc_hash_final(hash_ctx, ws_buf);
+ lc_hash_zero(hash_ctx);
+
+ polyz_unpack(a, ws_buf);
+}
+
+/**
+ * @brief poly_challenge - Implementation of H. Samples polynomial with TAU
+ * nonzero coefficients in {-1,1} using the output
+ * stream of SHAKE256(seed).
+ *
+ * @param [out] c pointer to output polynomial
+ * @param [in] mu byte array containing seed of length LC_DILITHIUM_CTILDE_BYTES
+ */
+void
+poly_challenge(poly *c, const uint8_t seed[LC_DILITHIUM_CTILDE_BYTES],
+ void *ws_buf)
+{
+ unsigned int i, b, pos;
+ uint64_t signs;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CTILDE_BYTES);
+ lc_hash_set_digestsize(hash_ctx, POLY_CHALLENGE_BYTES);
+ lc_hash_final(hash_ctx, buf);
+
+ signs = 0;
+ for (i = 0; i < 8; ++i)
+ signs |= (uint64_t)buf[i] << 8 * i;
+ pos = 8;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ c->coeffs[i] = 0;
+
+ for (i = LC_DILITHIUM_N - LC_DILITHIUM_TAU; i < LC_DILITHIUM_N; ++i) {
+ do {
+ if (pos >= LC_SHAKE_256_SIZE_BLOCK) {
+ lc_hash_final(hash_ctx, buf);
+ pos = 0;
+ }
+
+ b = buf[pos++];
+ } while (b > i);
+
+ c->coeffs[i] = c->coeffs[b];
+ c->coeffs[b] = 1 - (int32_t)(2 * (signs & 1));
+ signs >>= 1;
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief polyeta_pack - Bit-pack polynomial with coefficients in [-ETA,ETA].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYETA_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyeta_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint8_t t[8];
+
+#if LC_DILITHIUM_ETA == 2
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ t[0] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 0]);
+ t[1] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 1]);
+ t[2] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 2]);
+ t[3] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 3]);
+ t[4] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 4]);
+ t[5] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 5]);
+ t[6] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 6]);
+ t[7] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 7]);
+
+ r[3 * i + 0] =
+ (uint8_t)((t[0] >> 0) | (t[1] << 3) | (t[2] << 6));
+ r[3 * i + 1] = (uint8_t)((t[2] >> 2) | (t[3] << 1) |
+ (t[4] << 4) | (t[5] << 7));
+ r[3 * i + 2] =
+ (uint8_t)((t[5] >> 1) | (t[6] << 2) | (t[7] << 5));
+ }
+#elif LC_DILITHIUM_ETA == 4
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ t[0] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[2 * i + 0]);
+ t[1] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[2 * i + 1]);
+ r[i] = (uint8_t)(t[0] | (t[1] << 4));
+ }
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+}
+
+/**
+ * @brief polyeta_unpack - Unpack polynomial with coefficients in [-ETA,ETA].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void
+polyeta_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_ETA == 2
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ r->coeffs[8 * i + 0] = (a[3 * i + 0] >> 0) & 7;
+ r->coeffs[8 * i + 1] = (a[3 * i + 0] >> 3) & 7;
+ r->coeffs[8 * i + 2] =
+ ((a[3 * i + 0] >> 6) | (a[3 * i + 1] << 2)) & 7;
+ r->coeffs[8 * i + 3] = (a[3 * i + 1] >> 1) & 7;
+ r->coeffs[8 * i + 4] = (a[3 * i + 1] >> 4) & 7;
+ r->coeffs[8 * i + 5] =
+ ((a[3 * i + 1] >> 7) | (a[3 * i + 2] << 1)) & 7;
+ r->coeffs[8 * i + 6] = (a[3 * i + 2] >> 2) & 7;
+ r->coeffs[8 * i + 7] = (a[3 * i + 2] >> 5) & 7;
+
+ r->coeffs[8 * i + 0] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 0];
+ r->coeffs[8 * i + 1] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 1];
+ r->coeffs[8 * i + 2] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 2];
+ r->coeffs[8 * i + 3] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 3];
+ r->coeffs[8 * i + 4] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 4];
+ r->coeffs[8 * i + 5] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 5];
+ r->coeffs[8 * i + 6] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 6];
+ r->coeffs[8 * i + 7] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 7];
+ }
+#elif LC_DILITHIUM_ETA == 4
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ r->coeffs[2 * i + 0] = a[i] & 0x0F;
+ r->coeffs[2 * i + 1] = a[i] >> 4;
+ r->coeffs[2 * i + 0] = LC_DILITHIUM_ETA - r->coeffs[2 * i + 0];
+ r->coeffs[2 * i + 1] = LC_DILITHIUM_ETA - r->coeffs[2 * i + 1];
+ }
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+}
+
+/**
+ * @brief polyt1_pack - Bit-pack polynomial t1 with coefficients fitting in 10
+ * bits. Input coefficients are assumed to be standard
+ * representatives.
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYT1_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyt1_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r[5 * i + 0] = (uint8_t)((a->coeffs[4 * i + 0] >> 0));
+ r[5 * i + 1] = (uint8_t)((a->coeffs[4 * i + 0] >> 8) |
+ (a->coeffs[4 * i + 1] << 2));
+ r[5 * i + 2] = (uint8_t)((a->coeffs[4 * i + 1] >> 6) |
+ (a->coeffs[4 * i + 2] << 4));
+ r[5 * i + 3] = (uint8_t)((a->coeffs[4 * i + 2] >> 4) |
+ (a->coeffs[4 * i + 3] << 6));
+ r[5 * i + 4] = (uint8_t)((a->coeffs[4 * i + 3] >> 2));
+ }
+}
+
+/**
+ * @brief polyt0_pack - Bit-pack polynomial t0 with coefficients in
+ * ]-2^{D-1}, 2^{D-1}].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYT0_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyt0_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint32_t t[8];
+
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ t[0] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 0]);
+ t[1] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 1]);
+ t[2] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 2]);
+ t[3] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 3]);
+ t[4] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 4]);
+ t[5] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 5]);
+ t[6] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 6]);
+ t[7] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 7]);
+
+ r[13 * i + 0] = (uint8_t)(t[0]);
+ r[13 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[13 * i + 1] |= (uint8_t)(t[1] << 5);
+ r[13 * i + 2] = (uint8_t)(t[1] >> 3);
+ r[13 * i + 3] = (uint8_t)(t[1] >> 11);
+ r[13 * i + 3] |= (uint8_t)(t[2] << 2);
+ r[13 * i + 4] = (uint8_t)(t[2] >> 6);
+ r[13 * i + 4] |= (uint8_t)(t[3] << 7);
+ r[13 * i + 5] = (uint8_t)(t[3] >> 1);
+ r[13 * i + 6] = (uint8_t)(t[3] >> 9);
+ r[13 * i + 6] |= (uint8_t)(t[4] << 4);
+ r[13 * i + 7] = (uint8_t)(t[4] >> 4);
+ r[13 * i + 8] = (uint8_t)(t[4] >> 12);
+ r[13 * i + 8] |= (uint8_t)(t[5] << 1);
+ r[13 * i + 9] = (uint8_t)(t[5] >> 7);
+ r[13 * i + 9] |= (uint8_t)(t[6] << 6);
+ r[13 * i + 10] = (uint8_t)(t[6] >> 2);
+ r[13 * i + 11] = (uint8_t)(t[6] >> 10);
+ r[13 * i + 11] |= (uint8_t)(t[7] << 3);
+ r[13 * i + 12] = (uint8_t)(t[7] >> 5);
+ }
+
+ lc_memset_secure(t, 0, sizeof(t));
+}
+
+/**
+ * @brief polyt0_unpack - Unpack polynomial t0 with coefficients in
+ * ]-2^{D-1}, 2^{D-1}].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void
+polyt0_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ r->coeffs[8 * i + 0] = a[13 * i + 0];
+ r->coeffs[8 * i + 0] |= (int32_t)a[13 * i + 1] << 8;
+ r->coeffs[8 * i + 0] &= 0x1FFF;
+
+ r->coeffs[8 * i + 1] = a[13 * i + 1] >> 5;
+ r->coeffs[8 * i + 1] |= (int32_t)a[13 * i + 2] << 3;
+ r->coeffs[8 * i + 1] |= (int32_t)a[13 * i + 3] << 11;
+ r->coeffs[8 * i + 1] &= 0x1FFF;
+
+ r->coeffs[8 * i + 2] = a[13 * i + 3] >> 2;
+ r->coeffs[8 * i + 2] |= (int32_t)a[13 * i + 4] << 6;
+ r->coeffs[8 * i + 2] &= 0x1FFF;
+
+ r->coeffs[8 * i + 3] = a[13 * i + 4] >> 7;
+ r->coeffs[8 * i + 3] |= (int32_t)a[13 * i + 5] << 1;
+ r->coeffs[8 * i + 3] |= (int32_t)a[13 * i + 6] << 9;
+ r->coeffs[8 * i + 3] &= 0x1FFF;
+
+ r->coeffs[8 * i + 4] = a[13 * i + 6] >> 4;
+ r->coeffs[8 * i + 4] |= (int32_t)a[13 * i + 7] << 4;
+ r->coeffs[8 * i + 4] |= (int32_t)a[13 * i + 8] << 12;
+ r->coeffs[8 * i + 4] &= 0x1FFF;
+
+ r->coeffs[8 * i + 5] = a[13 * i + 8] >> 1;
+ r->coeffs[8 * i + 5] |= (int32_t)a[13 * i + 9] << 7;
+ r->coeffs[8 * i + 5] &= 0x1FFF;
+
+ r->coeffs[8 * i + 6] = a[13 * i + 9] >> 6;
+ r->coeffs[8 * i + 6] |= (int32_t)a[13 * i + 10] << 2;
+ r->coeffs[8 * i + 6] |= (int32_t)a[13 * i + 11] << 10;
+ r->coeffs[8 * i + 6] &= 0x1FFF;
+
+ r->coeffs[8 * i + 7] = a[13 * i + 11] >> 3;
+ r->coeffs[8 * i + 7] |= (int32_t)a[13 * i + 12] << 5;
+ r->coeffs[8 * i + 7] &= 0x1FFF;
+
+ r->coeffs[8 * i + 0] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 0];
+ r->coeffs[8 * i + 1] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 1];
+ r->coeffs[8 * i + 2] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 2];
+ r->coeffs[8 * i + 3] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 3];
+ r->coeffs[8 * i + 4] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 4];
+ r->coeffs[8 * i + 5] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 5];
+ r->coeffs[8 * i + 6] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 6];
+ r->coeffs[8 * i + 7] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 7];
+ }
+}
+
+/**
+ * @param polyz_pack - Bit-pack polynomial with coefficients
+ * in [-(GAMMA1 - 1), GAMMA1].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYZ_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyz_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint32_t t[4];
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ t[0] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 0]);
+ t[1] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 1]);
+ t[2] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 2]);
+ t[3] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 3]);
+
+ r[9 * i + 0] = (uint8_t)(t[0]);
+ r[9 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[9 * i + 2] = (uint8_t)(t[0] >> 16);
+ r[9 * i + 2] |= (uint8_t)(t[1] << 2);
+ r[9 * i + 3] = (uint8_t)(t[1] >> 6);
+ r[9 * i + 4] = (uint8_t)(t[1] >> 14);
+ r[9 * i + 4] |= (uint8_t)(t[2] << 4);
+ r[9 * i + 5] = (uint8_t)(t[2] >> 4);
+ r[9 * i + 6] = (uint8_t)(t[2] >> 12);
+ r[9 * i + 6] |= (uint8_t)(t[3] << 6);
+ r[9 * i + 7] = (uint8_t)(t[3] >> 2);
+ r[9 * i + 8] = (uint8_t)(t[3] >> 10);
+ }
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ t[0] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[2 * i + 0]);
+ t[1] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[2 * i + 1]);
+
+ r[5 * i + 0] = (uint8_t)(t[0]);
+ r[5 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[5 * i + 2] = (uint8_t)(t[0] >> 16);
+ r[5 * i + 2] |= (uint8_t)(t[1] << 4);
+ r[5 * i + 3] = (uint8_t)(t[1] >> 4);
+ r[5 * i + 4] = (uint8_t)(t[1] >> 12);
+ }
+#else
+#error "Undefined Gamma"
+#endif
+
+ lc_memset_secure(t, 0, sizeof(t));
+}
+
+/**
+ * @brief polyz_unpack - Unpack polynomial z with coefficients
+ * in [-(GAMMA1 - 1), GAMMA1].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void
+polyz_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r->coeffs[4 * i + 0] = a[9 * i + 0];
+ r->coeffs[4 * i + 0] |= (int32_t)a[9 * i + 1] << 8;
+ r->coeffs[4 * i + 0] |= (int32_t)a[9 * i + 2] << 16;
+ r->coeffs[4 * i + 0] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 1] = a[9 * i + 2] >> 2;
+ r->coeffs[4 * i + 1] |= (int32_t)a[9 * i + 3] << 6;
+ r->coeffs[4 * i + 1] |= (int32_t)a[9 * i + 4] << 14;
+ r->coeffs[4 * i + 1] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 2] = a[9 * i + 4] >> 4;
+ r->coeffs[4 * i + 2] |= (int32_t)a[9 * i + 5] << 4;
+ r->coeffs[4 * i + 2] |= (int32_t)a[9 * i + 6] << 12;
+ r->coeffs[4 * i + 2] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 3] = a[9 * i + 6] >> 6;
+ r->coeffs[4 * i + 3] |= (int32_t)a[9 * i + 7] << 2;
+ r->coeffs[4 * i + 3] |= (int32_t)a[9 * i + 8] << 10;
+ r->coeffs[4 * i + 3] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 0] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 0];
+ r->coeffs[4 * i + 1] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 1];
+ r->coeffs[4 * i + 2] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 2];
+ r->coeffs[4 * i + 3] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 3];
+ }
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ r->coeffs[2 * i + 0] = a[5 * i + 0];
+ r->coeffs[2 * i + 0] |= (int32_t)a[5 * i + 1] << 8;
+ r->coeffs[2 * i + 0] |= (int32_t)a[5 * i + 2] << 16;
+ r->coeffs[2 * i + 0] &= 0xFFFFF;
+
+ r->coeffs[2 * i + 1] = a[5 * i + 2] >> 4;
+ r->coeffs[2 * i + 1] |= (int32_t)a[5 * i + 3] << 4;
+ r->coeffs[2 * i + 1] |= (int32_t)a[5 * i + 4] << 12;
+ r->coeffs[2 * i + 1] &= 0xFFFFF;
+
+ r->coeffs[2 * i + 0] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[2 * i + 0];
+ r->coeffs[2 * i + 1] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[2 * i + 1];
+ }
+#else
+#error "Undefined Gamma"
+#endif
+}
+
+/**
+ * @brief polyw1_pack - Bit-pack polynomial w1 with coefficients in [0,15] or
+ * [0,43]. Input coefficients are assumed to be standard
+ * representatives.
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYW1_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyw1_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r[3 * i + 0] = (uint8_t)(a->coeffs[4 * i + 0]);
+ r[3 * i + 0] |= (uint8_t)(a->coeffs[4 * i + 1] << 6);
+ r[3 * i + 1] = (uint8_t)(a->coeffs[4 * i + 1] >> 2);
+ r[3 * i + 1] |= (uint8_t)(a->coeffs[4 * i + 2] << 4);
+ r[3 * i + 2] = (uint8_t)(a->coeffs[4 * i + 2] >> 4);
+ r[3 * i + 2] |= (uint8_t)(a->coeffs[4 * i + 3] << 2);
+ }
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i)
+ r[i] = (uint8_t)(a->coeffs[2 * i + 0] |
+ (a->coeffs[2 * i + 1] << 4));
+#else
+#error "Undefined Gamma"
+#endif
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_65_rounding.c b/lib/freebl/leancrypto/ml_dsa_65_rounding.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_65_rounding.c
@@ -0,0 +1,134 @@
+#define LC_DILITHIUM_TYPE_65 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_rounding.h"
+
+/**
+ * @brief power2round - For finite field element a, compute a0, a1 such that
+ * a mod^+ Q = a1*2^D + a0 with -2^{D-1} < a0 <= 2^{D-1}.
+ * Assumes a to be standard representative.
+ *
+ * @param [in] a input element
+ * @param [out] a0 pointer to output element a0
+ *
+ * @return a1.
+ */
+int32_t
+power2round(int32_t *a0, int32_t a)
+{
+ int32_t a1;
+
+ a1 = (a + (1 << (LC_DILITHIUM_D - 1)) - 1) >> LC_DILITHIUM_D;
+ *a0 = a - (a1 << LC_DILITHIUM_D);
+ return a1;
+}
+
+/**
+ * @brief decompose - For finite field element a, compute high and low bits a0,
+ * a1 such that a mod^+ Q = a1*ALPHA + a0 with
+ * -ALPHA/2 < a0 <= ALPHA/2 except if a1 = (Q-1)/ALPHA where
+ * we set a1 = 0 and -ALPHA/2 <= a0 = a mod^+ Q - Q < 0.
+ * Assumes a to be standard representative.
+ *
+ * @param [in] a input element
+ * @param [out] a0 pointer to output element a0
+ *
+ * @return a1.
+ */
+int32_t
+decompose(int32_t *a0, int32_t a)
+{
+ int32_t a1;
+
+ a1 = (a + 127) >> 7;
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ a1 = (a1 * 1025 + (1 << 21)) >> 22;
+ a1 &= 15;
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ a1 = (a1 * 11275 + (1 << 23)) >> 24;
+ a1 ^= ((43 - a1) >> 31) & a1;
+#else
+#error "Uknown GAMMA2"
+#endif
+
+ *a0 = a - a1 * 2 * LC_DILITHIUM_GAMMA2;
+ *a0 -= (((LC_DILITHIUM_Q - 1) / 2 - *a0) >> 31) & LC_DILITHIUM_Q;
+
+ return a1;
+}
+
+/**
+ * @brief make_hint - Compute hint bit indicating whether the low bits of the
+ * input element overflow into the high bits.
+ *
+ * @param a0 [in] low bits of input element
+ * @param a1 [in] high bits of input element
+ *
+ * @return 1 if overflow.
+ */
+int32_t
+make_hint(int32_t a0, int32_t a1)
+{
+ if (a0 > LC_DILITHIUM_GAMMA2 || a0 < -LC_DILITHIUM_GAMMA2 ||
+ (a0 == -LC_DILITHIUM_GAMMA2 && a1 != 0))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * @brief use_hint - Correct high bits according to hint.
+ *
+ * @param [in] a input element
+ * @param [in] hint hint bit
+ *
+ * @return corrected high bits.
+ */
+int32_t
+use_hint(int32_t a, int32_t hint)
+{
+ int32_t a0, a1;
+
+ a1 = decompose(&a0, a);
+ if (hint == 0)
+ return a1;
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ if (a0 > 0)
+ return (a1 + 1) & 15;
+ else
+ return (a1 - 1) & 15;
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ if (a0 > 0)
+ return (a1 == 43) ? 0 : a1 + 1;
+ else
+ return (a1 == 0) ? 43 : a1 - 1;
+#else
+#error "Uknown GAMMA2"
+#endif
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_65_signature_c.c b/lib/freebl/leancrypto/ml_dsa_65_signature_c.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_65_signature_c.c
@@ -0,0 +1,131 @@
+#define LC_DILITHIUM_TYPE_65 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_signature_c.h"
+#include "visibility.h"
+
+/* We need once the buffer size to handle the hashing */
+#define LC_POLY_UNIFOR_BUF_SIZE_MULTIPLIER 1
+
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_poly_c.h"
+#include "dilithium_polyvec.h"
+#include "dilithium_polyvec_c.h"
+#include "dilithium_pack.h"
+#include "dilithium_signature_impl.h"
+
+LC_INTERFACE_FUNCTION_4(int, lc_dilithium_keypair_from_seed_c,
+ struct lc_dilithium_pk *pk, struct lc_dilithium_sk *sk,
+ const uint8_t *seed, size_t seedlen)
+{
+ return lc_dilithium_keypair_from_seed_impl(pk, sk, seed, seedlen);
+}
+
+LC_INTERFACE_FUNCTION_3(int, lc_dilithium_keypair_c, struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk, struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_keypair_impl(pk, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION_5(int, lc_dilithium_sign_c, struct lc_dilithium_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_impl(sig, m, mlen, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION_6(int, lc_dilithium_sign_ctx_c,
+ struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_ctx_impl(sig, ctx, m, mlen, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION_2(int, lc_dilithium_sign_init_c,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk)
+{
+ return lc_dilithium_sign_init_impl(ctx, sk);
+}
+
+LC_INTERFACE_FUNCTION_3(int, lc_dilithium_sign_update_c,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ return lc_dilithium_sign_update_impl(ctx, m, mlen);
+}
+
+LC_INTERFACE_FUNCTION_4(int, lc_dilithium_sign_final_c,
+ struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_final_impl(sig, ctx, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION_4(int, lc_dilithium_verify_c,
+ const struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_impl(sig, m, mlen, pk);
+}
+
+LC_INTERFACE_FUNCTION_5(int, lc_dilithium_verify_ctx_c,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_ctx_impl(sig, ctx, m, mlen, pk);
+}
+
+LC_INTERFACE_FUNCTION_2(int, lc_dilithium_verify_init_c,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_init_impl(ctx, pk);
+}
+
+LC_INTERFACE_FUNCTION_3(int, lc_dilithium_verify_update_c,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ return lc_dilithium_verify_update_impl(ctx, m, mlen);
+}
+
+LC_INTERFACE_FUNCTION_3(int, lc_dilithium_verify_final_c,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_final_impl(sig, ctx, pk);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_65_signature_helper.c b/lib/freebl/leancrypto/ml_dsa_65_signature_helper.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_65_signature_helper.c
@@ -0,0 +1,103 @@
+#define LC_DILITHIUM_TYPE_65 1
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "dilithium_type.h"
+#include "visibility.h"
+
+#include "lc_sha3.h"
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc,
+ struct lc_dilithium_ctx **ctx)
+{
+ struct lc_dilithium_ctx *out_ctx = NULL;
+ int ret;
+
+ if (!ctx)
+ return -EINVAL;
+
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE);
+ if (ret)
+ return -ret;
+
+ LC_DILITHIUM_SET_CTX(out_ctx);
+
+ *ctx = out_ctx;
+
+ return 0;
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc_ahat,
+ struct lc_dilithium_ctx **ctx)
+{
+ struct lc_dilithium_ctx *out_ctx = NULL;
+ int ret;
+
+ if (!ctx)
+ return -EINVAL;
+
+#if LC_DILITHIUM_MODE == 2
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_44_AHAT_PAD +
+ LC_DILITHIUM_44_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_44_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_44_AHAT_SIZE;
+#elif LC_DILITHIUM_MODE == 3
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_65_AHAT_PAD +
+ LC_DILITHIUM_65_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_65_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_65_AHAT_SIZE;
+#elif LC_DILITHIUM_MODE == 5
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_87_AHAT_PAD +
+ LC_DILITHIUM_87_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_87_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_87_AHAT_SIZE;
+#endif
+
+ LC_SHAKE_256_CTX((&(out_ctx)->dilithium_hash_ctx));
+
+ *ctx = out_ctx;
+
+ return 0;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_zero_free,
+ struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+ lc_dilithium_ctx_zero(ctx);
+ lc_free(ctx);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_87_debug.c b/lib/freebl/leancrypto/ml_dsa_87_debug.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_87_debug.c
@@ -0,0 +1,97 @@
+#define LC_DILITHIUM_TYPE_87 1
+/*
+ * Copyright (C) 2023 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "binhexbin.h"
+
+/* This code is only tuned to the C implementation */
+#include "dilithium_type.h"
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_poly_c.h"
+#include "dilithium_polyvec.h"
+#include "dilithium_polyvec_c.h"
+
+#include "dilithium_debug.h"
+
+void
+dilithium_print_buffer(const uint8_t *buffer, const size_t bufferlen,
+ const char *explanation)
+{
+ bin2print(buffer, bufferlen, stdout, explanation);
+}
+
+void
+dilithium_print_polyvecl_k(polyvecl mat[LC_DILITHIUM_K],
+ const char *explanation)
+{
+ unsigned int i, j, k;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_K; i++) {
+ for (j = 0; j < LC_DILITHIUM_L; j++) {
+ printf("\nK(%u) x L(%u) x N: ", i, j);
+ for (k = 0; k < LC_DILITHIUM_N; k++)
+ printf("0x%.8x ", mat[i].vec[j].coeffs[k]);
+ }
+ }
+ printf("\n");
+}
+
+void
+dilithium_print_polyvecl(polyvecl *polyvec, const char *explanation)
+{
+ unsigned int i, j;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_L; i++) {
+ printf("\nL(%u) x N: ", i);
+ for (j = 0; j < LC_DILITHIUM_N; j++) {
+ printf("%d ", polyvec->vec[i].coeffs[j]);
+ }
+ }
+ printf("\n");
+}
+
+void
+dilithium_print_polyveck(polyveck *polyvec, const char *explanation)
+{
+ unsigned int i, j;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_K; i++) {
+ printf("\nK(%u) x N: ", i);
+ for (j = 0; j < LC_DILITHIUM_N; j++) {
+ printf("%d ", polyvec->vec[i].coeffs[j]);
+ }
+ }
+ printf("\n");
+}
+
+void
+dilithium_print_poly(poly *vec, const char *explanation)
+{
+ unsigned int i;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_N; i++) {
+ printf("%d ", vec->coeffs[i]);
+ }
+ printf("\n");
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_87_def_header.h b/lib/freebl/leancrypto/ml_dsa_87_def_header.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_87_def_header.h
@@ -0,0 +1 @@
+#define LC_DILITHIUM_TYPE_87 1
diff --git a/lib/freebl/leancrypto/ml_dsa_87_ntt.c b/lib/freebl/leancrypto/ml_dsa_87_ntt.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_87_ntt.c
@@ -0,0 +1,94 @@
+#define LC_DILITHIUM_TYPE_87 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_ntt.h"
+#include "dilithium_reduce.h"
+#include "dilithium_zetas.h"
+
+/**
+ * @brief ntt - Forward NTT, in-place. No modular reduction is performed after
+ * additions or subtractions. Output vector is in bitreversed
+ * order.
+ *
+ * @param [in,out] p input/output coefficient array
+ */
+void
+ntt(int32_t a[LC_DILITHIUM_N])
+{
+ unsigned int len, start, j, k;
+ int32_t zeta, t;
+
+ k = 0;
+
+ for (len = 128; len > 0; len >>= 1) {
+ for (start = 0; start < LC_DILITHIUM_N; start = j + len) {
+ zeta = dilithium_zetas[++k];
+ for (j = start; j < start + len; ++j) {
+ t = montgomery_reduce((int64_t)zeta *
+ a[j + len]);
+ a[j + len] = a[j] - t;
+ a[j] = a[j] + t;
+ }
+ }
+ }
+}
+
+/**
+ * @brief invntt_tomont - Inverse NTT and multiplication by Montgomery factor
+ * 2^32. In-place. No modular reductions after additions
+ * or subtractions; input coefficients need to be smaller
+ * than Q in absolute value. Output coefficient are
+ * smaller than Q in absolute value.
+ *
+ * @param [in,out] p input/output coefficient array
+ */
+void
+invntt_tomont(int32_t a[LC_DILITHIUM_N])
+{
+ unsigned int start, len, j, k;
+ int32_t t, zeta;
+ const int32_t f = 41978; // mont^2/256
+
+ k = 256;
+
+ for (len = 1; len < LC_DILITHIUM_N; len <<= 1) {
+ for (start = 0; start < LC_DILITHIUM_N; start = j + len) {
+ zeta = -dilithium_zetas[--k];
+ for (j = start; j < start + len; ++j) {
+ t = a[j];
+ a[j] = t + a[j + len];
+ a[j + len] = t - a[j + len];
+ a[j + len] = montgomery_reduce((int64_t)zeta *
+ a[j + len]);
+ }
+ }
+ }
+
+ for (j = 0; j < LC_DILITHIUM_N; ++j)
+ a[j] = montgomery_reduce((int64_t)f * a[j]);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_87_poly.c b/lib/freebl/leancrypto/ml_dsa_87_poly.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_87_poly.c
@@ -0,0 +1,608 @@
+#define LC_DILITHIUM_TYPE_87 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_service_helpers.h"
+#include "lc_sha3.h"
+#include "timecop.h"
+
+/**
+ * @brief poly_chknorm - Check infinity norm of polynomial against given bound.
+ * Assumes input coefficients were reduced by reduce32().
+ *
+ * @param [in] a pointer to polynomial
+ * @param [in] B norm bound
+ *
+ * @return 0 if norm is strictly smaller than B <= (Q-1)/8 and 1 otherwise.
+ */
+int
+poly_chknorm(const poly *a, int32_t B)
+{
+ unsigned int i;
+ int32_t t;
+
+ if (B > (LC_DILITHIUM_Q - 1) / 8)
+ return 1;
+
+ /*
+ * It is ok to leak which coefficient violates the bound since
+ * the probability for each coefficient *is independent of secret
+ * data but we must not leak the sign of the centralized representative.
+ */
+ for (i = 0; i < LC_DILITHIUM_N; ++i) {
+ /* Absolute value */
+ t = a->coeffs[i] >> 31;
+ t = a->coeffs[i] - (t & 2 * a->coeffs[i]);
+
+ if (t >= B)
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief poly_uniform - Sample polynomial with uniformly random coefficients
+ * in [0,Q-1] by performing rejection sampling on the
+ * output stream of SHAKE128(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed byte array with seed of length LC_DILITHIUM_SEEDBYTES
+ * @param [in] nonce 2-byte nonce
+ */
+void
+poly_uniform(poly *a, const uint8_t seed[LC_DILITHIUM_SEEDBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int i, ctr, off;
+ unsigned int buflen = POLY_UNIFORM_NBLOCKS * LC_SHAKE_128_SIZE_BLOCK;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake128);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_SEEDBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, buflen);
+ lc_hash_final(hash_ctx, buf);
+
+ lc_hash_set_digestsize(hash_ctx, LC_SHAKE_128_SIZE_BLOCK);
+
+ ctr = rej_uniform(a->coeffs, LC_DILITHIUM_N, buf, buflen);
+
+ while (ctr < LC_DILITHIUM_N) {
+ off = buflen % 3;
+ for (i = 0; i < off; ++i)
+ buf[i] = buf[buflen - off + i];
+
+ lc_hash_final(hash_ctx, buf + off);
+ buflen = LC_DILITHIUM_SEEDBYTES + off;
+ ctr += rej_uniform(a->coeffs + ctr, LC_DILITHIUM_N - ctr, buf,
+ buflen);
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief poly_uniform_eta - Sample polynomial with uniformly random
+ * coefficients in [-ETA,ETA] by performing rejection
+ * sampling on the output stream from
+ * SHAKE256(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed byte array with seed of length LC_DILITHIUM_CRHBYTES
+ * @param [in] nonce 2-byte nonce
+ */
+void
+poly_uniform_eta(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int ctr;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CRHBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, POLY_UNIFORM_ETA_BYTES);
+ lc_hash_final(hash_ctx, buf);
+
+ ctr = rej_eta(a->coeffs, LC_DILITHIUM_N, buf, POLY_UNIFORM_ETA_BYTES);
+
+ while (ctr < LC_DILITHIUM_N) {
+ lc_hash_final(hash_ctx, buf);
+
+ ctr += rej_eta(a->coeffs + ctr, LC_DILITHIUM_N - ctr, buf,
+ LC_SHAKE_256_SIZE_BLOCK);
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief poly_uniform_gamma1 - Sample polynomial with uniformly random
+ * coefficients in [-(GAMMA1 - 1), GAMMA1] by
+ * unpacking output stream of
+ * SHAKE256(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed: byte array with seed of length LC_DILITHIUM_CRHBYTES
+ * @param nonce 16-bit nonce
+ */
+void
+poly_uniform_gamma1(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CRHBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, POLY_UNIFORM_GAMMA1_BYTES);
+ lc_hash_final(hash_ctx, ws_buf);
+ lc_hash_zero(hash_ctx);
+
+ polyz_unpack(a, ws_buf);
+}
+
+/**
+ * @brief poly_challenge - Implementation of H. Samples polynomial with TAU
+ * nonzero coefficients in {-1,1} using the output
+ * stream of SHAKE256(seed).
+ *
+ * @param [out] c pointer to output polynomial
+ * @param [in] mu byte array containing seed of length LC_DILITHIUM_CTILDE_BYTES
+ */
+void
+poly_challenge(poly *c, const uint8_t seed[LC_DILITHIUM_CTILDE_BYTES],
+ void *ws_buf)
+{
+ unsigned int i, b, pos;
+ uint64_t signs;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CTILDE_BYTES);
+ lc_hash_set_digestsize(hash_ctx, POLY_CHALLENGE_BYTES);
+ lc_hash_final(hash_ctx, buf);
+
+ signs = 0;
+ for (i = 0; i < 8; ++i)
+ signs |= (uint64_t)buf[i] << 8 * i;
+ pos = 8;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ c->coeffs[i] = 0;
+
+ for (i = LC_DILITHIUM_N - LC_DILITHIUM_TAU; i < LC_DILITHIUM_N; ++i) {
+ do {
+ if (pos >= LC_SHAKE_256_SIZE_BLOCK) {
+ lc_hash_final(hash_ctx, buf);
+ pos = 0;
+ }
+
+ b = buf[pos++];
+ } while (b > i);
+
+ c->coeffs[i] = c->coeffs[b];
+ c->coeffs[b] = 1 - (int32_t)(2 * (signs & 1));
+ signs >>= 1;
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief polyeta_pack - Bit-pack polynomial with coefficients in [-ETA,ETA].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYETA_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyeta_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint8_t t[8];
+
+#if LC_DILITHIUM_ETA == 2
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ t[0] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 0]);
+ t[1] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 1]);
+ t[2] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 2]);
+ t[3] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 3]);
+ t[4] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 4]);
+ t[5] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 5]);
+ t[6] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 6]);
+ t[7] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 7]);
+
+ r[3 * i + 0] =
+ (uint8_t)((t[0] >> 0) | (t[1] << 3) | (t[2] << 6));
+ r[3 * i + 1] = (uint8_t)((t[2] >> 2) | (t[3] << 1) |
+ (t[4] << 4) | (t[5] << 7));
+ r[3 * i + 2] =
+ (uint8_t)((t[5] >> 1) | (t[6] << 2) | (t[7] << 5));
+ }
+#elif LC_DILITHIUM_ETA == 4
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ t[0] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[2 * i + 0]);
+ t[1] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[2 * i + 1]);
+ r[i] = (uint8_t)(t[0] | (t[1] << 4));
+ }
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+}
+
+/**
+ * @brief polyeta_unpack - Unpack polynomial with coefficients in [-ETA,ETA].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void
+polyeta_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_ETA == 2
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ r->coeffs[8 * i + 0] = (a[3 * i + 0] >> 0) & 7;
+ r->coeffs[8 * i + 1] = (a[3 * i + 0] >> 3) & 7;
+ r->coeffs[8 * i + 2] =
+ ((a[3 * i + 0] >> 6) | (a[3 * i + 1] << 2)) & 7;
+ r->coeffs[8 * i + 3] = (a[3 * i + 1] >> 1) & 7;
+ r->coeffs[8 * i + 4] = (a[3 * i + 1] >> 4) & 7;
+ r->coeffs[8 * i + 5] =
+ ((a[3 * i + 1] >> 7) | (a[3 * i + 2] << 1)) & 7;
+ r->coeffs[8 * i + 6] = (a[3 * i + 2] >> 2) & 7;
+ r->coeffs[8 * i + 7] = (a[3 * i + 2] >> 5) & 7;
+
+ r->coeffs[8 * i + 0] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 0];
+ r->coeffs[8 * i + 1] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 1];
+ r->coeffs[8 * i + 2] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 2];
+ r->coeffs[8 * i + 3] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 3];
+ r->coeffs[8 * i + 4] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 4];
+ r->coeffs[8 * i + 5] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 5];
+ r->coeffs[8 * i + 6] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 6];
+ r->coeffs[8 * i + 7] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 7];
+ }
+#elif LC_DILITHIUM_ETA == 4
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ r->coeffs[2 * i + 0] = a[i] & 0x0F;
+ r->coeffs[2 * i + 1] = a[i] >> 4;
+ r->coeffs[2 * i + 0] = LC_DILITHIUM_ETA - r->coeffs[2 * i + 0];
+ r->coeffs[2 * i + 1] = LC_DILITHIUM_ETA - r->coeffs[2 * i + 1];
+ }
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+}
+
+/**
+ * @brief polyt1_pack - Bit-pack polynomial t1 with coefficients fitting in 10
+ * bits. Input coefficients are assumed to be standard
+ * representatives.
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYT1_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyt1_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r[5 * i + 0] = (uint8_t)((a->coeffs[4 * i + 0] >> 0));
+ r[5 * i + 1] = (uint8_t)((a->coeffs[4 * i + 0] >> 8) |
+ (a->coeffs[4 * i + 1] << 2));
+ r[5 * i + 2] = (uint8_t)((a->coeffs[4 * i + 1] >> 6) |
+ (a->coeffs[4 * i + 2] << 4));
+ r[5 * i + 3] = (uint8_t)((a->coeffs[4 * i + 2] >> 4) |
+ (a->coeffs[4 * i + 3] << 6));
+ r[5 * i + 4] = (uint8_t)((a->coeffs[4 * i + 3] >> 2));
+ }
+}
+
+/**
+ * @brief polyt0_pack - Bit-pack polynomial t0 with coefficients in
+ * ]-2^{D-1}, 2^{D-1}].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYT0_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyt0_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint32_t t[8];
+
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ t[0] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 0]);
+ t[1] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 1]);
+ t[2] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 2]);
+ t[3] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 3]);
+ t[4] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 4]);
+ t[5] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 5]);
+ t[6] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 6]);
+ t[7] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 7]);
+
+ r[13 * i + 0] = (uint8_t)(t[0]);
+ r[13 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[13 * i + 1] |= (uint8_t)(t[1] << 5);
+ r[13 * i + 2] = (uint8_t)(t[1] >> 3);
+ r[13 * i + 3] = (uint8_t)(t[1] >> 11);
+ r[13 * i + 3] |= (uint8_t)(t[2] << 2);
+ r[13 * i + 4] = (uint8_t)(t[2] >> 6);
+ r[13 * i + 4] |= (uint8_t)(t[3] << 7);
+ r[13 * i + 5] = (uint8_t)(t[3] >> 1);
+ r[13 * i + 6] = (uint8_t)(t[3] >> 9);
+ r[13 * i + 6] |= (uint8_t)(t[4] << 4);
+ r[13 * i + 7] = (uint8_t)(t[4] >> 4);
+ r[13 * i + 8] = (uint8_t)(t[4] >> 12);
+ r[13 * i + 8] |= (uint8_t)(t[5] << 1);
+ r[13 * i + 9] = (uint8_t)(t[5] >> 7);
+ r[13 * i + 9] |= (uint8_t)(t[6] << 6);
+ r[13 * i + 10] = (uint8_t)(t[6] >> 2);
+ r[13 * i + 11] = (uint8_t)(t[6] >> 10);
+ r[13 * i + 11] |= (uint8_t)(t[7] << 3);
+ r[13 * i + 12] = (uint8_t)(t[7] >> 5);
+ }
+
+ lc_memset_secure(t, 0, sizeof(t));
+}
+
+/**
+ * @brief polyt0_unpack - Unpack polynomial t0 with coefficients in
+ * ]-2^{D-1}, 2^{D-1}].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void
+polyt0_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ r->coeffs[8 * i + 0] = a[13 * i + 0];
+ r->coeffs[8 * i + 0] |= (int32_t)a[13 * i + 1] << 8;
+ r->coeffs[8 * i + 0] &= 0x1FFF;
+
+ r->coeffs[8 * i + 1] = a[13 * i + 1] >> 5;
+ r->coeffs[8 * i + 1] |= (int32_t)a[13 * i + 2] << 3;
+ r->coeffs[8 * i + 1] |= (int32_t)a[13 * i + 3] << 11;
+ r->coeffs[8 * i + 1] &= 0x1FFF;
+
+ r->coeffs[8 * i + 2] = a[13 * i + 3] >> 2;
+ r->coeffs[8 * i + 2] |= (int32_t)a[13 * i + 4] << 6;
+ r->coeffs[8 * i + 2] &= 0x1FFF;
+
+ r->coeffs[8 * i + 3] = a[13 * i + 4] >> 7;
+ r->coeffs[8 * i + 3] |= (int32_t)a[13 * i + 5] << 1;
+ r->coeffs[8 * i + 3] |= (int32_t)a[13 * i + 6] << 9;
+ r->coeffs[8 * i + 3] &= 0x1FFF;
+
+ r->coeffs[8 * i + 4] = a[13 * i + 6] >> 4;
+ r->coeffs[8 * i + 4] |= (int32_t)a[13 * i + 7] << 4;
+ r->coeffs[8 * i + 4] |= (int32_t)a[13 * i + 8] << 12;
+ r->coeffs[8 * i + 4] &= 0x1FFF;
+
+ r->coeffs[8 * i + 5] = a[13 * i + 8] >> 1;
+ r->coeffs[8 * i + 5] |= (int32_t)a[13 * i + 9] << 7;
+ r->coeffs[8 * i + 5] &= 0x1FFF;
+
+ r->coeffs[8 * i + 6] = a[13 * i + 9] >> 6;
+ r->coeffs[8 * i + 6] |= (int32_t)a[13 * i + 10] << 2;
+ r->coeffs[8 * i + 6] |= (int32_t)a[13 * i + 11] << 10;
+ r->coeffs[8 * i + 6] &= 0x1FFF;
+
+ r->coeffs[8 * i + 7] = a[13 * i + 11] >> 3;
+ r->coeffs[8 * i + 7] |= (int32_t)a[13 * i + 12] << 5;
+ r->coeffs[8 * i + 7] &= 0x1FFF;
+
+ r->coeffs[8 * i + 0] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 0];
+ r->coeffs[8 * i + 1] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 1];
+ r->coeffs[8 * i + 2] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 2];
+ r->coeffs[8 * i + 3] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 3];
+ r->coeffs[8 * i + 4] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 4];
+ r->coeffs[8 * i + 5] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 5];
+ r->coeffs[8 * i + 6] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 6];
+ r->coeffs[8 * i + 7] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 7];
+ }
+}
+
+/**
+ * @param polyz_pack - Bit-pack polynomial with coefficients
+ * in [-(GAMMA1 - 1), GAMMA1].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYZ_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyz_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint32_t t[4];
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ t[0] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 0]);
+ t[1] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 1]);
+ t[2] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 2]);
+ t[3] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 3]);
+
+ r[9 * i + 0] = (uint8_t)(t[0]);
+ r[9 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[9 * i + 2] = (uint8_t)(t[0] >> 16);
+ r[9 * i + 2] |= (uint8_t)(t[1] << 2);
+ r[9 * i + 3] = (uint8_t)(t[1] >> 6);
+ r[9 * i + 4] = (uint8_t)(t[1] >> 14);
+ r[9 * i + 4] |= (uint8_t)(t[2] << 4);
+ r[9 * i + 5] = (uint8_t)(t[2] >> 4);
+ r[9 * i + 6] = (uint8_t)(t[2] >> 12);
+ r[9 * i + 6] |= (uint8_t)(t[3] << 6);
+ r[9 * i + 7] = (uint8_t)(t[3] >> 2);
+ r[9 * i + 8] = (uint8_t)(t[3] >> 10);
+ }
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ t[0] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[2 * i + 0]);
+ t[1] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[2 * i + 1]);
+
+ r[5 * i + 0] = (uint8_t)(t[0]);
+ r[5 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[5 * i + 2] = (uint8_t)(t[0] >> 16);
+ r[5 * i + 2] |= (uint8_t)(t[1] << 4);
+ r[5 * i + 3] = (uint8_t)(t[1] >> 4);
+ r[5 * i + 4] = (uint8_t)(t[1] >> 12);
+ }
+#else
+#error "Undefined Gamma"
+#endif
+
+ lc_memset_secure(t, 0, sizeof(t));
+}
+
+/**
+ * @brief polyz_unpack - Unpack polynomial z with coefficients
+ * in [-(GAMMA1 - 1), GAMMA1].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void
+polyz_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r->coeffs[4 * i + 0] = a[9 * i + 0];
+ r->coeffs[4 * i + 0] |= (int32_t)a[9 * i + 1] << 8;
+ r->coeffs[4 * i + 0] |= (int32_t)a[9 * i + 2] << 16;
+ r->coeffs[4 * i + 0] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 1] = a[9 * i + 2] >> 2;
+ r->coeffs[4 * i + 1] |= (int32_t)a[9 * i + 3] << 6;
+ r->coeffs[4 * i + 1] |= (int32_t)a[9 * i + 4] << 14;
+ r->coeffs[4 * i + 1] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 2] = a[9 * i + 4] >> 4;
+ r->coeffs[4 * i + 2] |= (int32_t)a[9 * i + 5] << 4;
+ r->coeffs[4 * i + 2] |= (int32_t)a[9 * i + 6] << 12;
+ r->coeffs[4 * i + 2] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 3] = a[9 * i + 6] >> 6;
+ r->coeffs[4 * i + 3] |= (int32_t)a[9 * i + 7] << 2;
+ r->coeffs[4 * i + 3] |= (int32_t)a[9 * i + 8] << 10;
+ r->coeffs[4 * i + 3] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 0] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 0];
+ r->coeffs[4 * i + 1] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 1];
+ r->coeffs[4 * i + 2] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 2];
+ r->coeffs[4 * i + 3] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 3];
+ }
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ r->coeffs[2 * i + 0] = a[5 * i + 0];
+ r->coeffs[2 * i + 0] |= (int32_t)a[5 * i + 1] << 8;
+ r->coeffs[2 * i + 0] |= (int32_t)a[5 * i + 2] << 16;
+ r->coeffs[2 * i + 0] &= 0xFFFFF;
+
+ r->coeffs[2 * i + 1] = a[5 * i + 2] >> 4;
+ r->coeffs[2 * i + 1] |= (int32_t)a[5 * i + 3] << 4;
+ r->coeffs[2 * i + 1] |= (int32_t)a[5 * i + 4] << 12;
+ r->coeffs[2 * i + 1] &= 0xFFFFF;
+
+ r->coeffs[2 * i + 0] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[2 * i + 0];
+ r->coeffs[2 * i + 1] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[2 * i + 1];
+ }
+#else
+#error "Undefined Gamma"
+#endif
+}
+
+/**
+ * @brief polyw1_pack - Bit-pack polynomial w1 with coefficients in [0,15] or
+ * [0,43]. Input coefficients are assumed to be standard
+ * representatives.
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYW1_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void
+polyw1_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r[3 * i + 0] = (uint8_t)(a->coeffs[4 * i + 0]);
+ r[3 * i + 0] |= (uint8_t)(a->coeffs[4 * i + 1] << 6);
+ r[3 * i + 1] = (uint8_t)(a->coeffs[4 * i + 1] >> 2);
+ r[3 * i + 1] |= (uint8_t)(a->coeffs[4 * i + 2] << 4);
+ r[3 * i + 2] = (uint8_t)(a->coeffs[4 * i + 2] >> 4);
+ r[3 * i + 2] |= (uint8_t)(a->coeffs[4 * i + 3] << 2);
+ }
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i)
+ r[i] = (uint8_t)(a->coeffs[2 * i + 0] |
+ (a->coeffs[2 * i + 1] << 4));
+#else
+#error "Undefined Gamma"
+#endif
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_87_rounding.c b/lib/freebl/leancrypto/ml_dsa_87_rounding.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_87_rounding.c
@@ -0,0 +1,134 @@
+#define LC_DILITHIUM_TYPE_87 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_rounding.h"
+
+/**
+ * @brief power2round - For finite field element a, compute a0, a1 such that
+ * a mod^+ Q = a1*2^D + a0 with -2^{D-1} < a0 <= 2^{D-1}.
+ * Assumes a to be standard representative.
+ *
+ * @param [in] a input element
+ * @param [out] a0 pointer to output element a0
+ *
+ * @return a1.
+ */
+int32_t
+power2round(int32_t *a0, int32_t a)
+{
+ int32_t a1;
+
+ a1 = (a + (1 << (LC_DILITHIUM_D - 1)) - 1) >> LC_DILITHIUM_D;
+ *a0 = a - (a1 << LC_DILITHIUM_D);
+ return a1;
+}
+
+/**
+ * @brief decompose - For finite field element a, compute high and low bits a0,
+ * a1 such that a mod^+ Q = a1*ALPHA + a0 with
+ * -ALPHA/2 < a0 <= ALPHA/2 except if a1 = (Q-1)/ALPHA where
+ * we set a1 = 0 and -ALPHA/2 <= a0 = a mod^+ Q - Q < 0.
+ * Assumes a to be standard representative.
+ *
+ * @param [in] a input element
+ * @param [out] a0 pointer to output element a0
+ *
+ * @return a1.
+ */
+int32_t
+decompose(int32_t *a0, int32_t a)
+{
+ int32_t a1;
+
+ a1 = (a + 127) >> 7;
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ a1 = (a1 * 1025 + (1 << 21)) >> 22;
+ a1 &= 15;
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ a1 = (a1 * 11275 + (1 << 23)) >> 24;
+ a1 ^= ((43 - a1) >> 31) & a1;
+#else
+#error "Uknown GAMMA2"
+#endif
+
+ *a0 = a - a1 * 2 * LC_DILITHIUM_GAMMA2;
+ *a0 -= (((LC_DILITHIUM_Q - 1) / 2 - *a0) >> 31) & LC_DILITHIUM_Q;
+
+ return a1;
+}
+
+/**
+ * @brief make_hint - Compute hint bit indicating whether the low bits of the
+ * input element overflow into the high bits.
+ *
+ * @param a0 [in] low bits of input element
+ * @param a1 [in] high bits of input element
+ *
+ * @return 1 if overflow.
+ */
+int32_t
+make_hint(int32_t a0, int32_t a1)
+{
+ if (a0 > LC_DILITHIUM_GAMMA2 || a0 < -LC_DILITHIUM_GAMMA2 ||
+ (a0 == -LC_DILITHIUM_GAMMA2 && a1 != 0))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * @brief use_hint - Correct high bits according to hint.
+ *
+ * @param [in] a input element
+ * @param [in] hint hint bit
+ *
+ * @return corrected high bits.
+ */
+int32_t
+use_hint(int32_t a, int32_t hint)
+{
+ int32_t a0, a1;
+
+ a1 = decompose(&a0, a);
+ if (hint == 0)
+ return a1;
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ if (a0 > 0)
+ return (a1 + 1) & 15;
+ else
+ return (a1 - 1) & 15;
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ if (a0 > 0)
+ return (a1 == 43) ? 0 : a1 + 1;
+ else
+ return (a1 == 0) ? 43 : a1 - 1;
+#else
+#error "Uknown GAMMA2"
+#endif
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_87_signature_c.c b/lib/freebl/leancrypto/ml_dsa_87_signature_c.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_87_signature_c.c
@@ -0,0 +1,131 @@
+#define LC_DILITHIUM_TYPE_87 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_signature_c.h"
+#include "visibility.h"
+
+/* We need once the buffer size to handle the hashing */
+#define LC_POLY_UNIFOR_BUF_SIZE_MULTIPLIER 1
+
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_poly_c.h"
+#include "dilithium_polyvec.h"
+#include "dilithium_polyvec_c.h"
+#include "dilithium_pack.h"
+#include "dilithium_signature_impl.h"
+
+LC_INTERFACE_FUNCTION_4(int, lc_dilithium_keypair_from_seed_c,
+ struct lc_dilithium_pk *pk, struct lc_dilithium_sk *sk,
+ const uint8_t *seed, size_t seedlen)
+{
+ return lc_dilithium_keypair_from_seed_impl(pk, sk, seed, seedlen);
+}
+
+LC_INTERFACE_FUNCTION_3(int, lc_dilithium_keypair_c, struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk, struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_keypair_impl(pk, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION_5(int, lc_dilithium_sign_c, struct lc_dilithium_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_impl(sig, m, mlen, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION_6(int, lc_dilithium_sign_ctx_c,
+ struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_ctx_impl(sig, ctx, m, mlen, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION_2(int, lc_dilithium_sign_init_c,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk)
+{
+ return lc_dilithium_sign_init_impl(ctx, sk);
+}
+
+LC_INTERFACE_FUNCTION_3(int, lc_dilithium_sign_update_c,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ return lc_dilithium_sign_update_impl(ctx, m, mlen);
+}
+
+LC_INTERFACE_FUNCTION_4(int, lc_dilithium_sign_final_c,
+ struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_final_impl(sig, ctx, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION_4(int, lc_dilithium_verify_c,
+ const struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_impl(sig, m, mlen, pk);
+}
+
+LC_INTERFACE_FUNCTION_5(int, lc_dilithium_verify_ctx_c,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_ctx_impl(sig, ctx, m, mlen, pk);
+}
+
+LC_INTERFACE_FUNCTION_2(int, lc_dilithium_verify_init_c,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_init_impl(ctx, pk);
+}
+
+LC_INTERFACE_FUNCTION_3(int, lc_dilithium_verify_update_c,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ return lc_dilithium_verify_update_impl(ctx, m, mlen);
+}
+
+LC_INTERFACE_FUNCTION_3(int, lc_dilithium_verify_final_c,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_final_impl(sig, ctx, pk);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_87_signature_helper.c b/lib/freebl/leancrypto/ml_dsa_87_signature_helper.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_87_signature_helper.c
@@ -0,0 +1,103 @@
+#define LC_DILITHIUM_TYPE_87 1
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "dilithium_type.h"
+#include "visibility.h"
+
+#include "lc_sha3.h"
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc,
+ struct lc_dilithium_ctx **ctx)
+{
+ struct lc_dilithium_ctx *out_ctx = NULL;
+ int ret;
+
+ if (!ctx)
+ return -EINVAL;
+
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE);
+ if (ret)
+ return -ret;
+
+ LC_DILITHIUM_SET_CTX(out_ctx);
+
+ *ctx = out_ctx;
+
+ return 0;
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc_ahat,
+ struct lc_dilithium_ctx **ctx)
+{
+ struct lc_dilithium_ctx *out_ctx = NULL;
+ int ret;
+
+ if (!ctx)
+ return -EINVAL;
+
+#if LC_DILITHIUM_MODE == 2
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_44_AHAT_PAD +
+ LC_DILITHIUM_44_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_44_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_44_AHAT_SIZE;
+#elif LC_DILITHIUM_MODE == 3
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_65_AHAT_PAD +
+ LC_DILITHIUM_65_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_65_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_65_AHAT_SIZE;
+#elif LC_DILITHIUM_MODE == 5
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_87_AHAT_PAD +
+ LC_DILITHIUM_87_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_87_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_87_AHAT_SIZE;
+#endif
+
+ LC_SHAKE_256_CTX((&(out_ctx)->dilithium_hash_ctx));
+
+ *ctx = out_ctx;
+
+ return 0;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_zero_free,
+ struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+ lc_dilithium_ctx_zero(ctx);
+ lc_free(ctx);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_api.h b/lib/freebl/leancrypto/ml_dsa_api.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_api.h
@@ -0,0 +1,71 @@
+#ifndef ML_DSA_API_H
+#define ML_DSA_API_H
+// This is a generated file from the various XXX_sign.h files
+#include <stddef.h>
+#include "ml_dsa_apit.h"
+
+// from ml_dsa_44_sign.h
+int lc_dilithium_44_keypair_from_seed_c(struct lc_dilithium_44_pk *pk,
+ struct lc_dilithium_44_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+int lc_dilithium_44_sign_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_sk *sk);
+int lc_dilithium_44_sign_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_44_sign_final_c(struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_44_verify_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_pk *pk);
+int lc_dilithium_44_verify_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_44_verify_final_c(const struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_pk *pk);
+
+// from ml_dsa_65_sign.h
+int lc_dilithium_65_keypair_from_seed_c(struct lc_dilithium_65_pk *pk,
+ struct lc_dilithium_65_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+int lc_dilithium_65_sign_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_sk *sk);
+int lc_dilithium_65_sign_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_65_sign_final_c(struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_65_verify_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_pk *pk);
+int lc_dilithium_65_verify_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_65_verify_final_c(const struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_pk *pk);
+// from ml_dsa_87_sign.h
+int lc_dilithium_87_keypair_from_seed_c(struct lc_dilithium_87_pk *pk,
+ struct lc_dilithium_87_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+int lc_dilithium_87_sign_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_sk *sk);
+int lc_dilithium_87_sign_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_87_sign_final_c(struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_87_verify_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_pk *pk);
+int lc_dilithium_87_verify_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_87_verify_final_c(const struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_pk *pk);
+#endif /* ML_DSA_API_H */
diff --git a/lib/freebl/leancrypto/ml_dsa_apit.h b/lib/freebl/leancrypto/ml_dsa_apit.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_apit.h
@@ -0,0 +1,10 @@
+// This is a generated file from the various XXX_sign.h files
+#ifndef ML_DSA_APIT_H
+#define ML_DSA_APIT_H
+
+// to make the function defines work
+#ifndef RNDBYTES
+#define RNDBYTES 32
+#endif
+
+#endif /* ML_DSA_APIT_H */
diff --git a/lib/freebl/leancrypto/mldsa_api.c b/lib/freebl/leancrypto/mldsa_api.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/mldsa_api.c
@@ -0,0 +1,2544 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "ext_headers.h"
+#include "lc_dilithium.h"
+#include "dilithium_pct.h"
+#include "visibility.h"
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc,
+ struct lc_dilithium_ctx **ctx)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ctx_alloc(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ctx_alloc(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ctx_alloc(ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc_ahat,
+ struct lc_dilithium_ctx **ctx)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ctx_alloc_ahat(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ctx_alloc_ahat(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ctx_alloc_ahat(ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_zero_free,
+ struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ lc_dilithium_87_ctx_zero_free(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ lc_dilithium_65_ctx_zero_free(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ lc_dilithium_44_ctx_zero_free(ctx);
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_zero, struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ lc_dilithium_87_ctx_zero(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ lc_dilithium_65_ctx_zero(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ lc_dilithium_44_ctx_zero(ctx);
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_internal,
+ struct lc_dilithium_ctx *ctx)
+{
+ if (ctx)
+ ctx->ml_dsa_internal = 1;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_hash, struct lc_dilithium_ctx *ctx,
+ const struct lc_hash *hash)
+{
+ if (ctx)
+ ctx->dilithium_prehash_type = hash;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_userctx,
+ struct lc_dilithium_ctx *ctx, const uint8_t *userctx,
+ size_t userctxlen)
+{
+ if (ctx) {
+ ctx->userctx = userctx;
+ ctx->userctxlen = userctxlen;
+ }
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_external_mu,
+ struct lc_dilithium_ctx *ctx, const uint8_t *external_mu,
+ size_t external_mu_len)
+{
+ if (ctx) {
+ ctx->external_mu = external_mu;
+ ctx->external_mu_len = external_mu_len;
+ }
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_drop_ahat,
+ struct lc_dilithium_ctx *ctx)
+{
+ if (ctx)
+ ctx->ahat_expanded = 0;
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_sk_type,
+ const struct lc_dilithium_sk *sk)
+{
+ if (!sk)
+ return LC_DILITHIUM_UNKNOWN;
+ return sk->dilithium_type;
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_pk_type,
+ const struct lc_dilithium_pk *pk)
+{
+ if (!pk)
+ return LC_DILITHIUM_UNKNOWN;
+ return pk->dilithium_type;
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_sig_type,
+ const struct lc_dilithium_sig *sig)
+{
+ if (!sig)
+ return LC_DILITHIUM_UNKNOWN;
+ return sig->dilithium_type;
+}
+
+LC_PURE
+LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_sk_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_sk, key.sk_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_sk, key.sk_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_sk, key.sk_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_PURE
+LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_pk_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_pk, key.pk_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_pk, key.pk_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_pk, key.pk_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_PURE
+LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_sig_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_sig, sig.sig_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_sig, sig.sig_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_sig, sig.sig_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sk_load, struct lc_dilithium_sk *sk,
+ const uint8_t *src_key, size_t src_key_len)
+{
+ if (!sk || !src_key || src_key_len == 0) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (src_key_len == lc_dilithium_sk_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_sk *_sk = &sk->key.sk_87;
+
+ memcpy(_sk->sk, src_key, src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (src_key_len == lc_dilithium_sk_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_sk *_sk = &sk->key.sk_65;
+
+ memcpy(_sk->sk, src_key, src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (src_key_len == lc_dilithium_sk_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_sk *_sk = &sk->key.sk_44;
+
+ memcpy(_sk->sk, src_key, src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_pk_load, struct lc_dilithium_pk *pk,
+ const uint8_t *src_key, size_t src_key_len)
+{
+ if (!pk || !src_key || src_key_len == 0) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (src_key_len == lc_dilithium_pk_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_pk *_pk = &pk->key.pk_87;
+
+ memcpy(_pk->pk, src_key, src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (src_key_len == lc_dilithium_pk_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_pk *_pk = &pk->key.pk_65;
+
+ memcpy(_pk->pk, src_key, src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (src_key_len == lc_dilithium_pk_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_pk *_pk = &pk->key.pk_44;
+
+ memcpy(_pk->pk, src_key, src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sig_load, struct lc_dilithium_sig *sig,
+ const uint8_t *src_sig, size_t src_sig_len)
+{
+ if (!sig || !src_sig || src_sig_len == 0) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (src_sig_len == lc_dilithium_sig_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_sig *_sig = &sig->sig.sig_87;
+
+ memcpy(_sig->sig, src_sig, src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (src_sig_len == lc_dilithium_sig_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_sig *_sig = &sig->sig.sig_65;
+
+ memcpy(_sig->sig, src_sig, src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (src_sig_len == lc_dilithium_sig_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_sig *_sig = &sig->sig.sig_44;
+
+ memcpy(_sig->sig, src_sig, src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sk_ptr, uint8_t **dilithium_key,
+ size_t *dilithium_key_len, struct lc_dilithium_sk *sk)
+{
+ if (!sk || !dilithium_key || !dilithium_key_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_sk *_sk = &sk->key.sk_87;
+
+ *dilithium_key = _sk->sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_sk *_sk = &sk->key.sk_65;
+
+ *dilithium_key = _sk->sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_sk *_sk = &sk->key.sk_44;
+
+ *dilithium_key = _sk->sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_pk_ptr, uint8_t **dilithium_key,
+ size_t *dilithium_key_len, struct lc_dilithium_pk *pk)
+{
+ if (!pk || !dilithium_key || !dilithium_key_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_pk *_pk = &pk->key.pk_87;
+
+ *dilithium_key = _pk->pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_pk *_pk = &pk->key.pk_65;
+
+ *dilithium_key = _pk->pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_pk *_pk = &pk->key.pk_44;
+
+ *dilithium_key = _pk->pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sig_ptr, uint8_t **dilithium_sig,
+ size_t *dilithium_sig_len, struct lc_dilithium_sig *sig)
+{
+ if (!sig || !dilithium_sig || !dilithium_sig_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_sig *_sig = &sig->sig.sig_87;
+
+ *dilithium_sig = _sig->sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_sig *_sig = &sig->sig.sig_65;
+
+ *dilithium_sig = _sig->sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_sig *_sig = &sig->sig.sig_44;
+
+ *dilithium_sig = _sig->sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_keypair, struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk, struct lc_rng_ctx *rng_ctx,
+ enum lc_dilithium_type dilithium_type)
+{
+ if (!pk || !sk)
+ return -EINVAL;
+
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_87_keypair(&pk->key.pk_87, &sk->key.sk_87,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_65_keypair(&pk->key.pk_65, &sk->key.sk_65,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_44_keypair(&pk->key.pk_44, &sk->key.sk_44,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_keypair_from_seed,
+ struct lc_dilithium_pk *pk, struct lc_dilithium_sk *sk,
+ const uint8_t *seed, size_t seedlen,
+ enum lc_dilithium_type dilithium_type)
+{
+ if (!pk || !sk)
+ return -EINVAL;
+
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_87_keypair_from_seed(
+ &pk->key.pk_87, &sk->key.sk_87, seed, seedlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_65_keypair_from_seed(
+ &pk->key.pk_65, &sk->key.sk_65, seed, seedlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_44_keypair_from_seed(
+ &pk->key.pk_44, &sk->key.sk_44, seed, seedlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_pct, const struct lc_dilithium_pk *pk,
+ const struct lc_dilithium_sk *sk)
+{
+ return _lc_dilithium_pct_fips(pk, sk);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign, struct lc_dilithium_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_sign(&sig->sig.sig_87, m, mlen,
+ &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_sign(&sig->sig.sig_65, m, mlen,
+ &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_sign(&sig->sig.sig_44, m, mlen,
+ &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_ctx, struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_sign_ctx(&sig->sig.sig_87, ctx, m, mlen,
+ &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_sign_ctx(&sig->sig.sig_65, ctx, m, mlen,
+ &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_sign_ctx(&sig->sig.sig_44, ctx, m, mlen,
+ &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_init, struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk)
+{
+ if (!sk)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_sign_init(ctx, &sk->key.sk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_sign_init(ctx, &sk->key.sk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_sign_init(ctx, &sk->key.sk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_update,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_sign_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_sign_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_sign_update(ctx, m, mlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_final,
+ struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_sign_final(&sig->sig.sig_87, ctx,
+ &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_sign_final(&sig->sig.sig_65, ctx,
+ &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_sign_final(&sig->sig.sig_44, ctx,
+ &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify,
+ const struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ if (!pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_verify(&sig->sig.sig_87, m, mlen,
+ &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_verify(&sig->sig.sig_65, m, mlen,
+ &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_verify(&sig->sig.sig_44, m, mlen,
+ &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_ctx,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ if (!pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_verify_ctx(&sig->sig.sig_87, ctx, m,
+ mlen, &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_verify_ctx(&sig->sig.sig_65, ctx, m,
+ mlen, &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_verify_ctx(&sig->sig.sig_44, ctx, m,
+ mlen, &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_init,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ if (!pk)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_verify_init(ctx, &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_verify_init(ctx, &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_verify_init(ctx, &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_update,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_verify_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_verify_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_verify_update(ctx, m, mlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_final,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ if (!pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_verify_final(&sig->sig.sig_87, ctx,
+ &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_verify_final(&sig->sig.sig_65, ctx,
+ &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_verify_final(&sig->sig.sig_44, ctx,
+ &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/****************************** Dilithium ED25510 *****************************/
+
+#ifdef LC_DILITHIUM_ED25519_SIG
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_ctx_alloc,
+ struct lc_dilithium_ed25519_ctx **ctx)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_ctx_alloc(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ed25519_ctx_alloc(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ed25519_ctx_alloc(ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed25519_ctx_zero_free,
+ struct lc_dilithium_ed25519_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ lc_dilithium_87_ed25519_ctx_zero_free(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ lc_dilithium_65_ed25519_ctx_zero_free(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ lc_dilithium_44_ed25519_ctx_zero_free(ctx);
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed25519_ctx_zero,
+ struct lc_dilithium_ed25519_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ lc_dilithium_87_ed25519_ctx_zero(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ lc_dilithium_65_ed25519_ctx_zero(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ lc_dilithium_44_ed25519_ctx_zero(ctx);
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed25519_ctx_hash,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_hash *hash)
+{
+ if (ctx)
+ ctx->dilithium_ctx.dilithium_prehash_type = hash;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed25519_ctx_internal,
+ struct lc_dilithium_ed25519_ctx *ctx)
+{
+ if (ctx)
+ ctx->dilithium_ctx.ml_dsa_internal = 1;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed25519_ctx_userctx,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *userctx, size_t userctxlen)
+{
+ if (ctx) {
+ ctx->dilithium_ctx.userctx = userctx;
+ ctx->dilithium_ctx.userctxlen = userctxlen;
+ }
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed25519_ctx_randomizer,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *randomizer, size_t randomizerlen)
+{
+ if (ctx) {
+ ctx->dilithium_ctx.randomizer = randomizer;
+ ctx->dilithium_ctx.randomizerlen = randomizerlen;
+ }
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_ed25519_sk_type,
+ const struct lc_dilithium_ed25519_sk *sk)
+{
+ if (!sk)
+ return LC_DILITHIUM_UNKNOWN;
+ return sk->dilithium_type;
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_ed25519_pk_type,
+ const struct lc_dilithium_ed25519_pk *pk)
+{
+ if (!pk)
+ return LC_DILITHIUM_UNKNOWN;
+ return pk->dilithium_type;
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_ed25519_sig_type,
+ const struct lc_dilithium_ed25519_sig *sig)
+{
+ if (!sig)
+ return LC_DILITHIUM_UNKNOWN;
+ return sig->dilithium_type;
+}
+
+LC_PURE
+LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_ed25519_sk_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_sk,
+ key.sk_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_sk,
+ key.sk_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_sk,
+ key.sk_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_PURE
+LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_ed25519_pk_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_pk,
+ key.pk_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_pk,
+ key.pk_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_pk,
+ key.pk_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_PURE
+LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_ed25519_sig_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_sig,
+ sig.sig_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_sig,
+ sig.sig_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_sig,
+ sig.sig_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sk_load,
+ struct lc_dilithium_ed25519_sk *sk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed25519_src_key,
+ size_t ed25519_src_key_len)
+{
+ if (!sk || !dilithium_src_key || !ed25519_src_key ||
+ ed25519_src_key_len != LC_ED25519_SECRETKEYBYTES) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_sk_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_ed25519_sk *_sk = &sk->key.sk_87;
+
+ memcpy(_sk->sk.sk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_sk->sk_ed25519.sk, ed25519_src_key,
+ ed25519_src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_sk_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_ed25519_sk *_sk = &sk->key.sk_65;
+
+ memcpy(_sk->sk.sk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_sk->sk_ed25519.sk, ed25519_src_key,
+ ed25519_src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_sk_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_ed25519_sk *_sk = &sk->key.sk_44;
+
+ memcpy(_sk->sk.sk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_sk->sk_ed25519.sk, ed25519_src_key,
+ ed25519_src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_pk_load,
+ struct lc_dilithium_ed25519_pk *pk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed25519_src_key,
+ size_t ed25519_src_key_len)
+{
+ if (!pk || !dilithium_src_key || !ed25519_src_key ||
+ ed25519_src_key_len != LC_ED25519_PUBLICKEYBYTES) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_pk_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_ed25519_pk *_pk = &pk->key.pk_87;
+
+ memcpy(_pk->pk.pk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_pk->pk_ed25519.pk, ed25519_src_key,
+ ed25519_src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_pk_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_ed25519_pk *_pk = &pk->key.pk_65;
+
+ memcpy(_pk->pk.pk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_pk->pk_ed25519.pk, ed25519_src_key,
+ ed25519_src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_pk_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_ed25519_pk *_pk = &pk->key.pk_44;
+
+ memcpy(_pk->pk.pk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_pk->pk_ed25519.pk, ed25519_src_key,
+ ed25519_src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sig_load,
+ struct lc_dilithium_ed25519_sig *sig,
+ const uint8_t *dilithium_src_sig,
+ size_t dilithium_src_sig_len,
+ const uint8_t *ed25519_src_sig,
+ size_t ed25519_src_sig_len)
+{
+ if (!sig || !dilithium_src_sig || !ed25519_src_sig ||
+ ed25519_src_sig_len != LC_ED25519_SIGBYTES) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (dilithium_src_sig_len ==
+ lc_dilithium_sig_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_ed25519_sig *_sig = &sig->sig.sig_87;
+
+ memcpy(_sig->sig.sig, dilithium_src_sig, dilithium_src_sig_len);
+ memcpy(_sig->sig_ed25519.sig, ed25519_src_sig,
+ ed25519_src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (dilithium_src_sig_len ==
+ lc_dilithium_sig_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_ed25519_sig *_sig = &sig->sig.sig_65;
+
+ memcpy(_sig->sig.sig, dilithium_src_sig, dilithium_src_sig_len);
+ memcpy(_sig->sig_ed25519.sig, ed25519_src_sig,
+ ed25519_src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (dilithium_src_sig_len ==
+ lc_dilithium_sig_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_ed25519_sig *_sig = &sig->sig.sig_44;
+
+ memcpy(_sig->sig.sig, dilithium_src_sig, dilithium_src_sig_len);
+ memcpy(_sig->sig_ed25519.sig, ed25519_src_sig,
+ ed25519_src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sk_ptr, uint8_t **dilithium_key,
+ size_t *dilithium_key_len, uint8_t **ed25519_key,
+ size_t *ed25519_key_len,
+ struct lc_dilithium_ed25519_sk *sk)
+{
+ if (!sk || !dilithium_key || !dilithium_key_len || !ed25519_key ||
+ !ed25519_key_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_ed25519_sk *_sk = &sk->key.sk_87;
+
+ *dilithium_key = _sk->sk.sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ *ed25519_key = _sk->sk_ed25519.sk;
+ *ed25519_key_len = LC_ED25519_SECRETKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_ed25519_sk *_sk = &sk->key.sk_65;
+
+ *dilithium_key = _sk->sk.sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ *ed25519_key = _sk->sk_ed25519.sk;
+ *ed25519_key_len = LC_ED25519_SECRETKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_ed25519_sk *_sk = &sk->key.sk_44;
+
+ *dilithium_key = _sk->sk.sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ *ed25519_key = _sk->sk_ed25519.sk;
+ *ed25519_key_len = LC_ED25519_SECRETKEYBYTES;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_pk_ptr, uint8_t **dilithium_key,
+ size_t *dilithium_key_len, uint8_t **ed25519_key,
+ size_t *ed25519_key_len,
+ struct lc_dilithium_ed25519_pk *pk)
+{
+ if (!pk || !dilithium_key || !dilithium_key_len || !ed25519_key ||
+ !ed25519_key_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_ed25519_pk *_pk = &pk->key.pk_87;
+
+ *dilithium_key = _pk->pk.pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ *ed25519_key = _pk->pk_ed25519.pk;
+ *ed25519_key_len = LC_ED25519_PUBLICKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_ed25519_pk *_pk = &pk->key.pk_65;
+
+ *dilithium_key = _pk->pk.pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ *ed25519_key = _pk->pk_ed25519.pk;
+ *ed25519_key_len = LC_ED25519_PUBLICKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_ed25519_pk *_pk = &pk->key.pk_44;
+
+ *dilithium_key = _pk->pk.pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ *ed25519_key = _pk->pk_ed25519.pk;
+ *ed25519_key_len = LC_ED25519_PUBLICKEYBYTES;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sig_ptr,
+ uint8_t **dilithium_sig, size_t *dilithium_sig_len,
+ uint8_t **ed25519_sig, size_t *ed25519_sig_len,
+ struct lc_dilithium_ed25519_sig *sig)
+{
+ if (!sig || !dilithium_sig || !dilithium_sig_len || !ed25519_sig ||
+ !ed25519_sig_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_ed25519_sig *_sig = &sig->sig.sig_87;
+
+ *dilithium_sig = _sig->sig.sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ *ed25519_sig = _sig->sig_ed25519.sig;
+ *ed25519_sig_len = LC_ED25519_SIGBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_ed25519_sig *_sig = &sig->sig.sig_65;
+
+ *dilithium_sig = _sig->sig.sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ *ed25519_sig = _sig->sig_ed25519.sig;
+ *ed25519_sig_len = LC_ED25519_SIGBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_ed25519_sig *_sig = &sig->sig.sig_44;
+
+ *dilithium_sig = _sig->sig.sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ *ed25519_sig = _sig->sig_ed25519.sig;
+ *ed25519_sig_len = LC_ED25519_SIGBYTES;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_keypair,
+ struct lc_dilithium_ed25519_pk *pk,
+ struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx,
+ enum lc_dilithium_type dilithium_type)
+{
+ if (!pk || !sk)
+ return -EINVAL;
+
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_87_ed25519_keypair(&pk->key.pk_87,
+ &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_65_ed25519_keypair(&pk->key.pk_65,
+ &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_44_ed25519_keypair(&pk->key.pk_44,
+ &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sign,
+ struct lc_dilithium_ed25519_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_ed25519_sign(&sig->sig.sig_87, m, mlen,
+ &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_ed25519_sign(&sig->sig.sig_65, m, mlen,
+ &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_ed25519_sign(&sig->sig.sig_44, m, mlen,
+ &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sign_ctx,
+ struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_ed25519_sign_ctx(&sig->sig.sig_87, ctx,
+ m, mlen, &sk->key.sk_87,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_ed25519_sign_ctx(&sig->sig.sig_65, ctx,
+ m, mlen, &sk->key.sk_65,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_ed25519_sign_ctx(&sig->sig.sig_44, ctx,
+ m, mlen, &sk->key.sk_44,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sign_init,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_sk *sk)
+{
+ if (!ctx || !sk)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_sign_init(ctx, &sk->key.sk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed25519_sign_init(ctx, &sk->key.sk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed25519_sign_init(ctx, &sk->key.sk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sign_update,
+ struct lc_dilithium_ed25519_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_sign_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ed25519_sign_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ed25519_sign_update(ctx, m, mlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sign_final,
+ struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig || !ctx)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_ed25519_sign_final(
+ &sig->sig.sig_87, ctx, &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_ed25519_sign_final(
+ &sig->sig.sig_65, ctx, &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_ed25519_sign_final(
+ &sig->sig.sig_44, ctx, &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_verify,
+ const struct lc_dilithium_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed25519_pk *pk)
+{
+ if (!pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_verify(&sig->sig.sig_87, m, mlen,
+ &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed25519_verify(&sig->sig.sig_65, m, mlen,
+ &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed25519_verify(&sig->sig.sig_44, m, mlen,
+ &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_verify_ctx,
+ const struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_ed25519_pk *pk)
+{
+ if (!pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_verify_ctx(
+ &sig->sig.sig_87, ctx, m, mlen, &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed25519_verify_ctx(
+ &sig->sig.sig_65, ctx, m, mlen, &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed25519_verify_ctx(
+ &sig->sig.sig_44, ctx, m, mlen, &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_verify_init,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_pk *pk)
+{
+ if (!pk || !ctx)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_verify_init(ctx, &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed25519_verify_init(ctx, &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed25519_verify_init(ctx, &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_verify_update,
+ struct lc_dilithium_ed25519_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_verify_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ed25519_verify_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ed25519_verify_update(ctx, m, mlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_verify_final,
+ const struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_pk *pk)
+{
+ if (!ctx || !pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_verify_final(
+ &sig->sig.sig_87, ctx, &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed25519_verify_final(
+ &sig->sig.sig_65, ctx, &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed25519_verify_final(
+ &sig->sig.sig_44, ctx, &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+#endif /* LC_DILITHIUM_ED25519_SIG */
+
+/****************************** Dilithium ED25510 *****************************/
+
+#ifdef LC_DILITHIUM_ED448_SIG
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_ctx_alloc,
+ struct lc_dilithium_ed448_ctx **ctx)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_ctx_alloc(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ed448_ctx_alloc(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ed448_ctx_alloc(ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed448_ctx_zero_free,
+ struct lc_dilithium_ed448_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ lc_dilithium_87_ed448_ctx_zero_free(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ lc_dilithium_65_ed448_ctx_zero_free(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ lc_dilithium_44_ed448_ctx_zero_free(ctx);
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed448_ctx_zero,
+ struct lc_dilithium_ed448_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ lc_dilithium_87_ed448_ctx_zero(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ lc_dilithium_65_ed448_ctx_zero(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ lc_dilithium_44_ed448_ctx_zero(ctx);
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed448_ctx_hash,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_hash *hash)
+{
+ if (ctx)
+ ctx->dilithium_ctx.dilithium_prehash_type = hash;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed448_ctx_internal,
+ struct lc_dilithium_ed448_ctx *ctx)
+{
+ if (ctx)
+ ctx->dilithium_ctx.ml_dsa_internal = 1;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed448_ctx_userctx,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *userctx, size_t userctxlen)
+{
+ if (ctx) {
+ ctx->dilithium_ctx.userctx = userctx;
+ ctx->dilithium_ctx.userctxlen = userctxlen;
+ }
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed448_ctx_randomizer,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *randomizer, size_t randomizerlen)
+{
+ if (ctx) {
+ ctx->dilithium_ctx.randomizer = randomizer;
+ ctx->dilithium_ctx.randomizerlen = randomizerlen;
+ }
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_ed448_sk_type,
+ const struct lc_dilithium_ed448_sk *sk)
+{
+ if (!sk)
+ return LC_DILITHIUM_UNKNOWN;
+ return sk->dilithium_type;
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_ed448_pk_type,
+ const struct lc_dilithium_ed448_pk *pk)
+{
+ if (!pk)
+ return LC_DILITHIUM_UNKNOWN;
+ return pk->dilithium_type;
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_ed448_sig_type,
+ const struct lc_dilithium_ed448_sig *sig)
+{
+ if (!sig)
+ return LC_DILITHIUM_UNKNOWN;
+ return sig->dilithium_type;
+}
+
+LC_PURE
+LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_ed448_sk_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_sk, key.sk_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_sk, key.sk_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_sk, key.sk_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_PURE
+LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_ed448_pk_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_pk, key.pk_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_pk, key.pk_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_pk, key.pk_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_PURE
+LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_ed448_sig_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_sig,
+ sig.sig_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_sig,
+ sig.sig_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_sig,
+ sig.sig_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sk_load,
+ struct lc_dilithium_ed448_sk *sk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed448_src_key, size_t ed448_src_key_len)
+{
+ if (!sk || !dilithium_src_key || !ed448_src_key ||
+ ed448_src_key_len != LC_ED448_SECRETKEYBYTES) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_sk_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_ed448_sk *_sk = &sk->key.sk_87;
+
+ memcpy(_sk->sk.sk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_sk->sk_ed448.sk, ed448_src_key, ed448_src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_sk_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_ed448_sk *_sk = &sk->key.sk_65;
+
+ memcpy(_sk->sk.sk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_sk->sk_ed448.sk, ed448_src_key, ed448_src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_sk_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_ed448_sk *_sk = &sk->key.sk_44;
+
+ memcpy(_sk->sk.sk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_sk->sk_ed448.sk, ed448_src_key, ed448_src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_pk_load,
+ struct lc_dilithium_ed448_pk *pk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed448_src_key, size_t ed448_src_key_len)
+{
+ if (!pk || !dilithium_src_key || !ed448_src_key ||
+ ed448_src_key_len != LC_ED448_PUBLICKEYBYTES) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_pk_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_ed448_pk *_pk = &pk->key.pk_87;
+
+ memcpy(_pk->pk.pk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_pk->pk_ed448.pk, ed448_src_key, ed448_src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_pk_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_ed448_pk *_pk = &pk->key.pk_65;
+
+ memcpy(_pk->pk.pk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_pk->pk_ed448.pk, ed448_src_key, ed448_src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_pk_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_ed448_pk *_pk = &pk->key.pk_44;
+
+ memcpy(_pk->pk.pk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_pk->pk_ed448.pk, ed448_src_key, ed448_src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sig_load,
+ struct lc_dilithium_ed448_sig *sig,
+ const uint8_t *dilithium_src_sig,
+ size_t dilithium_src_sig_len,
+ const uint8_t *ed448_src_sig, size_t ed448_src_sig_len)
+{
+ if (!sig || !dilithium_src_sig || !ed448_src_sig ||
+ ed448_src_sig_len != LC_ED448_SIGBYTES) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (dilithium_src_sig_len ==
+ lc_dilithium_sig_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_ed448_sig *_sig = &sig->sig.sig_87;
+
+ memcpy(_sig->sig.sig, dilithium_src_sig, dilithium_src_sig_len);
+ memcpy(_sig->sig_ed448.sig, ed448_src_sig, ed448_src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (dilithium_src_sig_len ==
+ lc_dilithium_sig_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_ed448_sig *_sig = &sig->sig.sig_65;
+
+ memcpy(_sig->sig.sig, dilithium_src_sig, dilithium_src_sig_len);
+ memcpy(_sig->sig_ed448.sig, ed448_src_sig, ed448_src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (dilithium_src_sig_len ==
+ lc_dilithium_sig_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_ed448_sig *_sig = &sig->sig.sig_44;
+
+ memcpy(_sig->sig.sig, dilithium_src_sig, dilithium_src_sig_len);
+ memcpy(_sig->sig_ed448.sig, ed448_src_sig, ed448_src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sk_ptr, uint8_t **dilithium_key,
+ size_t *dilithium_key_len, uint8_t **ed448_key,
+ size_t *ed448_key_len, struct lc_dilithium_ed448_sk *sk)
+{
+ if (!sk || !dilithium_key || !dilithium_key_len || !ed448_key ||
+ !ed448_key_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_ed448_sk *_sk = &sk->key.sk_87;
+
+ *dilithium_key = _sk->sk.sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ *ed448_key = _sk->sk_ed448.sk;
+ *ed448_key_len = LC_ED448_SECRETKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_ed448_sk *_sk = &sk->key.sk_65;
+
+ *dilithium_key = _sk->sk.sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ *ed448_key = _sk->sk_ed448.sk;
+ *ed448_key_len = LC_ED448_SECRETKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_ed448_sk *_sk = &sk->key.sk_44;
+
+ *dilithium_key = _sk->sk.sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ *ed448_key = _sk->sk_ed448.sk;
+ *ed448_key_len = LC_ED448_SECRETKEYBYTES;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_pk_ptr, uint8_t **dilithium_key,
+ size_t *dilithium_key_len, uint8_t **ed448_key,
+ size_t *ed448_key_len, struct lc_dilithium_ed448_pk *pk)
+{
+ if (!pk || !dilithium_key || !dilithium_key_len || !ed448_key ||
+ !ed448_key_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_ed448_pk *_pk = &pk->key.pk_87;
+
+ *dilithium_key = _pk->pk.pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ *ed448_key = _pk->pk_ed448.pk;
+ *ed448_key_len = LC_ED448_PUBLICKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_ed448_pk *_pk = &pk->key.pk_65;
+
+ *dilithium_key = _pk->pk.pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ *ed448_key = _pk->pk_ed448.pk;
+ *ed448_key_len = LC_ED448_PUBLICKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_ed448_pk *_pk = &pk->key.pk_44;
+
+ *dilithium_key = _pk->pk.pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ *ed448_key = _pk->pk_ed448.pk;
+ *ed448_key_len = LC_ED448_PUBLICKEYBYTES;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sig_ptr, uint8_t **dilithium_sig,
+ size_t *dilithium_sig_len, uint8_t **ed448_sig,
+ size_t *ed448_sig_len, struct lc_dilithium_ed448_sig *sig)
+{
+ if (!sig || !dilithium_sig || !dilithium_sig_len || !ed448_sig ||
+ !ed448_sig_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_ed448_sig *_sig = &sig->sig.sig_87;
+
+ *dilithium_sig = _sig->sig.sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ *ed448_sig = _sig->sig_ed448.sig;
+ *ed448_sig_len = LC_ED448_SIGBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_ed448_sig *_sig = &sig->sig.sig_65;
+
+ *dilithium_sig = _sig->sig.sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ *ed448_sig = _sig->sig_ed448.sig;
+ *ed448_sig_len = LC_ED448_SIGBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_ed448_sig *_sig = &sig->sig.sig_44;
+
+ *dilithium_sig = _sig->sig.sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ *ed448_sig = _sig->sig_ed448.sig;
+ *ed448_sig_len = LC_ED448_SIGBYTES;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_keypair,
+ struct lc_dilithium_ed448_pk *pk,
+ struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx,
+ enum lc_dilithium_type dilithium_type)
+{
+ if (!pk || !sk)
+ return -EINVAL;
+
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_87_ed448_keypair(&pk->key.pk_87,
+ &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_65_ed448_keypair(&pk->key.pk_65,
+ &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_44_ed448_keypair(&pk->key.pk_44,
+ &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sign,
+ struct lc_dilithium_ed448_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_ed448_sign(&sig->sig.sig_87, m, mlen,
+ &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_ed448_sign(&sig->sig.sig_65, m, mlen,
+ &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_ed448_sign(&sig->sig.sig_44, m, mlen,
+ &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sign_ctx,
+ struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_ed448_sign_ctx(&sig->sig.sig_87, ctx, m,
+ mlen, &sk->key.sk_87,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_ed448_sign_ctx(&sig->sig.sig_65, ctx, m,
+ mlen, &sk->key.sk_65,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_ed448_sign_ctx(&sig->sig.sig_44, ctx, m,
+ mlen, &sk->key.sk_44,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sign_init,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_sk *sk)
+{
+ if (!ctx || !sk)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_sign_init(ctx, &sk->key.sk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed448_sign_init(ctx, &sk->key.sk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed448_sign_init(ctx, &sk->key.sk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sign_update,
+ struct lc_dilithium_ed448_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_sign_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ed448_sign_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ed448_sign_update(ctx, m, mlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sign_final,
+ struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig || !ctx)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_ed448_sign_final(
+ &sig->sig.sig_87, ctx, &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_ed448_sign_final(
+ &sig->sig.sig_65, ctx, &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_ed448_sign_final(
+ &sig->sig.sig_44, ctx, &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_verify,
+ const struct lc_dilithium_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed448_pk *pk)
+{
+ if (!pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_verify(&sig->sig.sig_87, m, mlen,
+ &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed448_verify(&sig->sig.sig_65, m, mlen,
+ &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed448_verify(&sig->sig.sig_44, m, mlen,
+ &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_verify_ctx,
+ const struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_ed448_pk *pk)
+{
+ if (!pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_verify_ctx(
+ &sig->sig.sig_87, ctx, m, mlen, &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed448_verify_ctx(
+ &sig->sig.sig_65, ctx, m, mlen, &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed448_verify_ctx(
+ &sig->sig.sig_44, ctx, m, mlen, &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_verify_init,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_pk *pk)
+{
+ if (!pk || !ctx)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_verify_init(ctx, &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed448_verify_init(ctx, &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed448_verify_init(ctx, &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_verify_update,
+ struct lc_dilithium_ed448_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_verify_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ed448_verify_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ed448_verify_update(ctx, m, mlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_verify_final,
+ const struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_pk *pk)
+{
+ if (!ctx || !pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_verify_final(&sig->sig.sig_87, ctx,
+ &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed448_verify_final(&sig->sig.sig_65, ctx,
+ &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed448_verify_final(&sig->sig.sig_44, ctx,
+ &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+#endif /* LC_DILITHIUM_ED448_SIG */
diff --git a/lib/freebl/leancrypto/mldsa_zetas.c b/lib/freebl/leancrypto/mldsa_zetas.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/mldsa_zetas.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_zetas.h"
+#include "ext_headers.h"
+
+LC_FIPS_RODATA_SECTION
+const int32_t dilithium_zetas[LC_DILITHIUM_N] = {
+ 0, 25847, -2608894, -518909, 237124, -777960, -876248,
+ 466468, 1826347, 2353451, -359251, -2091905, 3119733, -2884855,
+ 3111497, 2680103, 2725464, 1024112, -1079900, 3585928, -549488,
+ -1119584, 2619752, -2108549, -2118186, -3859737, -1399561, -3277672,
+ 1757237, -19422, 4010497, 280005, 2706023, 95776, 3077325,
+ 3530437, -1661693, -3592148, -2537516, 3915439, -3861115, -3043716,
+ 3574422, -2867647, 3539968, -300467, 2348700, -539299, -1699267,
+ -1643818, 3505694, -3821735, 3507263, -2140649, -1600420, 3699596,
+ 811944, 531354, 954230, 3881043, 3900724, -2556880, 2071892,
+ -2797779, -3930395, -1528703, -3677745, -3041255, -1452451, 3475950,
+ 2176455, -1585221, -1257611, 1939314, -4083598, -1000202, -3190144,
+ -3157330, -3632928, 126922, 3412210, -983419, 2147896, 2715295,
+ -2967645, -3693493, -411027, -2477047, -671102, -1228525, -22981,
+ -1308169, -381987, 1349076, 1852771, -1430430, -3343383, 264944,
+ 508951, 3097992, 44288, -1100098, 904516, 3958618, -3724342,
+ -8578, 1653064, -3249728, 2389356, -210977, 759969, -1316856,
+ 189548, -3553272, 3159746, -1851402, -2409325, -177440, 1315589,
+ 1341330, 1285669, -1584928, -812732, -1439742, -3019102, -3881060,
+ -3628969, 3839961, 2091667, 3407706, 2316500, 3817976, -3342478,
+ 2244091, -2446433, -3562462, 266997, 2434439, -1235728, 3513181,
+ -3520352, -3759364, -1197226, -3193378, 900702, 1859098, 909542,
+ 819034, 495491, -1613174, -43260, -522500, -655327, -3122442,
+ 2031748, 3207046, -3556995, -525098, -768622, -3595838, 342297,
+ 286988, -2437823, 4108315, 3437287, -3342277, 1735879, 203044,
+ 2842341, 2691481, -2590150, 1265009, 4055324, 1247620, 2486353,
+ 1595974, -3767016, 1250494, 2635921, -3548272, -2994039, 1869119,
+ 1903435, -1050970, -1333058, 1237275, -3318210, -1430225, -451100,
+ 1312455, 3306115, -1962642, -1279661, 1917081, -2546312, -1374803,
+ 1500165, 777191, 2235880, 3406031, -542412, -2831860, -1671176,
+ -1846953, -2584293, -3724270, 594136, -3776993, -2013608, 2432395,
+ 2454455, -164721, 1957272, 3369112, 185531, -1207385, -3183426,
+ 162844, 1616392, 3014001, 810149, 1652634, -3694233, -1799107,
+ -3038916, 3523897, 3866901, 269760, 2213111, -975884, 1717735,
+ 472078, -426683, 1723600, -1803090, 1910376, -1667432, -1104333,
+ -260646, -3833893, -2939036, -2235985, -420899, -2286327, 183443,
+ -976891, 1612842, -3545687, -554416, 3919660, -48306, -1362209,
+ 3937738, 1400424, -846154, 1976782
+};
diff --git a/lib/freebl/leancrypto/mutex_w.h b/lib/freebl/leancrypto/mutex_w.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/mutex_w.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see COPYING file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef _MUTEX_W_H
+#define _MUTEX_W_H
+
+#include "atomic.h"
+#include "atomic_bool.h"
+
+/**
+ * @brief Writer mutex with a polling mechanism
+ *
+ * This mutex and its implementation below is intended to cover the needs of
+ * leancrypto and having no dependencies whatsoever. Thus, the implementation
+ * is below is not intended for general-purpose use! Yet, it serves its purpose
+ * for leancrypto.
+ */
+typedef struct {
+ atomic_bool_t lock;
+ atomic_t writer_pending;
+} mutex_w_t;
+
+/* 1 microsecond when using nanosleep */
+#define MUTEX_DEFAULT_SLEEP_TIME_NS (1 << 10)
+/** 1 << (MUTEX_DEFAULT_SLEEP_TIME_NS + MUTEX_MAX_INC_BITS) */
+#define MUTEX_MAX_INC_BITS 14
+
+#define __MUTEX_W_INITIALIZER(locked) \
+ { \
+ .lock = ATOMIC_BOOL_INIT(locked), \
+ .writer_pending = ATOMIC_INIT(0), \
+ }
+
+#define DEFINE_MUTEX_W_UNLOCKED(name) \
+ mutex_w_t name = __MUTEX_W_INITIALIZER(false)
+
+#define DEFINE_MUTEX_W_LOCKED(name) mutex_w_t name = __MUTEX_W_INITIALIZER(true)
+
+/*
+ * Instead of using a environment-dependent nanosleep implementation, we use
+ * a small busy-wait loop which should serve the purpose of the short-duration
+ * contentions possible in leancrypto.
+ */
+#if 0
+#include <time.h>
+static inline void mutex_w_sleep(mutex_w_t *mutex)
+{
+ struct timespec sleeptime = { .tv_sec = 0,
+ .tv_nsec = MUTEX_DEFAULT_SLEEP_TIME_NS};
+ int pending = atomic_read(&mutex->writer_pending);
+
+ /* Increase wait time exponentially depending on waiters */
+ if (pending > MUTEX_MAX_INC_BITS)
+ pending = MUTEX_MAX_INC_BITS;
+ if (pending < 0)
+ pending = 0;
+ sleeptime.tv_nsec <<= pending;
+ nanosleep(&sleeptime, NULL);
+}
+#else
+static inline void
+mutex_w_sleep(mutex_w_t *mutex)
+{
+ uint64_t tv_nsec = MUTEX_DEFAULT_SLEEP_TIME_NS;
+ /*
+ * Use volatile to ensure the compiler does not optimize the busyloop
+ * away.
+ */
+ volatile uint64_t i;
+ int pending = atomic_read(&mutex->writer_pending);
+
+ /* Increase wait time exponentially depending on waiters */
+ if (pending > MUTEX_MAX_INC_BITS)
+ pending = MUTEX_MAX_INC_BITS;
+ if (pending < 0)
+ pending = 0;
+ tv_nsec <<= pending;
+
+ /* Busy-loop for sleeping */
+ for (i = 0; i < tv_nsec; i++)
+ ;
+}
+#endif
+
+/**
+ * @brief Initialize a mutex
+ * @param mutex [in] Lock variable to initialize.
+ * @param locked [in] Specify whether the lock shall already be locked (true)
+ * or unlocked (false).
+ */
+static inline void
+mutex_w_init(mutex_w_t *mutex, bool locked)
+{
+ atomic_bool_set(locked, &mutex->lock);
+ atomic_set(&mutex->writer_pending, 0);
+}
+
+static inline void
+mutex_w_destroy(mutex_w_t *mutex)
+{
+ (void)mutex;
+}
+
+/**
+ * Mutual exclusion lock (covering also the reader lock use case).
+ * @param mutex [in] lock variable to lock
+ */
+static inline void
+mutex_w_lock(mutex_w_t *mutex)
+{
+ atomic_inc(&mutex->writer_pending);
+
+ /* Take the writer lock only if no writer lock is taken. */
+ while (!atomic_bool_cmpxchg(&mutex->lock, false, true))
+ mutex_w_sleep(mutex);
+
+ atomic_dec(&mutex->writer_pending);
+}
+
+/**
+ * Mutual exclusion lock: Attempt to take the lock. The function will never
+ * block but return whether the lock was successfully taken or not.
+ *
+ * @param mutex [in] lock variable to lock
+ * @return true if lock was taken, false if lock was not taken
+ */
+static inline bool
+mutex_w_trylock(mutex_w_t *mutex)
+{
+ return atomic_bool_cmpxchg(&mutex->lock, false, true);
+}
+
+static inline bool
+mutex_w_islocked(mutex_w_t *mutex)
+{
+ return atomic_bool_read(&mutex->lock);
+}
+
+/**
+ * Unlock the lock
+ * @param mutex [in] lock variable to lock
+ */
+static inline void
+mutex_w_unlock(mutex_w_t *mutex)
+{
+ /* Release the writer lock. */
+ atomic_bool_cmpxchg(&mutex->lock, true, false);
+}
+
+#endif /* _MUTEX_W_H */
diff --git a/lib/freebl/leancrypto/new_headers/lc_hash.h b/lib/freebl/leancrypto/new_headers/lc_hash.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/new_headers/lc_hash.h
@@ -0,0 +1,15 @@
+#include "hasht.h"
+#include "blapit.h"
+#include "lc_memset_secure.h" /* sigh the original included it,
+ * so some files expect it to be there already */
+#include "lc_memory_support.h"
+/* sigh Stephen doesn seem to believe in typedef, so
+ * just stuff our context pointer into a struct */
+#define LC_SHA3_256_CTX_SIZE (SHA3_256_BLOCK_LENGTH)
+#define LC_SHA3_STATE_SIZE_ALIGN(x) (x)
+
+#define LC_SHA3_512_SIZE_DIGEST SHA3_512_LENGTH
+
+#ifndef LC_HASH_COMMON_ALIGNMENT
+#define LC_HASH_COMMON_ALIGNMENT 64
+#endif
diff --git a/lib/freebl/leancrypto/new_headers/lc_rng.h b/lib/freebl/leancrypto/new_headers/lc_rng.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/new_headers/lc_rng.h
@@ -0,0 +1,58 @@
+#ifndef LC_RNG_H
+#define LC_RNG_H 1
+#include <blapi.h>
+#include <secrng.h>
+
+struct lc_rng_ctx;
+extern struct lc_rng_ctx *lc_seeded_rng;
+
+/* just enough of the rng_context to make the code happy.
+ * in the end, we just use our NSS internal RNG */
+struct lc_static_rng_data {
+ const unsigned char *seed;
+ size_t seedlen;
+};
+
+struct lc_rng_ctx {
+ struct lc_static_rng_data *dummy;
+};
+
+static inline int
+lc_rng_generate(struct lc_rng_ctx *rng,
+ unsigned char *addinput,
+ size_t addlen,
+ unsigned char *out,
+ size_t outlen)
+{
+ size_t len;
+ if (rng->dummy != NULL) {
+ if (outlen > rng->dummy->seedlen) {
+ return -1;
+ }
+ PORT_Memcpy(out, rng->dummy->seed, outlen);
+ return 0;
+ }
+ if (addlen != 0) {
+ RNG_RandomUpdate(addinput, addlen);
+ }
+ len = RNG_SystemRNG(out, outlen);
+ if (len != outlen) {
+ return -1;
+ }
+ return 0;
+}
+
+#define lc_rng_seed(rng, seed, seedlen, pers, perslen) \
+ { \
+ if (pers_len != 0) { \
+ RNG_RandomUpdate(pers, perslen); \
+ } \
+ RNG_SystemRNG(seed, seedlen); \
+ }
+#define lc_rng_check(rng)
+
+#define LC_STATIC_DRNG_ON_STACK(sdrng, state) \
+ struct lc_rng_ctx sdrng; \
+ sdrng.dummy = state;
+
+#endif
diff --git a/lib/freebl/leancrypto/new_headers/lc_sha3.h b/lib/freebl/leancrypto/new_headers/lc_sha3.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/new_headers/lc_sha3.h
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: MIT
+// NSS SHA3 bindings for ML-DSA leancrypto
+
+#ifndef LC_SHA3__H
+#define LC_SHA3__H
+#include <blapi.h>
+
+#define LC_SHA3_SIZE_RATE(bits) ((1600 - 2 * bits) >> 3)
+
+#define LC_SHAKE_128_SIZE_BLOCK LC_SHA3_SIZE_RATE(128)
+#define LC_SHAKE_256_SIZE_BLOCK LC_SHA3_SIZE_RATE(256)
+
+/*#define SHAKE128_RATE 168
+#define shake128 SHAKE_128_HashBuf
+
+#define SHAKE256_RATE SHA3_256_BLOCK_LENGTH
+#define shake256 SHAKE_256_HashBuf */
+
+#define LC_HASH_CTX_ON_STACK(name, type_) \
+ struct lc_hash_ctx _##name; \
+ _##name.hash = type_; \
+ _##name.buf = NULL; \
+ _##name.stream = false; \
+ _##name.u.ctx_ptr = NULL; \
+ struct lc_hash_ctx *name = &_##name;
+
+#define LC_HASH_SET_CTX(name, type_) \
+ name->hash = type_;
+
+#define LC_SHAKE_256_CTX(name) \
+ LC_HASH_SET_CTX(name, lc_shake256);
+
+typedef enum {
+ lc_shake128,
+ lc_shake256,
+} sha3Type;
+
+/* sigh, we buffer eKEverything because we can't correctly do multiple
+ * finals correctly. In cases where we know we are not going to
+ * do multiple finals, set the streaming bool */
+struct lc_hash_ctx {
+ sha3Type hash;
+ bool stream;
+ union {
+ SHAKE_256Context *shake256_ctx;
+ SHAKE_128Context *shake128_ctx;
+ void *ctx_ptr;
+ } u;
+ size_t digestSize;
+ size_t current_input;
+ size_t current_output;
+ unsigned char *buf;
+ size_t buf_size;
+ unsigned char buf_space[2048];
+ unsigned char buf2_space[2048];
+};
+
+#define lc_xof(type, in, inlen, out, outlen) \
+ switch (type) { \
+ case lc_shake128: \
+ SHAKE_128_HashBuf(out, outlen, in, inlen); \
+ break; \
+ case lc_shake256: \
+ SHAKE_256_HashBuf(out, outlen, in, inlen); \
+ break; \
+ default: \
+ assert(0); \
+ }
+
+static inline void
+lc_hash_init(struct lc_hash_ctx *ptr)
+{
+ if (ptr->stream) {
+ /* if we already have a context, just reset it, This is
+ * what the caller wanted, saving a destroy and create */
+ if (ptr->u.ctx_ptr == NULL) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ ptr->u.shake128_ctx = SHAKE_128_NewContext();
+ break;
+ case lc_shake256:
+ ptr->u.shake256_ctx = SHAKE_256_NewContext();
+ break;
+ }
+ }
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_Begin(ptr->u.shake128_ctx);
+ break;
+ case lc_shake256:
+ SHAKE_256_Begin(ptr->u.shake256_ctx);
+ break;
+ }
+ return;
+ }
+ /* we can be called with an active buffer, do and implicit reset here
+ * and free that buffer before we set up the next one */
+ if (ptr->buf && ptr->buf != ptr->buf_space) {
+ memset(ptr->buf, 0, ptr->current_input);
+ free(ptr->buf);
+ }
+ ptr->digestSize = 0;
+ ptr->current_input = 0;
+ ptr->current_output = 0;
+ ptr->buf_size = sizeof(ptr->buf_space);
+ ptr->buf = &ptr->buf_space[0];
+}
+
+static inline void
+lc_hash_update(struct lc_hash_ctx *ptr, const unsigned char *input, size_t inLen)
+{
+ if (inLen == 0) {
+ return;
+ } /* why were we even called with a NULL buffer? */
+ if (ptr->stream) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_Absorb(ptr->u.shake128_ctx, input, inLen);
+ break;
+ case lc_shake256:
+ SHAKE_256_Absorb(ptr->u.shake256_ctx, input, inLen);
+ break;
+ }
+ return;
+ }
+ if (ptr->current_input + inLen > ptr->buf_size) {
+ int len = ptr->current_input + inLen + 2048;
+ unsigned char *newBuf;
+ if (ptr->buf_size == sizeof(ptr->buf_space)) {
+ newBuf = calloc(1, len);
+ if (newBuf) {
+ memcpy(newBuf, ptr->buf, ptr->buf_size);
+ memset(ptr->buf_space, 0, sizeof(ptr->buf_space));
+ }
+ } else {
+ newBuf = reallocarray(ptr->buf, 1, len);
+ }
+ if (!newBuf) {
+ return;
+ }
+ ptr->buf = newBuf;
+ ptr->buf_size = len;
+ }
+ memcpy(ptr->buf + ptr->current_input, input, inLen);
+ ptr->current_input += inLen;
+}
+
+#define lc_hash_set_digestsize(ptr, len) ((ptr)->digestSize = (len))
+static inline void
+lc_hash_final(struct lc_hash_ctx *ptr, unsigned char *output)
+{
+ size_t outLen = ptr->digestSize;
+ if (ptr->stream) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_SqueezeEnd(ptr->u.shake128_ctx, output, outLen);
+ break;
+ case lc_shake256:
+ SHAKE_256_SqueezeEnd(ptr->u.shake256_ctx, output, outLen);
+ break;
+ }
+ return;
+ }
+ int len = ptr->current_output + outLen;
+ if (ptr->current_output == 0) {
+ lc_xof(ptr->hash, ptr->buf, ptr->current_input, output, outLen);
+ ptr->current_output += outLen;
+ return;
+ }
+ if (len > sizeof(ptr->buf2_space)) {
+ unsigned char *newBuf = calloc(1, len);
+ if (!newBuf) {
+ memset(output, 0, outLen);
+ return;
+ }
+ lc_xof(ptr->hash, ptr->buf, ptr->current_input, newBuf, len);
+ memcpy(output, newBuf + ptr->current_output, outLen);
+ memset(newBuf, 0, len);
+ free(newBuf);
+ ptr->current_output += outLen;
+ return;
+ }
+ lc_xof(ptr->hash, ptr->buf, ptr->current_input, ptr->buf2_space, len);
+ memcpy(output, &ptr->buf2_space[ptr->current_output], outLen);
+ memset(ptr->buf2_space, 0, len);
+ ptr->current_output += outLen;
+ return;
+}
+
+static inline void
+lc_hash_zero(struct lc_hash_ctx *ptr)
+{
+ if (ptr->stream) {
+ if (ptr->u.ctx_ptr != NULL) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_DestroyContext(ptr->u.shake128_ctx, PR_TRUE);
+ ptr->u.shake128_ctx = NULL;
+ break;
+ case lc_shake256:
+ SHAKE_128_DestroyContext(ptr->u.shake256_ctx, PR_TRUE);
+ ptr->u.shake256_ctx = NULL;
+ break;
+ }
+ }
+ return;
+ }
+ memset(ptr->buf2_space, 0, sizeof(ptr->buf2_space));
+ memset(ptr->buf_space, 0, sizeof(ptr->buf_space));
+ if (ptr->buf != ptr->buf_space) {
+ memset(ptr->buf, 0, ptr->buf_size);
+ free(ptr->buf);
+ ptr->buf = NULL;
+ }
+ lc_hash_init(ptr);
+}
+#endif
diff --git a/lib/freebl/leancrypto/new_headers/lc_sha512.h b/lib/freebl/leancrypto/new_headers/lc_sha512.h
new file mode 100644
diff --git a/lib/freebl/leancrypto/new_headers/ml_dsa_api.h b/lib/freebl/leancrypto/new_headers/ml_dsa_api.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/new_headers/ml_dsa_api.h
@@ -0,0 +1,71 @@
+#ifndef ML_DSA_API_H
+#define ML_DSA_API_H
+// This is a generated file from the various XXX_sign.h files
+#include <stddef.h>
+#include "ml_dsa_apit.h"
+
+// from ml_dsa_44_sign.h
+int lc_dilithium_44_keypair_from_seed_c(struct lc_dilithium_44_pk *pk,
+ struct lc_dilithium_44_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+int lc_dilithium_44_sign_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_sk *sk);
+int lc_dilithium_44_sign_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_44_sign_final_c(struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_44_verify_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_pk *pk);
+int lc_dilithium_44_verify_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_44_verify_final_c(const struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_pk *pk);
+
+// from ml_dsa_65_sign.h
+int lc_dilithium_65_keypair_from_seed_c(struct lc_dilithium_65_pk *pk,
+ struct lc_dilithium_65_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+int lc_dilithium_65_sign_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_sk *sk);
+int lc_dilithium_65_sign_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_65_sign_final_c(struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_65_verify_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_pk *pk);
+int lc_dilithium_65_verify_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_65_verify_final_c(const struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_pk *pk);
+// from ml_dsa_87_sign.h
+int lc_dilithium_87_keypair_from_seed_c(struct lc_dilithium_87_pk *pk,
+ struct lc_dilithium_87_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+int lc_dilithium_87_sign_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_sk *sk);
+int lc_dilithium_87_sign_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_87_sign_final_c(struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_87_verify_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_pk *pk);
+int lc_dilithium_87_verify_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_87_verify_final_c(const struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_pk *pk);
+#endif /* ML_DSA_API_H */
diff --git a/lib/freebl/leancrypto/new_headers/static_rng.h b/lib/freebl/leancrypto/new_headers/static_rng.h
new file mode 100644
diff --git a/lib/freebl/leancrypto/null_buffer.h b/lib/freebl/leancrypto/null_buffer.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/null_buffer.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef NULL_BUFFER_H
+#define NULL_BUFFER_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LC_NULL_BUFFER_SIZE 64
+extern const uint8_t null_buffer[];
+
+extern volatile int16_t optimization_blocker_int16;
+extern volatile int8_t optimization_blocker_int8;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NULL_BUFFER_H */
diff --git a/lib/freebl/leancrypto/randombytes.h b/lib/freebl/leancrypto/randombytes.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/randombytes.h
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: MIT
+// NSS stub for liboqs randombytes.h
+
+#ifndef RANDOMBYTES_H
+#define RANDOMBYTES_H
+
+// run the random number generator through our mldsa code so we can support
+// CKA_SEED (both acquiring it and generating keys from it) and
+// DETERMINISTIC signatures (by returning zeros from the RNG)
+void mldsa_GetRandomBytes(unsigned char *rdn, int bytes);
+#define randombytes mldsa_GetRandomBytes
+
+#endif
diff --git a/lib/freebl/leancrypto/ret_checkers.h b/lib/freebl/leancrypto/ret_checkers.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ret_checkers.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef RET_CHECKERS_H
+#define RET_CHECKERS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//#define ret_t int __attribute__((warn_unused_result))
+
+#ifdef LC_DEBUG
+#define CKERROR_LOG \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wpedantic\"") \
+ printf("Error %d at %s:%s:%u\n", ret, __FILE__, \
+ __FUNCTION__, __LINE__); \
+ _Pragma("GCC diagnostic pop")
+#else
+#define CKERROR_LOG
+#endif
+
+#define CKINT(x) \
+ { \
+ ret = x; \
+ if (ret < 0) { \
+ CKERROR_LOG \
+ goto out; \
+ } \
+ }
+
+#define CKINT_LOG(x, ...) \
+ { \
+ ret = x; \
+ if (ret < 0) { \
+ CKERROR_LOG \
+ printf(__VA_ARGS__); \
+ goto out; \
+ } \
+ }
+
+#define CKNULL(v, r) \
+ { \
+ if (!v) { \
+ ret = r; \
+ CKERROR_LOG \
+ goto out; \
+ } \
+ }
+
+#define CKNULL_LOG(v, r, ...) \
+ { \
+ if (!v) { \
+ printf(__VA_ARGS__); \
+ ret = r; \
+ CKERROR_LOG \
+ goto out; \
+ } \
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RET_CHECKERS_H */
diff --git a/lib/freebl/leancrypto/rotate.h b/lib/freebl/leancrypto/rotate.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/rotate.h
@@ -0,0 +1,98 @@
+/* Rotate left / right functions
+ *
+ * Copyright (C) 2015 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef ROTATE_H
+#define ROTATE_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LINUX_KERNEL
+
+/*
+ * Rotate 8 bit unsigned integer X by N bits left/right
+ */
+static inline uint8_t
+rol8(uint16_t x, uint8_t n)
+{
+ return (uint8_t)((x << (n & (8 - 1))) | (x >> ((8 - n) & (8 - 1))));
+}
+
+static inline uint8_t
+ror8(uint16_t x, uint8_t n)
+{
+ return (uint8_t)((x >> (n & (8 - 1))) | (x << ((8 - n) & (8 - 1))));
+}
+
+/*
+ * Rotate 16 bit unsigned integer X by N bits left/right
+ */
+static inline uint16_t
+rol16(uint16_t x, uint8_t n)
+{
+ return (uint16_t)((x << (n & (16 - 1))) | (x >> ((16 - n) & (16 - 1))));
+}
+
+static inline uint16_t
+ror16(uint16_t x, uint8_t n)
+{
+ return (uint16_t)((x >> (n & (16 - 1))) | (x << ((16 - n) & (16 - 1))));
+}
+
+/*
+ * Rotate 32 bit unsigned integer X by N bits left/right
+ */
+static inline uint32_t
+rol32(uint32_t x, uint8_t n)
+{
+ return ((x << (n & (32 - 1))) | (x >> ((32 - n) & (32 - 1))));
+}
+
+static inline uint32_t
+ror32(uint32_t x, uint8_t n)
+{
+ return ((x >> (n & (32 - 1))) | (x << ((32 - n) & (32 - 1))));
+}
+
+/*
+ * Rotate 64 bit unsigned integer X by N bits left/right
+ */
+static inline uint64_t
+rol64(uint64_t x, uint8_t n)
+{
+ return ((x << (n & (64 - 1))) | (x >> ((64 - n) & (64 - 1))));
+}
+
+static inline uint64_t
+ror64(uint64_t x, uint8_t n)
+{
+ return ((x >> (n & (64 - 1))) | (x << ((64 - n) & (64 - 1))));
+}
+
+#endif /* LINUX_KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ROTATE_H */
diff --git a/lib/freebl/leancrypto/sidechannel_resistantce.h b/lib/freebl/leancrypto/sidechannel_resistantce.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/sidechannel_resistantce.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/kyber
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/).
+ */
+
+#ifndef SIDECHANNEL_RESISTANCE_H
+#define SIDECHANNEL_RESISTANCE_H
+
+#include "ext_headers.h"
+#include "null_buffer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief cmov - Copy len bytes from x to r if b is 1;
+ * don't modify x if b is 0. Requires b to be in {0,1};
+ * assumes two's complement representation of negative integers.
+ * Runs in constant time.
+ *
+ * @param [out] r pointer to output byte array
+ * @param [in] x pointer to input byte array
+ * @param [in] len Amount of bytes to be copied
+ * @param [in] b Condition bit; has to be in {0,1}
+ */
+static inline void
+cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b)
+{
+ size_t i;
+ uint8_t opt_blocker;
+
+ /*
+ * Goal: copy data only depending on a given condition without
+ * the use of a branching operation which alters the timing behavior
+ * depending on the condition. As the condition here depends on
+ * secret data, the code has to ensure that no branching is used to have
+ * time-invariant code. This solution below also shall ensure that the
+ * compiler cannot optimize this code such that it brings back the
+ * branching.
+ *
+ * (condition ^ opt_blocker) can be any value at run-time to the
+ * compiler, making it impossible to skip the computation (except the
+ * compiler would care to create a branch for opt_blocker to be either
+ * 0 or 1, which would be extremely unlikely). Yet the volatile
+ * variable has to be loaded only once at the beginning of the function
+ * call.
+ *
+ * Note, the opt_blocker is not required in most instances, but in the
+ * ARMv8 Neon implementation of SLH-DSA the compiler managed to still
+ * create time-variant code without the optimization blocker.
+ */
+ opt_blocker = (uint8_t)optimization_blocker_int8;
+
+ b = -b;
+ for (i = 0; i < len; i++)
+ r[i] ^= (b & (r[i] ^ x[i])) ^ opt_blocker;
+}
+
+/**
+ * @brief cmov_int16 - Copy input v to *r if b is 1, don't modify *r if b is 0.
+ * Requires b to be in {0,1}; Runs in constant time.
+ *
+ * @param [out] r pointer to output int16_t
+ * @param [in] v input int16_t
+ * @param [in] b Condition bit; has to be in {0,1}
+ */
+static inline void
+cmov_int16(int16_t *r, int16_t v, uint16_t b)
+{
+ b = -b;
+ *r ^= (int16_t)(b & ((*r) ^ v));
+}
+
+/**
+ * @brief cmov_uint32 - Copy input v to *r if b is 1, don't modify *r if b is 0.
+ * Requires b to be in {0,1}; Runs in constant time.
+ *
+ * @param [out] r pointer to output int16_t
+ * @param [in] v input int16_t
+ * @param [in] b Condition bit; has to be in {0,1}
+ */
+static inline void
+cmov_uint32(uint32_t *r, uint32_t v, uint32_t b)
+{
+ b = -b;
+ *r ^= (uint32_t)(b & ((*r) ^ v));
+}
+
+/**
+ * @brief cmov_int - Copy input v to *r if b is 1, don't modify *r if b is 0.
+ * Requires b to be in {0,1}; Runs in constant time.
+ *
+ * @param [out] r pointer to output int16_t
+ * @param [in] v input int16_t
+ * @param [in] b Condition bit; has to be in {0,1}
+ */
+static inline void
+cmov_int(int *r, int v, uint16_t b)
+{
+ b = -b;
+ *r ^= (int)(b & ((*r) ^ v));
+}
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SIDECHANNEL_RESISTANCE_H */
diff --git a/lib/freebl/leancrypto/signature_domain_separation.h b/lib/freebl/leancrypto/signature_domain_separation.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/signature_domain_separation.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef SIGNATURE_DOMAIN_SEPARATION_H
+#define SIGNATURE_DOMAIN_SEPARATION_H
+
+#include "dilithium_type.h"
+#include "helper.h"
+#include "lc_hash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int signature_domain_separation(struct lc_hash_ctx *hash_ctx,
+ unsigned int ml_dsa_internal,
+ const struct lc_hash *signature_prehash_type,
+ const uint8_t *userctx, size_t userctxlen,
+ const uint8_t *m, size_t mlen,
+ const uint8_t *randomizer, size_t randomizerlen,
+ unsigned int nist_category);
+int signature_ph_oids(struct lc_hash_ctx *hash_ctx,
+ const struct lc_hash *signature_prehash_type, size_t mlen,
+ unsigned int nist_category);
+int composite_signature_domain_separation(struct lc_hash_ctx *hash_ctx,
+ const uint8_t *userctx,
+ size_t userctxlen,
+ const uint8_t *randomizer,
+ size_t randomizerlen,
+ unsigned int nist_category);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SIGNATURE_DOMAIN_SEPARATION_H */
diff --git a/lib/freebl/leancrypto/signature_domain_separation.c b/lib/freebl/leancrypto/signature_domain_separation.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/signature_domain_separation.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "signature_domain_separation.h"
+#include "lc_sha256.h"
+#include "lc_sha3.h"
+#include "lc_sha512.h"
+#include "ret_checkers.h"
+
+/* RFC4055 2.16.840.1.101.3.4.2.1 */
+static const uint8_t sha256_oid_der[] __maybe_unused = { 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x02, 0x01 };
+/* RFC4055 2.16.840.1.101.3.4.2.2 */
+static const uint8_t sha384_oid_der[] __maybe_unused = { 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x02, 0x02 };
+/* RFC4055 2.16.840.1.101.3.4.2.3 */
+static const uint8_t sha512_oid_der[] __maybe_unused = { 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x02, 0x03 };
+
+/*
+ * https://lamps-wg.github.io/draft-composite-sigs/draft-ietf-lamps-pq-composite-sigs.html
+ */
+static const uint8_t sha3_256_oid_der[] __maybe_unused = {
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08
+};
+static const uint8_t sha3_384_oid_der[] __maybe_unused = {
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x09
+};
+static const uint8_t sha3_512_oid_der[] __maybe_unused = {
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0a
+};
+
+/* RFC8692 2.16.840.1.101.3.4.2.11 */
+static const uint8_t shake128_oid_der[] __maybe_unused = {
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0B
+};
+
+/* RFC8692 2.16.840.1.101.3.4.2.11 */
+static const uint8_t shake256_oid_der[] __maybe_unused = {
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0C
+};
+
+/* OIDs from https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html */
+const uint8_t lc_x509_composite_sig_prefix[] = {
+ 0x43, 0x6F, 0x6D, 0x70, 0x6F, 0x73, 0x69, 0x74, 0x65, 0x41, 0x6C,
+ 0x67, 0x6F, 0x72, 0x69, 0x74, 0x68, 0x6D, 0x53, 0x69, 0x67, 0x6E,
+ 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x32, 0x30, 0x32, 0x35
+};
+
+static const uint8_t lc_x509_test_dom_sep[] = { 0x06, 0x0b, 0x60, 0x86, 0x48,
+ 0x01, 0x86, 0xfa, 0x6b, 0x50,
+ 0x09, 0x01, 0x08 };
+
+/* id-HashMLDSA44-Ed25519-SHA512 */
+static const uint8_t lc_x509_mldsa44_ed25519_sha512_dom_sep[] = {
+ 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x02
+};
+
+/* id-HashMLDSA65-Ed25519-SHA512 */
+static const uint8_t lc_x509_mldsa65_ed25519_sha512_dom_sep[] = {
+ 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x0B
+};
+
+/* id-HashMLDSA87-Ed448-SHAKE256 */
+static const uint8_t lc_x509_mldsa87_ed448_sha512_dom_sep[] = {
+ 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x0E
+};
+
+int
+signature_ph_oids(struct lc_hash_ctx *hash_ctx,
+ const struct lc_hash *signature_prehash_type, size_t mlen,
+ unsigned int nist_category)
+{
+ /* If no hash is supplied, we have no HashML-DSA */
+ if (!signature_prehash_type)
+ return 0;
+
+ /*
+ * The signature init/update/final operation will not work with the
+ * check of mlen, as only when _final is invoked, the message length
+ * is known.
+ *
+ * As defined in FIPS 204, section 5.4 requires
+ * "... the digest that is signed needs to be generated using an
+ * approved hash function or XOF (e.g., from FIPS 180 or FIPS 202) that
+ * provides at least λ bits of classical security strength against both
+ * collision and second preimage attacks ... Obtaining at least λ bits
+ * of classical security strength against collision attacks requires
+ * that the digest to be signed be at least 2λ bits in length."
+ * This requirement implies in the following definitions.
+ */
+ (void)mlen;
+
+ switch (nist_category) {
+ case 1:
+#ifdef LC_SHA2_256
+ if (signature_prehash_type == lc_sha256) {
+ // if (mlen != LC_SHA256_SIZE_DIGEST)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, sha256_oid_der,
+ sizeof(sha256_oid_der));
+ return 0;
+ }
+#endif
+#ifdef LC_SHA3
+ if (signature_prehash_type == lc_sha3_256) {
+ // if (mlen != LC_SHA3_256_SIZE_DIGEST)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, sha3_256_oid_der,
+ sizeof(sha3_256_oid_der));
+ return 0;
+ }
+ if (signature_prehash_type == lc_shake128) {
+ /* FIPS 204 section 5.4.1 */
+ // if (mlen != 32)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, shake128_oid_der,
+ sizeof(shake128_oid_der));
+ return 0;
+ }
+#endif
+ /* FALLTHROUGH - Dilithium44 allows the following, too */
+ fallthrough;
+ case 3:
+#ifdef LC_SHA3
+ if (signature_prehash_type == lc_sha3_384) {
+ // if (mlen != LC_SHA3_384_SIZE_DIGEST)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, sha3_384_oid_der,
+ sizeof(sha3_384_oid_der));
+ return 0;
+ }
+#endif
+#ifdef LC_SHA2_512
+ if (signature_prehash_type == lc_sha384) {
+ // if (mlen != LC_SHA384_SIZE_DIGEST)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, sha384_oid_der,
+ sizeof(sha384_oid_der));
+ return 0;
+ }
+#endif
+ /* FALLTHROUGH - Dilithium[44|65] allows the following, too */
+ fallthrough;
+ case 5:
+#ifdef LC_SHA2_512
+ if (signature_prehash_type == lc_sha512) {
+ // if (mlen != LC_SHA512_SIZE_DIGEST)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, sha512_oid_der,
+ sizeof(sha512_oid_der));
+ return 0;
+ }
+#endif
+#ifdef LC_SHA3
+ if (signature_prehash_type == lc_sha3_512) {
+ // if (mlen != LC_SHA3_512_SIZE_DIGEST)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, sha3_512_oid_der,
+ sizeof(sha3_512_oid_der));
+ return 0;
+ } else if (signature_prehash_type == lc_shake256) {
+ /* FIPS 204 section 5.4.1 */
+ /*
+ * TODO: mlen must be >= 64 to comply with the
+ * aforementioned requirement - unfortunately we can
+ * only check mlen at the end of the signature
+ * operation - shall this be implemented?
+ */
+ // if (mlen != 64)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, shake256_oid_der,
+ sizeof(shake256_oid_der));
+ return 0;
+ }
+#endif
+ break;
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int
+composite_signature_set_domain(const uint8_t **domain,
+ size_t *domainlen,
+ unsigned int nist_category)
+{
+ /* Set Domain */
+ switch (nist_category) {
+ case 0:
+ *domain = lc_x509_test_dom_sep;
+ *domainlen = sizeof(lc_x509_test_dom_sep);
+ break;
+ case 1:
+ *domain = lc_x509_mldsa44_ed25519_sha512_dom_sep;
+ *domainlen = sizeof(lc_x509_mldsa44_ed25519_sha512_dom_sep);
+ break;
+ case 3:
+ *domain = lc_x509_mldsa65_ed25519_sha512_dom_sep;
+ *domainlen = sizeof(lc_x509_mldsa65_ed25519_sha512_dom_sep);
+ break;
+ case 5:
+ *domain = lc_x509_mldsa87_ed448_sha512_dom_sep;
+ *domainlen = sizeof(lc_x509_mldsa87_ed448_sha512_dom_sep);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+int
+composite_signature_domain_separation(struct lc_hash_ctx *hash_ctx,
+ const uint8_t *userctx,
+ size_t userctxlen,
+ const uint8_t *randomizer,
+ size_t randomizerlen,
+ unsigned int nist_category)
+{
+ const uint8_t *domain;
+ size_t domainlen;
+ uint8_t userctxlen_small = (uint8_t)userctxlen;
+ int ret;
+
+ CKINT(composite_signature_set_domain(&domain, &domainlen,
+ nist_category));
+
+ /*
+ * M' = Prefix || Domain || len(ctx) || ctx || r
+ *
+ * See for details: https://lamps-wg.github.io/draft-composite-sigs/draft-ietf-lamps-pq-composite-sigs.html
+ */
+ lc_hash_update(hash_ctx, lc_x509_composite_sig_prefix,
+ sizeof(lc_x509_composite_sig_prefix));
+ lc_hash_update(hash_ctx, domain, domainlen);
+ lc_hash_update(hash_ctx, &userctxlen_small, sizeof(userctxlen_small));
+ lc_hash_update(hash_ctx, userctx, userctxlen);
+ lc_hash_update(hash_ctx, randomizer, randomizerlen);
+
+out:
+ return ret;
+}
+
+static int
+standalone_signature_domain_separation(
+ struct lc_hash_ctx *hash_ctx,
+ const struct lc_hash *signature_prehash_type, const uint8_t *userctx,
+ size_t userctxlen, size_t mlen, unsigned int nist_category)
+{
+ int ret;
+ uint8_t domainseparation[2];
+
+ domainseparation[0] = signature_prehash_type ? 1 : 0;
+ domainseparation[1] = (uint8_t)userctxlen;
+
+ lc_hash_update(hash_ctx, domainseparation, sizeof(domainseparation));
+ lc_hash_update(hash_ctx, userctx, userctxlen);
+
+ CKINT(signature_ph_oids(hash_ctx, signature_prehash_type, mlen,
+ nist_category));
+
+out:
+ return ret;
+}
+
+int
+signature_domain_separation(struct lc_hash_ctx *hash_ctx,
+ unsigned int ml_dsa_internal,
+ const struct lc_hash *signature_prehash_type,
+ const uint8_t *userctx, size_t userctxlen,
+ const uint8_t *m, size_t mlen,
+ const uint8_t *randomizer, size_t randomizerlen,
+ unsigned int nist_category)
+{
+ int ret = 0;
+
+ /* The internal operation skips the domain separation code */
+ if (ml_dsa_internal)
+ goto out;
+
+ if (userctxlen > 255)
+ return -EINVAL;
+
+ /* If Composite ML-DSA is requested, use domain as userctx */
+ if (randomizer) {
+ const uint8_t *domain;
+ size_t domainlen;
+
+ CKINT(composite_signature_set_domain(&domain, &domainlen,
+ nist_category));
+
+ /* Add the composite signature domain as context */
+ CKINT(standalone_signature_domain_separation(
+ hash_ctx, signature_prehash_type, domain, domainlen,
+ mlen, nist_category));
+
+ CKINT(composite_signature_domain_separation(
+ hash_ctx, userctx, userctxlen, randomizer,
+ randomizerlen, nist_category));
+ } else {
+ CKINT(standalone_signature_domain_separation(
+ hash_ctx, signature_prehash_type, userctx, userctxlen,
+ mlen, nist_category));
+ }
+
+out:
+ lc_hash_update(hash_ctx, m, mlen);
+ return ret;
+}
diff --git a/lib/freebl/leancrypto/small_stack_support.h b/lib/freebl/leancrypto/small_stack_support.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/small_stack_support.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef SMALL_STACK_SUPPORT_H
+#define SMALL_STACK_SUPPORT_H
+
+#include "ext_headers.h"
+#include "lc_memory_support.h"
+#include "lc_memset_secure.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WIN32
+#define LC_ALIGNED_BUFFER_ALIGNMENTSIZE(name, size, alignment) \
+ uint64_t name[(size + sizeof(uint64_t) - 1) / sizeof(uint64_t)]
+#else
+#define LC_ALIGNED_BUFFER_ALIGNMENTSIZE(name, size, alignment) \
+ uint64_t name[(size + sizeof(uint64_t) - 1) / sizeof(uint64_t)] \
+ __attribute__((aligned(alignment)))
+#endif
+
+/* Allocate memory on stack */
+#define __LC_DECLARE_MEM_STACK(name, type, alignment) \
+ LC_ALIGNED_BUFFER_ALIGNMENTSIZE(name##_buf, sizeof(type), alignment); \
+ lc_memset_secure(name##_buf, 0, sizeof(type)); \
+ type *name = (type *)name##_buf
+#define __LC_RELEASE_MEM_STACK(name) lc_memset_secure(name, 0, sizeof(*name))
+
+/* Allocate memory on heap */
+#define __LC_DECLARE_MEM_HEAP(name, type, alignment) \
+ type *name = NULL; \
+ int __ret = \
+ lc_alloc_high_aligned((void *)&name, alignment, sizeof(type)); \
+ if (__ret || !name) \
+ return __ret; \
+ lc_memset_secure(name, 0, sizeof(type))
+
+#define __LC_RELEASE_MEM_HEAP(name) \
+ lc_memset_secure(name, 0, sizeof(*name)); \
+ lc_free_high_aligned(name, sizeof(*name))
+
+/* Define macro LC_MEM_ON_HEAP if stack is less than 256KiB in size */
+#ifdef LC_MEM_ON_HEAP
+
+#define noinline_stack noinline
+
+#define LC_DECLARE_MEM(name, type, alignment) \
+ __LC_DECLARE_MEM_HEAP(name, type, alignment)
+#define LC_RELEASE_MEM(name) __LC_RELEASE_MEM_HEAP(name)
+
+#else
+
+#define noinline_stack
+
+#define LC_DECLARE_MEM(name, type, alignment) \
+ __LC_DECLARE_MEM_STACK(name, type, alignment)
+#define LC_RELEASE_MEM(name) __LC_RELEASE_MEM_STACK(name)
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SMALL_STACK_SUPPORT_H */
diff --git a/lib/freebl/leancrypto/static_rng.h b/lib/freebl/leancrypto/static_rng.h
new file mode 100644
diff --git a/lib/freebl/leancrypto/timecop.h b/lib/freebl/leancrypto/timecop.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/timecop.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived from
+ * https://www.post-apocalyptic-crypto.org/timecop/#source-code
+ *
+ * The license is: this code is released into the public domain
+ */
+
+#ifndef TIMECOP_H
+#define TIMECOP_H
+
+#if defined __has_include
+#if __has_include(<valgrind/memcheck.h>)
+#define LC_HAS_TIMECOP
+#endif
+#endif
+
+#if defined(LC_USE_TIMECOP) && !defined(LC_HAS_TIMECOP)
+#error "Compilation with TIMECOP requested, but valgrind's memcheck.h missing."
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LC_USE_TIMECOP
+
+/*
+ * This code requires header files from valgrind. They can be installed with
+ * packages like valgrind-client-headers.
+ */
+#include <valgrind/memcheck.h>
+
+/**
+ * Poisons a memory region of len bytes, starting at addr, indicating that
+ * execution time must not depend on the content of this memory region.
+ * Use this function to mark any memory regions containing secret data.
+ */
+#define poison(addr, len) VALGRIND_MAKE_MEM_UNDEFINED(addr, len)
+
+/**
+ * Use this function to indicate that the specified memory region does no longer
+ * contain data that must not affect execution time.
+ */
+#define unpoison(addr, len) VALGRIND_MAKE_MEM_DEFINED(addr, len)
+
+/**
+ * Checks whether the memory region of len bytes, starting at addr,
+ * contains any poisoned bits.
+ * Returns 0 if the code is running natively, rather than within valgrind.
+ * If valgrind is running, it returns the first address containing poisoned
+ * data, or 0 if there is no poisoned data in the specified memory region.
+ * You can use RUNNING_ON_VALGRIND from valgrind.h to check whether the code
+ * is being executed within valgrind.
+ */
+#define is_poisoned(addr, len) VALGRIND_CHECK_MEM_IS_DEFINED(addr, len)
+
+#else /* LC_USE_TIMECOP */
+
+#define poison(addr, len)
+#define unpoison(addr, len)
+#define is_poisoned(addr, len)
+
+#endif /* LC_USE_TIMECOP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TIMECOP_H */
diff --git a/lib/freebl/leancrypto/visibility.h b/lib/freebl/leancrypto/visibility.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/visibility.h
@@ -0,0 +1,128 @@
+/*
+ * see https://gcc.gnu.org/wiki/Visibility
+ *
+ * use -fvisibility=hidden to mark all symbols hidden per default
+ *
+ * It is sufficient to use the macros in the declarations only. The
+ * definitions do not need to be instrumented.
+ */
+
+#ifndef VISIBILITY_H
+#define VISIBILITY_H
+
+#ifdef LINUX_KERNEL
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wvariadic-macros"
+#define LC_INTERFACE_FUNCTION(ret, symbol, param...) \
+ ret symbol(param); \
+ EXPORT_SYMBOL(symbol); \
+ ret symbol(param)
+
+#define LC_INIT_FUNCTION(ret, symbol, param...) \
+ ret __init symbol(param); \
+ ret __init symbol(param)
+
+#define LC_TEST_FUNC(ret, symbol, param...) \
+ static ret symbol(param); \
+ static int __init symbol##_init(void) \
+ { \
+ int __ret; \
+ \
+ pr_info("%s: Starting test case\n", KBUILD_MODNAME); \
+ __ret = symbol(0, NULL); \
+ pr_info("%s: Test case completed with return code %d\n", \
+ KBUILD_MODNAME, __ret); \
+ return __ret ? -EFAULT : 0; \
+ } \
+ static void __exit symbol##_exit(void) \
+ { \
+ } \
+ module_init(symbol##_init); \
+ module_exit(symbol##_exit); \
+ MODULE_LICENSE("Dual BSD/GPL"); \
+ MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>"); \
+ MODULE_DESCRIPTION("leancrypto test case"); \
+ static ret symbol(param)
+
+#pragma GCC diagnostic pop
+
+#define LC_INTERFACE_SYMBOL(ret, symbol) \
+ ret symbol; \
+ EXPORT_SYMBOL(symbol); \
+ ret symbol
+
+#define LC_CONSTRUCTOR(_func) \
+ void __init _func(void); \
+ void __init _func(void)
+
+#else /* LINUX_KERNEL */
+
+#ifdef _WIN32
+#define DSO_PUBLIC
+#define DSO_LOCAL
+#else
+#define DSO_PUBLIC __attribute__((visibility("default")))
+#define DSO_LOCAL __attribute__((visibility("hidden")))
+#endif
+
+#ifndef _WIN32
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wvariadic-macros"
+#endif
+
+#define LC_INTERFACE_FUNCTION_1(ret, symbol, param1) \
+ DSO_PUBLIC ret symbol(param1)
+#define LC_INTERFACE_FUNCTION_2(ret, symbol, param1, param2) \
+ DSO_PUBLIC ret symbol(param1, param2)
+#define LC_INTERFACE_FUNCTION_3(ret, symbol, param1, param2, param3) \
+ DSO_PUBLIC ret symbol(param1, param2, param3)
+#define LC_INTERFACE_FUNCTION_4(ret, symbol, param1, param2, param3, param4) \
+ DSO_PUBLIC ret symbol(param1, param2, param3, param4)
+#define LC_INTERFACE_FUNCTION_5(ret, symbol, param1, param2, param3, param4, param5) \
+ DSO_PUBLIC ret symbol(param1, param2, param3, param4, param5)
+#define LC_INTERFACE_FUNCTION_6(ret, symbol, param1, param2, param3, param4, param5, param6) \
+ DSO_PUBLIC ret symbol(param1, param2, param3, param4, param5, param6)
+
+#ifdef notdef
+#define LC_INIT_FUNCTION(ret, symbol, param...) DSO_PUBLIC ret symbol(param)*
+
+#ifdef LC_STATIC
+#define LC_TEST_FUNC(ret, symbol, param...) \
+ int lc_init(unsigned int flags); \
+ static ret __##symbol(param); \
+ ret symbol(param) \
+ { \
+ lc_init(0); \
+ return __##symbol(argc, argv); \
+ } \
+ static ret __##symbol(param)
+#else
+#define LC_TEST_FUNC(ret, symbol, param...) ret symbol(param)
+#endif
+#endif
+
+#ifndef _WIN32
+#pragma GCC diagnostic pop
+#endif
+
+#define LC_INTERFACE_SYMBOL(ret, symbol) DSO_PUBLIC ret symbol
+
+#ifdef LC_EFI
+/*
+ * EFI does not have the constructor logic. Thus, mark the constructor functions
+ * as regular non-static functions as they are intended to be called by lc_init.
+ */
+#define LC_CONSTRUCTOR(_func) \
+ void _func(void); \
+ void _func(void)
+#else /* LC_EFI */
+
+#define LC_CONSTRUCTOR(_func) \
+ void __attribute__((constructor)) _func(void); \
+ void _func(void)
+#endif /* LC_EFI */
+
+#endif /* LINUX_KERNEL */
+
+#endif /* VISIBILITY_H */
diff --git a/lib/freebl/leancrypto/xor.h b/lib/freebl/leancrypto/xor.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/xor.h
@@ -0,0 +1,199 @@
+/* Efficient XOR implementation
+ *
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef XOR_H
+#define XOR_H
+
+#include "alignment.h"
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline void
+xor_8(uint8_t *dst, const uint8_t *src, size_t size)
+{
+ for (; size; size--)
+ *dst++ ^= *src++;
+}
+
+static inline void
+xor_32_aligned(uint8_t *dst, const uint8_t *src, size_t size)
+{
+ /*
+ * We can ignore the alignment warning as we checked
+ * for proper alignment.
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ uint32_t *dst_word = (uint32_t *)dst;
+ uint32_t *src_word = (uint32_t *)src;
+#pragma GCC diagnostic pop
+
+ for (; size >= sizeof(*src_word); size -= sizeof(*src_word))
+ *dst_word++ ^= *src_word++;
+
+ xor_8((uint8_t *)dst_word, (uint8_t *)src_word, size);
+}
+
+static inline void
+xor_32(uint8_t *dst, const uint8_t *src, size_t size)
+{
+ if (aligned(src, sizeof(uint32_t) - 1) &&
+ aligned(dst, sizeof(uint32_t) - 1))
+ xor_32_aligned(dst, src, size);
+ else
+ xor_8(dst, src, size);
+}
+
+static inline void
+xor_64_aligned(uint8_t *dst, const uint8_t *src, size_t size)
+{
+ /*
+ * We can ignore the alignment warning as we checked
+ * for proper alignment.
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ uint64_t *dst_dword = (uint64_t *)dst;
+ uint64_t *src_dword = (uint64_t *)src;
+#pragma GCC diagnostic pop
+
+#ifdef __LP64__
+ for (; size >= sizeof(*src_dword); size -= sizeof(*src_dword))
+ *dst_dword++ ^= *src_dword++;
+#endif /* __LP64__ */
+
+ xor_32_aligned((uint8_t *)dst_dword, (uint8_t *)src_dword, size);
+}
+
+/**
+ * @brief Perform XOR operation efficiently
+ *
+ * @param [in,out] dst Data in which the source data is XORed into
+ * @param [in] src Source data which is XORed into the destination
+ * @param [in] size Buffer lengths of both, dst and src
+ */
+static inline void
+xor_64(uint8_t *dst, const uint8_t *src, size_t size)
+{
+#ifdef __LP64__
+ if (aligned(src, sizeof(uint64_t) - 1) &&
+ aligned(dst, sizeof(uint64_t) - 1))
+ xor_64_aligned(dst, src, size);
+ else
+#endif
+ xor_32(dst, src, size);
+}
+
+static inline void
+xor_8_3(uint8_t *dst, const uint8_t *src1,
+ const uint8_t *src2, size_t size)
+{
+ for (; size; size--)
+ *dst++ = *src1++ ^ *src2++;
+}
+
+static inline void
+xor_32_3_aligned(uint8_t *dst, const uint8_t *src1,
+ const uint8_t *src2, size_t size)
+{
+ /*
+ * We can ignore the alignment warning as we checked
+ * for proper alignment.
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ uint32_t *dst_word = (uint32_t *)dst;
+ uint32_t *src1_word = (uint32_t *)src1;
+ uint32_t *src2_word = (uint32_t *)src2;
+#pragma GCC diagnostic pop
+
+ for (; size >= sizeof(*src1_word); size -= sizeof(*src1_word))
+ *dst_word++ = *src1_word++ ^ *src2_word++;
+
+ xor_8_3((uint8_t *)dst_word, (uint8_t *)src1_word, (uint8_t *)src2_word,
+ size);
+}
+
+static inline void
+xor_32_3(uint8_t *dst, const uint8_t *src1,
+ const uint8_t *src2, size_t size)
+{
+ if (aligned(src1, sizeof(uint32_t) - 1) &&
+ aligned(src2, sizeof(uint32_t) - 1) &&
+ aligned(dst, sizeof(uint32_t) - 1))
+ xor_32_3_aligned(dst, src1, src2, size);
+ else
+ xor_8_3(dst, src1, src2, size);
+}
+
+#ifdef __LP64__
+static inline void
+xor_64_3_aligned(uint8_t *dst, const uint8_t *src1,
+ const uint8_t *src2, size_t size)
+{
+ /*
+ * We can ignore the alignment warning as we checked
+ * for proper alignment.
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ uint64_t *dst_dword = (uint64_t *)dst;
+ uint64_t *src1_dword = (uint64_t *)src1;
+ uint64_t *src2_dword = (uint64_t *)src2;
+#pragma GCC diagnostic pop
+
+ for (; size >= sizeof(*src1_dword); size -= sizeof(*src1_dword))
+ *dst_dword++ = *src1_dword++ ^ *src2_dword++;
+
+ xor_32_3_aligned((uint8_t *)dst_dword, (uint8_t *)src1_dword,
+ (uint8_t *)src2_dword, size);
+}
+#endif
+
+/**
+ * @brief Perform XOR operation efficiently
+ *
+ * @param [out] dst Data in which the source data is XORed into
+ * @param [in] src1 1st source data which is XORed into the destination
+ * @param [in] src2 2nd source data which is XORed into the destination
+ * @param [in] size Buffer lengths all buffers dst, src1, and src2
+ */
+static inline void
+xor_64_3(uint8_t *dst, const uint8_t *src1,
+ const uint8_t *src2, size_t size)
+{
+#ifdef __LP64__
+ if (aligned(src1, sizeof(uint64_t) - 1) &&
+ aligned(src2, sizeof(uint64_t) - 1) &&
+ aligned(dst, sizeof(uint64_t) - 1))
+ xor_64_3_aligned(dst, src1, src2, size);
+ else
+#endif
+ xor_32_3(dst, src1, src2, size);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* XOR_H */
diff --git a/lib/freebl/leancrypto/xor256.h b/lib/freebl/leancrypto/xor256.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/xor256.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef XOR256_H
+#define XOR256_H
+
+#include "build_bug_on.h"
+#include "cpufeatures.h"
+#include "xor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LC_HOST_X86_64
+
+/*
+ * AVX2 implementation of XOR (processing 256 bit chunks)
+ */
+#include "ext_headers_x86.h"
+static inline void
+xor_256_aligned(uint8_t *dst, const uint8_t *src,
+ size_t size)
+{
+ __m256i dst_256, src_256;
+
+ LC_FPU_ENABLE;
+ for (; size >= sizeof(src_256); size -= sizeof(src_256),
+ dst += sizeof(dst_256),
+ src += sizeof(src_256)) {
+ __m128d t;
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+ dst_256 = _mm256_set_epi8(dst[31], dst[30], dst[29], dst[28],
+ dst[27], dst[26], dst[25], dst[24],
+ dst[23], dst[22], dst[21], dst[20],
+ dst[19], dst[18], dst[17], dst[16],
+ dst[15], dst[14], dst[13], dst[12],
+ dst[11], dst[10], dst[9], dst[8],
+ dst[7], dst[6], dst[5], dst[4],
+ dst[3], dst[2], dst[1], dst[0]);
+ src_256 = _mm256_set_epi8(src[31], src[30], src[29], src[28],
+ src[27], src[26], src[25], src[24],
+ src[23], src[22], src[21], src[20],
+ src[19], src[18], src[17], src[16],
+ src[15], src[14], src[13], src[12],
+ src[11], src[10], src[9], src[8],
+ src[7], src[6], src[5], src[4],
+ src[3], src[2], src[1], src[0]);
+#pragma GCC diagnostic pop
+
+ dst_256 = _mm256_xor_si256(dst_256, src_256);
+
+ /*
+ * We can ignore the alignment warning as we checked
+ * for proper alignment.
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ t = _mm_castsi128_pd(_mm256_castsi256_si128(dst_256));
+ _mm_storel_pd((__attribute__((__may_alias__)) double *)&dst[0],
+ t);
+ _mm_storeh_pd((__attribute__((__may_alias__)) double *)&dst[8],
+ t);
+ t = _mm_castsi128_pd(_mm256_extracti128_si256(dst_256, 1));
+ _mm_storel_pd((__attribute__((__may_alias__)) double *)&dst[16],
+ t);
+ _mm_storeh_pd((__attribute__((__may_alias__)) double *)&dst[24],
+ t);
+#pragma GCC diagnostic pop
+ }
+ LC_FPU_DISABLE;
+
+ /*
+ * As we skip the xor_64 alignment check, guarantee it at compile time.
+ */
+ BUILD_BUG_ON(LC_XOR_AVX2_ALIGNMENT < sizeof(uint64_t));
+ xor_64_aligned(dst, src, size);
+}
+
+static inline void
+xor_256(uint8_t *dst, const uint8_t *src, size_t size)
+{
+ enum lc_cpu_features feat =
+ lc_cpu_feature_available() & LC_CPU_FEATURE_INTEL_AVX2;
+
+ if (!aligned(src, LC_XOR_AVX2_ALIGNMENT - 1) ||
+ !aligned(dst, LC_XOR_AVX2_ALIGNMENT - 1) || !feat) {
+ xor_64(dst, src, size);
+ } else {
+ xor_256_aligned(dst, src, size);
+ }
+}
+
+#elif (defined(LC_HOST_ARM32_NEON) || defined(LC_HOST_AARCH64)) && \
+ !defined(LINUX_KERNEL)
+
+/*
+ * ARM Neon implementation of XOR (processing 128 bit chunks)
+ */
+/* This code cannot be compiled for the Linux kernel as of now */
+#include <arm_neon.h>
+#include "ext_headers_arm.h"
+static inline void
+xor_256_aligned(uint8_t *dst, const uint8_t *src,
+ size_t size)
+{
+ uint64x2_t dst_128, src_128;
+
+ if (!aligned(src, sizeof(uint64x2_t) - 1) ||
+ !aligned(dst, sizeof(uint64x2_t) - 1)) {
+ xor_64(dst, src, size);
+ return;
+ }
+
+ LC_NEON_ENABLE;
+ for (; size >= sizeof(src_128); size -= sizeof(src_128),
+ dst += sizeof(dst_128),
+ src += sizeof(src_128)) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ src_128 = vld1q_u64((uint64_t *)src);
+ dst_128 = vld1q_u64((uint64_t *)dst);
+#pragma GCC diagnostic pop
+
+ dst_128 = veorq_u64(dst_128, src_128);
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ vst1q_u64((uint64_t *)dst, dst_128);
+#pragma GCC diagnostic pop
+ }
+ LC_NEON_DISABLE;
+
+ /*
+ * As we skip the xor_64 alignment check, guarantee it at compile time.
+ */
+ BUILD_BUG_ON(LC_XOR_NEON_ALIGNMENT < sizeof(uint64_t));
+ xor_64_aligned(dst, src, size);
+}
+
+static inline void
+xor_256(uint8_t *dst, const uint8_t *src, size_t size)
+{
+ enum lc_cpu_features feat =
+ lc_cpu_feature_available() & LC_CPU_FEATURE_ARM_NEON;
+
+ if (!aligned(src, LC_XOR_NEON_ALIGNMENT - 1) ||
+ !aligned(dst, LC_XOR_NEON_ALIGNMENT - 1) || !feat) {
+ xor_64(dst, src, size);
+ } else {
+ xor_256_aligned(dst, src, size);
+ }
+}
+
+#else /* LC_HOST_X86_64 */
+
+static inline void
+xor_256(uint8_t *dst, const uint8_t *src, size_t size)
+{
+ xor_64(dst, src, size);
+}
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* XOR256_H */
diff --git a/lib/freebl/manifest.mn b/lib/freebl/manifest.mn
--- a/lib/freebl/manifest.mn
+++ b/lib/freebl/manifest.mn
@@ -85,10 +85,11 @@
EXPORTS = \
blapit.h \
shsign.h \
ecl-exp.h \
$(LOWHASH_EXPORTS) \
+ $(ML_DSA_EXPORTS) \
$(NULL)
PRIVATE_EXPORTS = \
cmac.h \
alghmac.h \
@@ -111,10 +112,12 @@
ECL_HDRS = ecl-exp.h ecl.h ecl-priv.h
ECL_SRCS = ecp_25519.c ecp_secp256r1.c ecp_secp384r1.c ecp_secp521r1.c
SHA_SRCS = sha_fast.c
MPCPU_SRCS = mpcpucache.c
VERIFIED_SRCS = $(NULL)
+ML_DSA_SRCS = $(NULL)
+
CSRCS = \
freeblver.c \
ldvector.c \
sysrand.c \
@@ -161,10 +164,11 @@
$(KYBER_PQCRYSTALS) \
$(MPI_SRCS) \
$(MPCPU_SRCS) \
$(ECL_SRCS) \
$(VERIFIED_SRCS) \
+ $(ML_DSA_SRCS) \
$(STUBS_SRCS) \
$(LOWHASH_SRCS) \
$(EXTRA_SRCS) \
$(NULL)
diff --git a/lib/freebl/ml_dsa.c b/lib/freebl/ml_dsa.c
--- a/lib/freebl/ml_dsa.c
+++ b/lib/freebl/ml_dsa.c
@@ -17,92 +17,463 @@
#include "secitem.h"
#include "blapit.h"
#include "secport.h"
#include "secrng.h"
-#include "ml_dsat.h"
+
+#include "lc_dilithium.h"
+#include "ml_dsa_api.h"
-/* include other ml-dsa library specific includes here */
+/*
+ * some missing utilities, just use the nss implementations
+ */
+int
+lc_memcmp_secure(const void *s1, size_t s1n, const void *s2, size_t s2n)
+{
+ /* NSS's secure function takes on one len, get the min length
+ * to pass to it the point here is to be constant time... so
+ * we do the select checks in constant time: NOTE:this is really
+ * only constant time is the min value is constant, but we can't
+ * overrun the min buffer */
+ PRUint32 s1n_, s2n_, min, res;
+ s1n_ = s1n;
+ s2n_ = s2n;
+ min = PORT_CT_SEL(PORT_CT_LT(s1n_, s2n_), s1n_, s2n_);
+ res = NSS_SecureMemcmp(s1, s2, min);
+ return (int)PORT_CT_SEL(PORT_CT_EQ(s1n_, s2n_), res, 1);
+}
-/* this is private to this function and can be changed at will */
struct MLDSAContextStr {
PLArenaPool *arena;
MLDSAPrivateKey *privKey;
MLDSAPublicKey *pubKey;
CK_HEDGE_TYPE hedgeType;
CK_ML_DSA_PARAMETER_SET_TYPE paramSet;
- /* other ml-dsa lowelevel library require values and contexts */
+ struct lc_dilithium_ctx lc_dilithium;
};
+static void
+mldsa_DestroyContext(MLDSAContext *ctx)
+{
+ PLArenaPool *arena = ctx->arena;
+
+ /* free up any dangling hashes. Can happen in certain signature
+ * failure cases */
+ lc_hash_zero(&ctx->lc_dilithium.dilithium_hash_ctx);
+
+ /* this zeros out all the arena allocated data, so we don't have to
+ * do any expicit freeing */
+ PORT_FreeArena(arena, PR_TRUE);
+}
+
+static MLDSAContext *
+mldsa_NewContext(const MLDSAPrivateKey *privKey, const MLDSAPublicKey *pubKey)
+{
+ PLArenaPool *arena = NULL;
+ MLDSAContext *ctx = NULL;
+
+ /* must have one and only one of the keys */
+ if (!privKey && !pubKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ }
+ if (privKey && pubKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ }
+
+ arena = PORT_NewArena(1024);
+ if (arena == NULL) {
+ return NULL;
+ }
+
+ ctx = PORT_ArenaZNew(arena, MLDSAContext);
+ if (!ctx) {
+ goto loser;
+ }
+ ctx->arena = arena;
+ ctx->lc_dilithium.dilithium_hash_ctx.hash = lc_shake256;
+ ctx->lc_dilithium.dilithium_hash_ctx.stream = true;
+ /* we ZNew'd the context, so this is not necessary, but
+ * document it here or hashing won't work if we don't
+ * have a zero'ed context:
+ lctx->lc_dilithium.dilithium_hash_ctx.u.ctx_ptr = NULL; */
+ if (privKey) {
+ ctx->privKey = PORT_ArenaNew(arena, MLDSAPrivateKey);
+ if (ctx->privKey == NULL) {
+ goto loser;
+ }
+ PORT_Memcpy(ctx->privKey, privKey, sizeof(MLDSAPrivateKey));
+ }
+ if (pubKey) {
+ ctx->pubKey = PORT_ArenaNew(arena, MLDSAPublicKey);
+ if (ctx->pubKey == NULL) {
+ goto loser;
+ }
+ PORT_Memcpy(ctx->pubKey, pubKey, sizeof(MLDSAPublicKey));
+ }
+ return ctx;
+
+loser:
+ if (ctx) {
+ arena = 0;
+ mldsa_DestroyContext(ctx);
+ }
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return NULL;
+}
+
+static const MLDSAPrivateKey *
+mldsa_ContextGetPrivateKey(const MLDSAContext *ctx)
+{
+ return ctx->privKey;
+}
+
+static const MLDSAPublicKey *
+mldsa_ContextGetPublicKey(const MLDSAContext *ctx)
+{
+ return ctx->pubKey;
+}
+
/*
** Generate and return a new DSA public and private key pair,
** both of which are encoded into a single DSAPrivateKey struct.
** "params" is a pointer to the PQG parameters for the domain
** Uses a random seed.
*/
SECStatus
MLDSA_NewKey(CK_ML_DSA_PARAMETER_SET_TYPE paramSet, SECItem *seed,
MLDSAPrivateKey *privKey, MLDSAPublicKey *pubKey)
{
- /* needs to support returning the seed in the private key
- * (if seed is not supplied) or generating the key using the seed
- * (if it is supplied) if seed is supplied, it must be the correct
- * length */
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ int ret = -1;
+
+ /* make sure we can set the keys first */
+ if (!privKey || !pubKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ privKey->seedLen = ML_DSA_SEED_LEN;
+ privKey->paramSet = paramSet;
+ pubKey->paramSet = paramSet;
+ if (seed != NULL) {
+ if ((seed->data == NULL) || (seed->len != ML_DSA_SEED_LEN)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser;
+ }
+ PORT_Memcpy(privKey->seed, seed->data, ML_DSA_SEED_LEN);
+ } else {
+ RNG_SystemRNG(privKey->seed, ML_DSA_SEED_LEN);
+ }
+ privKey->seedLen = ML_DSA_SEED_LEN;
+
+ switch (paramSet) {
+ case CKP_ML_DSA_44:
+ ret = lc_dilithium_44_keypair_from_seed_c(
+ (struct lc_dilithium_44_pk *)pubKey->keyVal,
+ (struct lc_dilithium_44_sk *)privKey->keyVal,
+ privKey->seed, privKey->seedLen);
+ pubKey->keyValLen = ML_DSA_44_PUBLICKEY_LEN;
+ privKey->keyValLen = ML_DSA_44_PRIVATEKEY_LEN;
+ break;
+ case CKP_ML_DSA_65:
+ ret = lc_dilithium_65_keypair_from_seed_c(
+ (struct lc_dilithium_65_pk *)pubKey->keyVal,
+ (struct lc_dilithium_65_sk *)privKey->keyVal,
+ privKey->seed, privKey->seedLen);
+ pubKey->keyValLen = ML_DSA_65_PUBLICKEY_LEN;
+ privKey->keyValLen = ML_DSA_65_PRIVATEKEY_LEN;
+ break;
+ case CKP_ML_DSA_87:
+ ret = lc_dilithium_87_keypair_from_seed_c(
+ (struct lc_dilithium_87_pk *)pubKey->keyVal,
+ (struct lc_dilithium_87_sk *)privKey->keyVal,
+ privKey->seed, privKey->seedLen);
+ pubKey->keyValLen = ML_DSA_87_PUBLICKEY_LEN;
+ privKey->keyValLen = ML_DSA_87_PRIVATEKEY_LEN;
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ if (ret != 0) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser;
+ }
+ return SECSuccess;
+
+loser:
+ PORT_SafeZero(privKey, sizeof(privKey));
+ PORT_SafeZero(pubKey, sizeof(pubKey));
return SECFailure;
}
/*
* we don't have a streaming interace, so use our own local context
* to keep track of things */
SECStatus
MLDSA_SignInit(MLDSAPrivateKey *key, CK_HEDGE_TYPE hedgeType,
const SECItem *sgnCtx, MLDSAContext **ctx)
{
- /* if hedgeType is CKH_DETERMINISTIC_REQUIRED, otherwise it
- * should generate a HEDGE signature, can stash this value
- * if the library takes the hedge parameter in a later call */
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SECFailure;
+ int ret = -1;
+ MLDSAContext *lctx = NULL;
+ if (!ctx || !key || (sgnCtx && sgnCtx->len > 255)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ lctx = mldsa_NewContext(key, NULL);
+ if (lctx == NULL) {
+ return SECFailure;
+ }
+ lctx->hedgeType = hedgeType;
+ if (sgnCtx && sgnCtx->len != 0) {
+ lctx->lc_dilithium.userctx = sgnCtx->data;
+ lctx->lc_dilithium.userctxlen = sgnCtx->len;
+ }
+ lctx->paramSet = key->paramSet;
+
+ switch (key->paramSet) {
+ case CKP_ML_DSA_44:
+ ret = lc_dilithium_44_sign_init_c(&lctx->lc_dilithium,
+ (struct lc_dilithium_44_sk *)key->keyVal);
+ break;
+ case CKP_ML_DSA_65:
+ ret = lc_dilithium_65_sign_init_c(&lctx->lc_dilithium,
+ (struct lc_dilithium_65_sk *)key->keyVal);
+ break;
+ case CKP_ML_DSA_87:
+ ret = lc_dilithium_87_sign_init_c(&lctx->lc_dilithium,
+ (struct lc_dilithium_87_sk *)key->keyVal);
+ break;
+ }
+
+ if (ret < 0) {
+ mldsa_DestroyContext(lctx);
+ return SECFailure;
+ }
+ *ctx = lctx;
+ return SECSuccess;
}
SECStatus
MLDSA_SignUpdate(MLDSAContext *ctx, const SECItem *data)
{
- /* streaming interface. should not return a signature yet.
- * if the library can't do streaming, we need to buffer */
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SECFailure;
+ int ret = -1;
+ switch (ctx->paramSet) {
+ case CKP_ML_DSA_44:
+ ret = lc_dilithium_44_sign_update_c(&ctx->lc_dilithium,
+ data->data, data->len);
+ break;
+ case CKP_ML_DSA_65:
+ ret = lc_dilithium_65_sign_update_c(&ctx->lc_dilithium,
+ data->data, data->len);
+ break;
+ case CKP_ML_DSA_87:
+ ret = lc_dilithium_87_sign_update_c(&ctx->lc_dilithium,
+ data->data, data->len);
+ break;
+ }
+
+ if (ret < 0) {
+ return SECFailure;
+ }
+ return SECSuccess;
}
SECStatus
MLDSA_SignFinal(MLDSAContext *ctx, SECItem *signature)
{
- /* produce the actual signature, may need the key, so it needs to be
- * stashed in ML_DSA_SignInit */
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SECFailure;
+ /* make sure we have all the parameters */
+ if (!ctx || !signature) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ const MLDSAPrivateKey *key = mldsa_ContextGetPrivateKey(ctx);
+ int ret = -1;
+ size_t len = signature->len;
+ struct lc_rng_ctx system_rng = { NULL };
+ struct lc_rng_ctx *fake_rng = &system_rng;
+
+ if (!key) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (ctx->hedgeType == CKH_DETERMINISTIC_REQUIRED) {
+ fake_rng = NULL;
+ }
+
+ ret = -1;
+ switch (key->paramSet) {
+ case CKP_ML_DSA_44:
+ /* handle the case where we are trying to get the signature length
+ * or we supplied a length that was too short */
+ len = ML_DSA_44_SIGNATURE_LEN;
+ if (!signature->data ||
+ (signature->len < len)) {
+ signature->len = len;
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ break;
+ }
+ ret = lc_dilithium_44_sign_final_c(
+ (struct lc_dilithium_44_sig *)signature->data,
+ &ctx->lc_dilithium,
+ (struct lc_dilithium_44_sk *)key->keyVal,
+ fake_rng);
+ break;
+ case CKP_ML_DSA_65:
+ /* handle the case where we are trying to get the signature length
+ * or we supplied a length that was too short */
+ len = ML_DSA_65_SIGNATURE_LEN;
+ if (!signature->data ||
+ (signature->len < len)) {
+ signature->len = len;
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ break;
+ }
+ ret = lc_dilithium_65_sign_final_c(
+ (struct lc_dilithium_65_sig *)signature->data,
+ &ctx->lc_dilithium,
+ (struct lc_dilithium_65_sk *)key->keyVal,
+ fake_rng);
+ break;
+ case CKP_ML_DSA_87:
+ /* handle the case where we are trying to get the signature length
+ * or we supplied a length that was too short */
+ len = ML_DSA_87_SIGNATURE_LEN;
+ if (!signature->data ||
+ (signature->len < len)) {
+ signature->len = len;
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ break;
+ }
+ ret = lc_dilithium_87_sign_final_c(
+ (struct lc_dilithium_87_sig *)signature->data,
+ &ctx->lc_dilithium,
+ (struct lc_dilithium_87_sk *)key->keyVal,
+ fake_rng);
+ break;
+ default:
+ ret = -1;
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ break;
+ }
+ if (ret != 0) {
+ /* error code already set */
+ return SECFailure;
+ }
+ signature->len = len;
+ mldsa_DestroyContext(ctx);
+ return SECSuccess;
}
/*
* we don't have a streaming interace, so use our own local context
* to keep track of things */
SECStatus
MLDSA_VerifyInit(MLDSAPublicKey *key, const SECItem *sgnCtx, MLDSAContext **ctx)
{
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SECFailure;
+ MLDSAContext *lctx;
+ int ret = -1;
+ if (!ctx || !key || (sgnCtx && sgnCtx->len > 255)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ lctx = mldsa_NewContext(NULL, key);
+ if (!lctx) {
+ return SECFailure;
+ }
+ if (sgnCtx && sgnCtx->len != 0) {
+ lctx->lc_dilithium.userctx = sgnCtx->data;
+ lctx->lc_dilithium.userctxlen = sgnCtx->len;
+ }
+ lctx->paramSet = key->paramSet;
+
+ switch (key->paramSet) {
+ case CKP_ML_DSA_44:
+ ret = lc_dilithium_44_verify_init_c(&lctx->lc_dilithium,
+ (struct lc_dilithium_44_pk *)key->keyVal);
+ break;
+ case CKP_ML_DSA_65:
+ ret = lc_dilithium_65_verify_init_c(&lctx->lc_dilithium,
+ (struct lc_dilithium_65_pk *)key->keyVal);
+ break;
+ case CKP_ML_DSA_87:
+ ret = lc_dilithium_87_verify_init_c(&lctx->lc_dilithium,
+ (struct lc_dilithium_87_pk *)key->keyVal);
+ break;
+ }
+
+ if (ret < 0) {
+ mldsa_DestroyContext(lctx);
+ return SECFailure;
+ }
+ *ctx = lctx;
+ return SECSuccess;
}
SECStatus
MLDSA_VerifyUpdate(MLDSAContext *ctx, const SECItem *data)
{
- /* like Sign, a streaming interface some rules about buffering */
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SECFailure;
+ int ret = -1;
+ switch (ctx->paramSet) {
+ case CKP_ML_DSA_44:
+ ret = lc_dilithium_44_verify_update_c(&ctx->lc_dilithium,
+ data->data, data->len);
+ break;
+ case CKP_ML_DSA_65:
+ ret = lc_dilithium_65_verify_update_c(&ctx->lc_dilithium,
+ data->data, data->len);
+ break;
+ case CKP_ML_DSA_87:
+ ret = lc_dilithium_87_verify_update_c(&ctx->lc_dilithium,
+ data->data, data->len);
+ break;
+ }
+
+ if (ret < 0) {
+ return SECFailure;
+ }
+ return SECSuccess;
}
SECStatus
MLDSA_VerifyFinal(MLDSAContext *ctx, const SECItem *signature)
{
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SECFailure;
+ const MLDSAPublicKey *key = mldsa_ContextGetPublicKey(ctx);
+ int ret = -1;
+
+ if (key == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ ret = -1;
+ switch (key->paramSet) {
+ case CKP_ML_DSA_44:
+ ret = lc_dilithium_44_verify_final_c(
+ (struct lc_dilithium_44_sig *)signature->data,
+ &ctx->lc_dilithium, (struct lc_dilithium_44_pk *)key->keyVal);
+ break;
+ case CKP_ML_DSA_65:
+ ret = lc_dilithium_65_verify_final_c(
+ (struct lc_dilithium_65_sig *)signature->data,
+ &ctx->lc_dilithium, (struct lc_dilithium_65_pk *)key->keyVal);
+ break;
+ case CKP_ML_DSA_87:
+ ret = lc_dilithium_87_verify_final_c(
+ (struct lc_dilithium_87_sig *)signature->data,
+ &ctx->lc_dilithium, (struct lc_dilithium_87_pk *)key->keyVal);
+ break;
+ default:
+ ret = -1;
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ break;
+ }
+ if (ret != 0) {
+ /* in Verify we close the context on an invalid signature as well
+ * as success */
+ mldsa_DestroyContext(ctx);
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ return SECFailure;
+ }
+ mldsa_DestroyContext(ctx);
+ return SECSuccess;
}
diff --git a/lib/freebl/stubs.h b/lib/freebl/stubs.h
--- a/lib/freebl/stubs.h
+++ b/lib/freebl/stubs.h
@@ -21,10 +21,11 @@
#define _LIBUTIL_H_ 1
#define PORT_Alloc PORT_Alloc_stub
#define PORT_ArenaAlloc PORT_ArenaAlloc_stub
#define PORT_ArenaZAlloc PORT_ArenaZAlloc_stub
+#define PORT_ArenaGrow PORT_ArenaGrow_stub
#define PORT_Free PORT_Free_stub
#define PORT_FreeArena PORT_FreeArena_stub
#define PORT_GetError PORT_GetError_stub
#define PORT_NewArena PORT_NewArena_stub
#define PORT_SafeZero PORT_SafeZero_stub
diff --git a/lib/freebl/stubs.c b/lib/freebl/stubs.c
--- a/lib/freebl/stubs.c
+++ b/lib/freebl/stubs.c
@@ -138,10 +138,11 @@
#endif
STUB_DECLARE(void *, PORT_Alloc_Util, (size_t len));
STUB_DECLARE(void *, PORT_ArenaAlloc_Util, (PLArenaPool * arena, size_t size));
STUB_DECLARE(void *, PORT_ArenaZAlloc_Util, (PLArenaPool * arena, size_t size));
+STUB_DECLARE(void *, PORT_ArenaGrow_Util, (PLArenaPool * arena, void *ptr, size_t oldsize, size_t newsize));
STUB_DECLARE(void, PORT_Free_Util, (void *ptr));
STUB_DECLARE(void, PORT_FreeArena_Util, (PLArenaPool * arena, PRBool zero));
STUB_DECLARE(int, PORT_GetError_Util, (void));
STUB_DECLARE(PLArenaPool *, PORT_NewArena_Util, (unsigned long chunksize));
STUB_DECLARE(void, PORT_SafeZero, (void *p, size_t n));
@@ -329,10 +330,19 @@
STUB_SAFE_CALL2(PORT_ArenaZAlloc_Util, arena, size);
abort();
return NULL;
}
+extern void *
+PORT_ArenaGrow_stub(PLArenaPool *arena, void *ptr, size_t oldsize, size_t newsize)
+{
+
+ STUB_SAFE_CALL4(PORT_ArenaGrow_Util, arena, ptr, oldsize, newsize);
+ abort();
+ return NULL;
+}
+
extern void
PORT_FreeArena_stub(PLArenaPool *arena, PRBool zero)
{
STUB_SAFE_CALL2(PORT_FreeArena_Util, arena, zero);
@@ -811,10 +821,11 @@
STUB_FETCH_FUNCTION(PORT_ZAlloc_Util);
STUB_FETCH_FUNCTION(PORT_ZFree_Util);
STUB_FETCH_FUNCTION(PORT_NewArena_Util);
STUB_FETCH_FUNCTION(PORT_ArenaAlloc_Util);
STUB_FETCH_FUNCTION(PORT_ArenaZAlloc_Util);
+ STUB_FETCH_FUNCTION(PORT_ArenaGrow_Util);
STUB_FETCH_FUNCTION(PORT_FreeArena_Util);
STUB_FETCH_FUNCTION(PORT_GetError_Util);
STUB_FETCH_FUNCTION(PORT_SetError_Util);
STUB_FETCH_FUNCTION(SECITEM_FreeItem_Util);
STUB_FETCH_FUNCTION(SECITEM_AllocItem_Util);
diff --git a/lib/softoken/pkcs11.c b/lib/softoken/pkcs11.c
--- a/lib/softoken/pkcs11.c
+++ b/lib/softoken/pkcs11.c
@@ -685,15 +685,13 @@
#endif
{ CKM_NSS_ML_KEM_KEY_PAIR_GEN, { 0, 0, CKF_GENERATE_KEY_PAIR }, PR_TRUE },
{ CKM_NSS_ML_KEM, { 0, 0, CKF_KEM }, PR_TRUE },
{ CKM_ML_KEM_KEY_PAIR_GEN, { 0, 0, CKF_GENERATE_KEY_PAIR }, PR_TRUE },
{ CKM_ML_KEM, { 0, 0, CKF_KEM }, PR_TRUE },
-/* don't advertize ML_DSA support until we have it working in freebl */
-#ifdef NSS_ENABLE_ML_DSA
+ /* don't advertize ML_DSA support until we have it working in freebl */
{ CKM_ML_DSA_KEY_PAIR_GEN, { ML_DSA_44_PUBLICKEY_LEN, ML_DSA_87_PUBLICKEY_LEN, CKF_GENERATE }, PR_TRUE },
{ CKM_ML_DSA, { ML_DSA_44_PUBLICKEY_LEN, ML_DSA_87_PUBLICKEY_LEN, CKF_SN_VR }, PR_TRUE },
-#endif
};
static const CK_ULONG mechanismCount = sizeof(mechanisms) / sizeof(mechanisms[0]);
/* sigh global so fipstokn can read it */
PRBool nsc_init = PR_FALSE;