From: Eric Biggers <ebiggers@kernel.org>
To: linux-crypto@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, Ard Biesheuvel <ardb@kernel.org>,
"Jason A . Donenfeld" <Jason@zx2c4.com>,
Herbert Xu <herbert@gondor.apana.org.au>,
linux-arm-kernel@lists.infradead.org, x86@kernel.org,
Eric Biggers <ebiggers@kernel.org>
Subject: [PATCH 01/12] lib/crypto: nh: Add NH library
Date: Wed, 10 Dec 2025 17:18:33 -0800 [thread overview]
Message-ID: <20251211011846.8179-2-ebiggers@kernel.org> (raw)
In-Reply-To: <20251211011846.8179-1-ebiggers@kernel.org>
Add support for the NH "almost-universal hash function" to lib/crypto/,
specifically the variant of NH used in Adiantum.
This will replace the need for the "nhpoly1305" crypto_shash algorithm.
All the implementations of "nhpoly1305" use architecture-optimized code
only for the NH stage; they just use the generic C Poly1305 code for the
Poly1305 stage. We can achieve the same result in a simpler way using
an (architecture-optimized) nh() function combined with code in
crypto/adiantum.c that passes the results to the Poly1305 library.
This commit begins this cleanup by adding the nh() function. The code
is derived from crypto/nhpoly1305.c and include/crypto/nhpoly1305.h.
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
include/crypto/nh.h | 52 ++++++++++++++++++++++++++++
lib/crypto/Kconfig | 10 ++++++
lib/crypto/Makefile | 8 +++++
lib/crypto/nh.c | 82 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 152 insertions(+)
create mode 100644 include/crypto/nh.h
create mode 100644 lib/crypto/nh.c
diff --git a/include/crypto/nh.h b/include/crypto/nh.h
new file mode 100644
index 000000000000..465e85bf203f
--- /dev/null
+++ b/include/crypto/nh.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * NH hash function for Adiantum
+ */
+
+#ifndef _CRYPTO_NH_H
+#define _CRYPTO_NH_H
+
+#include <linux/types.h>
+
+/* NH parameterization: */
+
+/* Endianness: little */
+/* Word size: 32 bits (works well on NEON, SSE2, AVX2) */
+
+/* Stride: 2 words (optimal on ARM32 NEON; works okay on other CPUs too) */
+#define NH_PAIR_STRIDE 2
+#define NH_MESSAGE_UNIT (NH_PAIR_STRIDE * 2 * sizeof(u32))
+
+/* Num passes (Toeplitz iteration count): 4, to give ε = 2^{-128} */
+#define NH_NUM_PASSES 4
+#define NH_HASH_BYTES (NH_NUM_PASSES * sizeof(u64))
+
+/* Max message size: 1024 bytes (32x compression factor) */
+#define NH_NUM_STRIDES 64
+#define NH_MESSAGE_WORDS (NH_PAIR_STRIDE * 2 * NH_NUM_STRIDES)
+#define NH_MESSAGE_BYTES (NH_MESSAGE_WORDS * sizeof(u32))
+#define NH_KEY_WORDS (NH_MESSAGE_WORDS + \
+ NH_PAIR_STRIDE * 2 * (NH_NUM_PASSES - 1))
+#define NH_KEY_BYTES (NH_KEY_WORDS * sizeof(u32))
+
+/**
+ * nh() - NH hash function for Adiantum
+ * @key: The key. @message_len + 48 bytes of it are used. This is NH_KEY_BYTES
+ * if @message_len has its maximum length of NH_MESSAGE_BYTES.
+ * @message: The message
+ * @message_len: The message length in bytes. Must be a multiple of 16
+ * (NH_MESSAGE_UNIT) and at most 1024 (NH_MESSAGE_BYTES).
+ * @hash: (output) The resulting hash value
+ *
+ * Note: the pseudocode for NH in the Adiantum paper iterates over 1024-byte
+ * segments of the message, computes a 32-byte hash for each, and returns all
+ * the hashes concatenated together. In contrast, this function just hashes one
+ * segment and returns one hash. It's the caller's responsibility to call this
+ * function for each 1024-byte segment and collect all the hashes.
+ *
+ * Context: Any context.
+ */
+void nh(const u32 *key, const u8 *message, size_t message_len,
+ __le64 hash[NH_NUM_PASSES]);
+
+#endif /* _CRYPTO_NH_H */
diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig
index ee6ab129d0cf..f14c9f5974d8 100644
--- a/lib/crypto/Kconfig
+++ b/lib/crypto/Kconfig
@@ -106,10 +106,20 @@ config CRYPTO_LIB_MLDSA
select CRYPTO_LIB_SHA3
help
The ML-DSA library functions. Select this if your module uses any of
the functions from <crypto/mldsa.h>.
+config CRYPTO_LIB_NH
+ tristate
+ help
+ Implementation of the NH almost-universal hash function, specifically
+ the variant of NH used in Adiantum.
+
+config CRYPTO_LIB_NH_ARCH
+ bool
+ depends on CRYPTO_LIB_NH && !UML
+
config CRYPTO_LIB_POLY1305
tristate
help
The Poly1305 library functions. Select this if your module uses any
of the functions from <crypto/poly1305.h>.
diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile
index a0578105266f..929b84568809 100644
--- a/lib/crypto/Makefile
+++ b/lib/crypto/Makefile
@@ -129,10 +129,18 @@ endif # CONFIG_CRYPTO_LIB_MD5_ARCH
obj-$(CONFIG_CRYPTO_LIB_MLDSA) += libmldsa.o
libmldsa-y := mldsa.o
################################################################################
+obj-$(CONFIG_CRYPTO_LIB_NH) += libnh.o
+libnh-y := nh.o
+ifeq ($(CONFIG_CRYPTO_LIB_NH_ARCH),y)
+CFLAGS_nh.o += -I$(src)/$(SRCARCH)
+endif
+
+################################################################################
+
obj-$(CONFIG_CRYPTO_LIB_POLY1305) += libpoly1305.o
libpoly1305-y := poly1305.o
ifeq ($(CONFIG_ARCH_SUPPORTS_INT128),y)
libpoly1305-$(CONFIG_CRYPTO_LIB_POLY1305_GENERIC) += poly1305-donna64.o
else
diff --git a/lib/crypto/nh.c b/lib/crypto/nh.c
new file mode 100644
index 000000000000..e1d0095b5289
--- /dev/null
+++ b/lib/crypto/nh.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2018 Google LLC
+ */
+
+/*
+ * Implementation of the NH almost-universal hash function, specifically the
+ * variant of NH used in Adiantum. This is *not* a cryptographic hash function.
+ *
+ * Reference: section 6.3 of "Adiantum: length-preserving encryption for
+ * entry-level processors" (https://eprint.iacr.org/2018/720.pdf).
+ */
+
+#include <crypto/nh.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/unaligned.h>
+
+#ifdef CONFIG_CRYPTO_LIB_NH_ARCH
+#include "nh.h" /* $(SRCARCH)/nh.h */
+#else
+static bool nh_arch(const u32 *key, const u8 *message, size_t message_len,
+ __le64 hash[NH_NUM_PASSES])
+{
+ return false;
+}
+#endif
+
+void nh(const u32 *key, const u8 *message, size_t message_len,
+ __le64 hash[NH_NUM_PASSES])
+{
+ u64 sums[4] = { 0, 0, 0, 0 };
+
+ if (nh_arch(key, message, message_len, hash))
+ return;
+
+ static_assert(NH_PAIR_STRIDE == 2);
+ static_assert(NH_NUM_PASSES == 4);
+
+ while (message_len) {
+ u32 m0 = get_unaligned_le32(message + 0);
+ u32 m1 = get_unaligned_le32(message + 4);
+ u32 m2 = get_unaligned_le32(message + 8);
+ u32 m3 = get_unaligned_le32(message + 12);
+
+ sums[0] += (u64)(u32)(m0 + key[0]) * (u32)(m2 + key[2]);
+ sums[1] += (u64)(u32)(m0 + key[4]) * (u32)(m2 + key[6]);
+ sums[2] += (u64)(u32)(m0 + key[8]) * (u32)(m2 + key[10]);
+ sums[3] += (u64)(u32)(m0 + key[12]) * (u32)(m2 + key[14]);
+ sums[0] += (u64)(u32)(m1 + key[1]) * (u32)(m3 + key[3]);
+ sums[1] += (u64)(u32)(m1 + key[5]) * (u32)(m3 + key[7]);
+ sums[2] += (u64)(u32)(m1 + key[9]) * (u32)(m3 + key[11]);
+ sums[3] += (u64)(u32)(m1 + key[13]) * (u32)(m3 + key[15]);
+ key += NH_MESSAGE_UNIT / sizeof(key[0]);
+ message += NH_MESSAGE_UNIT;
+ message_len -= NH_MESSAGE_UNIT;
+ }
+
+ hash[0] = cpu_to_le64(sums[0]);
+ hash[1] = cpu_to_le64(sums[1]);
+ hash[2] = cpu_to_le64(sums[2]);
+ hash[3] = cpu_to_le64(sums[3]);
+}
+EXPORT_SYMBOL_GPL(nh);
+
+#ifdef nh_mod_init_arch
+static int __init nh_mod_init(void)
+{
+ nh_mod_init_arch();
+ return 0;
+}
+subsys_initcall(nh_mod_init);
+
+static void __exit nh_mod_exit(void)
+{
+}
+module_exit(nh_mod_exit);
+#endif
+
+MODULE_DESCRIPTION("NH almost-universal hash function");
+MODULE_LICENSE("GPL");
--
2.52.0
next prev parent reply other threads:[~2025-12-11 1:21 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-11 1:18 [PATCH 00/12] NH library and Adiantum cleanup Eric Biggers
2025-12-11 1:18 ` Eric Biggers [this message]
2025-12-11 1:18 ` [PATCH 02/12] lib/crypto: tests: Add KUnit tests for NH Eric Biggers
2026-02-26 13:12 ` Geert Uytterhoeven
2026-02-26 18:11 ` Eric Biggers
2025-12-11 1:18 ` [PATCH 03/12] lib/crypto: arm/nh: Migrate optimized code into library Eric Biggers
2025-12-11 1:18 ` [PATCH 04/12] lib/crypto: arm64/nh: " Eric Biggers
2025-12-11 1:18 ` [PATCH 05/12] lib/crypto: x86/nh: " Eric Biggers
2025-12-11 1:18 ` [PATCH 06/12] crypto: adiantum - Convert to use NH library Eric Biggers
2025-12-11 1:18 ` [PATCH 07/12] crypto: adiantum - Use scatter_walk API instead of sg_miter Eric Biggers
2025-12-11 1:18 ` [PATCH 08/12] crypto: adiantum - Use memcpy_{to,from}_sglist() Eric Biggers
2025-12-11 3:02 ` Herbert Xu
2025-12-11 1:18 ` [PATCH 09/12] crypto: adiantum - Drop support for asynchronous xchacha ciphers Eric Biggers
2025-12-11 1:18 ` [PATCH 10/12] crypto: nhpoly1305 - Remove crypto_shash support Eric Biggers
2025-12-11 3:02 ` Herbert Xu
2025-12-11 1:18 ` [PATCH 11/12] crypto: testmgr - Remove nhpoly1305 tests Eric Biggers
2025-12-11 3:03 ` Herbert Xu
2025-12-11 1:18 ` [PATCH 12/12] fscrypt: Drop obsolete recommendation to enable optimized NHPoly1305 Eric Biggers
2025-12-18 19:25 ` [PATCH 00/12] NH library and Adiantum cleanup Eric Biggers
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251211011846.8179-2-ebiggers@kernel.org \
--to=ebiggers@kernel.org \
--cc=Jason@zx2c4.com \
--cc=ardb@kernel.org \
--cc=herbert@gondor.apana.org.au \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-crypto@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.