Commit 66de86426e9cdb88526974c765108f01554af2b0

Steffen Jaeckel 2022-03-14T10:43:52

Merge pull request #505 from urazoff/develop Add FNV-1a hash function

diff --git a/demo/test.c b/demo/test.c
index b9ef509..c87f97c 100644
--- a/demo/test.c
+++ b/demo/test.c
@@ -153,6 +153,56 @@ LBL_ERR:
    return EXIT_FAILURE;
 }
 
+static int test_mp_hash(void)
+{
+   mp_int a;
+   mp_hval hash;
+   int i;
+   int len = 5;
+
+   const char *input[] = {
+      "0",
+      "///////////////////////////////////////////////////////////////////",
+      "4n9cbk886QtLQmofprid3l2Q0GD8Yv979Lh8BdZkFE8g2pDUUSMBET/+M/YFyVZ3mBp",
+      "5NlgzHhmIX05O5YoW5yW5reAlVNtRAlIcN2dfoATnNdc1Cw5lHZUTwNthmK6/ZLKfY6",
+      "3gweiHDX+ji5utraSe46IJX+uuh7iggs63xIpMP5MriU4Np+LpHI5are8RzS9pKh9xP"
+   };
+   const mp_hval hvals[] = {
+#if (MP_DIGIT_BIT == 15)
+      0x50c5d1f,
+      0x51b3ba04,
+      0xf83febd7,
+      0x2dc8624c,
+      0xf5c2996b
+#elif (MP_DIGIT_BIT == 60)
+      0xaf63bd4c8601b7df,
+      0xdb090f8a5cd75210,
+      0xabae35c7872c107d,
+      0xfec74888bcef5fcd,
+      0x27ba96030abceda5
+#else
+      0xaf63bd4c8601b7df,
+      0x7e868fbf541faf44,
+      0x420cca3a4cb623bb,
+      0x16636d996304ee7f,
+      0x33afc9f1b274fa67
+#endif
+   };
+
+   DOR(mp_init(&a));
+   for (i = 0; i < len; ++i) {
+      DO(mp_read_radix(&a, input[i], 64));
+      DO(mp_hash(&a, &hash));
+      EXPECT(hash == hvals[i]);
+   }
+
+   mp_clear(&a);
+   return EXIT_SUCCESS;
+LBL_ERR:
+   mp_clear(&a);
+   return EXIT_FAILURE;
+}
+
 static int check_get_set_i32(mp_int *a, int32_t b)
 {
    mp_clear(a);
@@ -2164,6 +2214,7 @@ static int unit_tests(int argc, char **argv)
 #define T3(n, o1, o2, o3)  { #n, (MP_HAS(o1) && MP_HAS(o2) && MP_HAS(o3)) ? test_##n : NULL }
       T0(feature_detection),
       T0(trivial_stuff),
+      T1(mp_hash, MP_HASH),
       T2(mp_get_set_i32, MP_GET_I32, MP_GET_MAG_U32),
       T2(mp_get_set_i64, MP_GET_I64, MP_GET_MAG_U64),
       T1(mp_and, MP_AND),
diff --git a/doc/bn.tex b/doc/bn.tex
index 1dfb34d..3e01e32 100644
--- a/doc/bn.tex
+++ b/doc/bn.tex
@@ -1444,6 +1444,20 @@ This divides $a$ by $b$ and stores the quotient in $c$ and $d$.  The signed quot
 such that $bc + d = a$.  Note that either of $c$ or $d$ can be set to \texttt{NULL} if their value
 is not required.  If $b$ is zero the function returns \texttt{MP\_VAL}.
 
+\section{Hashing}
+To get a non-cryptographic hash of an \texttt{mp\_int} use the following function.
+
+\index{mp\_hash}
+\begin{alltt}
+mp_err mp_hash (mp_int *a, mp_hval *hash);
+\end{alltt}
+
+This will create the hash of $a$ following the \mbox{FNV-1a} algorithm as described on
+\url{http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-1a}. With the
+help of this function one can use an \texttt{mp\_int} as a key in a hash table.
+
+NB: The hashing is not stable over different widths of an \texttt{mp\_digit}.
+
 \chapter{Multiplication and Squaring}
 \section{Multiplication}
 A full signed integer multiplication can be performed with the following.
diff --git a/libtommath_VS2008.vcproj b/libtommath_VS2008.vcproj
index 1bf2f6b..e27aa98 100644
--- a/libtommath_VS2008.vcproj
+++ b/libtommath_VS2008.vcproj
@@ -481,6 +481,10 @@
 			>
 		</File>
 		<File
+			RelativePath="mp_hash.c"
+			>
+		</File>
+		<File
 			RelativePath="mp_init.c"
 			>
 		</File>
diff --git a/makefile b/makefile
index a5e5bf1..7fa04d2 100644
--- a/makefile
+++ b/makefile
@@ -31,9 +31,9 @@ mp_cmp.o mp_cmp_d.o mp_cmp_mag.o mp_cnt_lsb.o mp_complement.o mp_copy.o mp_count
 mp_div.o mp_div_2.o mp_div_2d.o mp_div_d.o mp_dr_is_modulus.o mp_dr_reduce.o mp_dr_setup.o \
 mp_error_to_string.o mp_exch.o mp_expt_n.o mp_exptmod.o mp_exteuclid.o mp_fread.o mp_from_sbin.o \
 mp_from_ubin.o mp_fwrite.o mp_gcd.o mp_get_double.o mp_get_i32.o mp_get_i64.o mp_get_l.o mp_get_mag_u32.o \
-mp_get_mag_u64.o mp_get_mag_ul.o mp_grow.o mp_init.o mp_init_copy.o mp_init_i32.o mp_init_i64.o mp_init_l.o \
-mp_init_multi.o mp_init_set.o mp_init_size.o mp_init_u32.o mp_init_u64.o mp_init_ul.o mp_invmod.o \
-mp_is_square.o mp_kronecker.o mp_lcm.o mp_log_n.o mp_lshd.o mp_mod.o mp_mod_2d.o \
+mp_get_mag_u64.o mp_get_mag_ul.o mp_grow.o mp_hash.o mp_init.o mp_init_copy.o mp_init_i32.o mp_init_i64.o \
+mp_init_l.o mp_init_multi.o mp_init_set.o mp_init_size.o mp_init_u32.o mp_init_u64.o mp_init_ul.o \
+mp_invmod.o mp_is_square.o mp_kronecker.o mp_lcm.o mp_log_n.o mp_lshd.o mp_mod.o mp_mod_2d.o \
 mp_montgomery_calc_normalization.o mp_montgomery_reduce.o mp_montgomery_setup.o mp_mul.o mp_mul_2.o \
 mp_mul_2d.o mp_mul_d.o mp_mulmod.o mp_neg.o mp_or.o mp_pack.o mp_pack_count.o mp_prime_fermat.o \
 mp_prime_frobenius_underwood.o mp_prime_is_prime.o mp_prime_miller_rabin.o mp_prime_next_prime.o \
diff --git a/makefile.mingw b/makefile.mingw
index a0192d6..7aeb564 100644
--- a/makefile.mingw
+++ b/makefile.mingw
@@ -33,9 +33,9 @@ mp_cmp.o mp_cmp_d.o mp_cmp_mag.o mp_cnt_lsb.o mp_complement.o mp_copy.o mp_count
 mp_div.o mp_div_2.o mp_div_2d.o mp_div_d.o mp_dr_is_modulus.o mp_dr_reduce.o mp_dr_setup.o \
 mp_error_to_string.o mp_exch.o mp_expt_n.o mp_exptmod.o mp_exteuclid.o mp_fread.o mp_from_sbin.o \
 mp_from_ubin.o mp_fwrite.o mp_gcd.o mp_get_double.o mp_get_i32.o mp_get_i64.o mp_get_l.o mp_get_mag_u32.o \
-mp_get_mag_u64.o mp_get_mag_ul.o mp_grow.o mp_init.o mp_init_copy.o mp_init_i32.o mp_init_i64.o mp_init_l.o \
-mp_init_multi.o mp_init_set.o mp_init_size.o mp_init_u32.o mp_init_u64.o mp_init_ul.o mp_invmod.o \
-mp_is_square.o mp_kronecker.o mp_lcm.o mp_log_n.o mp_lshd.o mp_mod.o mp_mod_2d.o \
+mp_get_mag_u64.o mp_get_mag_ul.o mp_grow.o mp_hash.o mp_init.o mp_init_copy.o mp_init_i32.o mp_init_i64.o \
+mp_init_l.o mp_init_multi.o mp_init_set.o mp_init_size.o mp_init_u32.o mp_init_u64.o mp_init_ul.o \
+mp_invmod.o mp_is_square.o mp_kronecker.o mp_lcm.o mp_log_n.o mp_lshd.o mp_mod.o mp_mod_2d.o \
 mp_montgomery_calc_normalization.o mp_montgomery_reduce.o mp_montgomery_setup.o mp_mul.o mp_mul_2.o \
 mp_mul_2d.o mp_mul_d.o mp_mulmod.o mp_neg.o mp_or.o mp_pack.o mp_pack_count.o mp_prime_fermat.o \
 mp_prime_frobenius_underwood.o mp_prime_is_prime.o mp_prime_miller_rabin.o mp_prime_next_prime.o \
diff --git a/makefile.msvc b/makefile.msvc
index df886e9..59a365e 100644
--- a/makefile.msvc
+++ b/makefile.msvc
@@ -29,9 +29,9 @@ mp_cmp.obj mp_cmp_d.obj mp_cmp_mag.obj mp_cnt_lsb.obj mp_complement.obj mp_copy.
 mp_div.obj mp_div_2.obj mp_div_2d.obj mp_div_d.obj mp_dr_is_modulus.obj mp_dr_reduce.obj mp_dr_setup.obj \
 mp_error_to_string.obj mp_exch.obj mp_expt_n.obj mp_exptmod.obj mp_exteuclid.obj mp_fread.obj mp_from_sbin.obj \
 mp_from_ubin.obj mp_fwrite.obj mp_gcd.obj mp_get_double.obj mp_get_i32.obj mp_get_i64.obj mp_get_l.obj mp_get_mag_u32.obj \
-mp_get_mag_u64.obj mp_get_mag_ul.obj mp_grow.obj mp_init.obj mp_init_copy.obj mp_init_i32.obj mp_init_i64.obj mp_init_l.obj \
-mp_init_multi.obj mp_init_set.obj mp_init_size.obj mp_init_u32.obj mp_init_u64.obj mp_init_ul.obj mp_invmod.obj \
-mp_is_square.obj mp_kronecker.obj mp_lcm.obj mp_log_n.obj mp_lshd.obj mp_mod.obj mp_mod_2d.obj \
+mp_get_mag_u64.obj mp_get_mag_ul.obj mp_grow.obj mp_hash.obj mp_init.obj mp_init_copy.obj mp_init_i32.obj mp_init_i64.obj \
+mp_init_l.obj mp_init_multi.obj mp_init_set.obj mp_init_size.obj mp_init_u32.obj mp_init_u64.obj mp_init_ul.obj \
+mp_invmod.obj mp_is_square.obj mp_kronecker.obj mp_lcm.obj mp_log_n.obj mp_lshd.obj mp_mod.obj mp_mod_2d.obj \
 mp_montgomery_calc_normalization.obj mp_montgomery_reduce.obj mp_montgomery_setup.obj mp_mul.obj mp_mul_2.obj \
 mp_mul_2d.obj mp_mul_d.obj mp_mulmod.obj mp_neg.obj mp_or.obj mp_pack.obj mp_pack_count.obj mp_prime_fermat.obj \
 mp_prime_frobenius_underwood.obj mp_prime_is_prime.obj mp_prime_miller_rabin.obj mp_prime_next_prime.obj \
diff --git a/makefile.shared b/makefile.shared
index 9319e65..41a0cd2 100644
--- a/makefile.shared
+++ b/makefile.shared
@@ -28,9 +28,9 @@ mp_cmp.o mp_cmp_d.o mp_cmp_mag.o mp_cnt_lsb.o mp_complement.o mp_copy.o mp_count
 mp_div.o mp_div_2.o mp_div_2d.o mp_div_d.o mp_dr_is_modulus.o mp_dr_reduce.o mp_dr_setup.o \
 mp_error_to_string.o mp_exch.o mp_expt_n.o mp_exptmod.o mp_exteuclid.o mp_fread.o mp_from_sbin.o \
 mp_from_ubin.o mp_fwrite.o mp_gcd.o mp_get_double.o mp_get_i32.o mp_get_i64.o mp_get_l.o mp_get_mag_u32.o \
-mp_get_mag_u64.o mp_get_mag_ul.o mp_grow.o mp_init.o mp_init_copy.o mp_init_i32.o mp_init_i64.o mp_init_l.o \
-mp_init_multi.o mp_init_set.o mp_init_size.o mp_init_u32.o mp_init_u64.o mp_init_ul.o mp_invmod.o \
-mp_is_square.o mp_kronecker.o mp_lcm.o mp_log_n.o mp_lshd.o mp_mod.o mp_mod_2d.o \
+mp_get_mag_u64.o mp_get_mag_ul.o mp_grow.o mp_hash.o mp_init.o mp_init_copy.o mp_init_i32.o mp_init_i64.o \
+mp_init_l.o mp_init_multi.o mp_init_set.o mp_init_size.o mp_init_u32.o mp_init_u64.o mp_init_ul.o \
+mp_invmod.o mp_is_square.o mp_kronecker.o mp_lcm.o mp_log_n.o mp_lshd.o mp_mod.o mp_mod_2d.o \
 mp_montgomery_calc_normalization.o mp_montgomery_reduce.o mp_montgomery_setup.o mp_mul.o mp_mul_2.o \
 mp_mul_2d.o mp_mul_d.o mp_mulmod.o mp_neg.o mp_or.o mp_pack.o mp_pack_count.o mp_prime_fermat.o \
 mp_prime_frobenius_underwood.o mp_prime_is_prime.o mp_prime_miller_rabin.o mp_prime_next_prime.o \
diff --git a/makefile.unix b/makefile.unix
index 64fb580..be04674 100644
--- a/makefile.unix
+++ b/makefile.unix
@@ -34,9 +34,9 @@ mp_cmp.o mp_cmp_d.o mp_cmp_mag.o mp_cnt_lsb.o mp_complement.o mp_copy.o mp_count
 mp_div.o mp_div_2.o mp_div_2d.o mp_div_d.o mp_dr_is_modulus.o mp_dr_reduce.o mp_dr_setup.o \
 mp_error_to_string.o mp_exch.o mp_expt_n.o mp_exptmod.o mp_exteuclid.o mp_fread.o mp_from_sbin.o \
 mp_from_ubin.o mp_fwrite.o mp_gcd.o mp_get_double.o mp_get_i32.o mp_get_i64.o mp_get_l.o mp_get_mag_u32.o \
-mp_get_mag_u64.o mp_get_mag_ul.o mp_grow.o mp_init.o mp_init_copy.o mp_init_i32.o mp_init_i64.o mp_init_l.o \
-mp_init_multi.o mp_init_set.o mp_init_size.o mp_init_u32.o mp_init_u64.o mp_init_ul.o mp_invmod.o \
-mp_is_square.o mp_kronecker.o mp_lcm.o mp_log_n.o mp_lshd.o mp_mod.o mp_mod_2d.o \
+mp_get_mag_u64.o mp_get_mag_ul.o mp_grow.o mp_hash.o mp_init.o mp_init_copy.o mp_init_i32.o mp_init_i64.o \
+mp_init_l.o mp_init_multi.o mp_init_set.o mp_init_size.o mp_init_u32.o mp_init_u64.o mp_init_ul.o \
+mp_invmod.o mp_is_square.o mp_kronecker.o mp_lcm.o mp_log_n.o mp_lshd.o mp_mod.o mp_mod_2d.o \
 mp_montgomery_calc_normalization.o mp_montgomery_reduce.o mp_montgomery_setup.o mp_mul.o mp_mul_2.o \
 mp_mul_2d.o mp_mul_d.o mp_mulmod.o mp_neg.o mp_or.o mp_pack.o mp_pack_count.o mp_prime_fermat.o \
 mp_prime_frobenius_underwood.o mp_prime_is_prime.o mp_prime_miller_rabin.o mp_prime_next_prime.o \
diff --git a/mp_hash.c b/mp_hash.c
new file mode 100644
index 0000000..2add757
--- /dev/null
+++ b/mp_hash.c
@@ -0,0 +1,36 @@
+#include "tommath_private.h"
+#ifdef MP_HASH_C
+/* LibTomMath, multiple-precision integer library -- Tom St Denis */
+/* SPDX-License-Identifier: Unlicense */
+
+#if defined(MP_16BIT)
+#define FNV_1A_INIT ((uint32_t)0x811c9dc5)
+#define FNV_1A_PRIME ((uint32_t)0x01000193)
+#else
+#define FNV_1A_INIT ((uint64_t)0xcbf29ce484222325ULL)
+#define FNV_1A_PRIME ((uint64_t)0x100000001b3ULL)
+#endif
+
+/* computes hash of mp_int. */
+mp_err mp_hash(mp_int *a, mp_hval *hash)
+{
+   int  x;
+   mp_hval hval = FNV_1A_INIT;
+   mp_digit r, mask, shift;
+
+   /* FNV-1a algorithm */
+   mask = ((mp_digit)1 << 8) - 1uL;
+   shift = (mp_digit)(MP_DIGIT_BIT - 8);
+   r = 0;
+   for (x = a->used; x --> 0;) {
+      mp_digit rr = a->dp[x] & mask;
+      hval ^= (mp_hval)(a->dp[x] >> 8) | (r << shift);
+      hval *= FNV_1A_PRIME;
+      r = rr;
+   }
+   hval ^= mp_isneg(a) ? (mp_hval)1 : (mp_hval)0;
+   *hash = hval * FNV_1A_PRIME;
+
+   return MP_OKAY;
+}
+#endif
diff --git a/tommath.def b/tommath.def
index f0a9703..1af4e9e 100644
--- a/tommath.def
+++ b/tommath.def
@@ -47,6 +47,7 @@ EXPORTS
     mp_get_mag_u64
     mp_get_mag_ul
     mp_grow
+    mp_hash
     mp_init
     mp_init_copy
     mp_init_i32
diff --git a/tommath.h b/tommath.h
index 25a166e..44c92b2 100644
--- a/tommath.h
+++ b/tommath.h
@@ -488,6 +488,15 @@ mp_err mp_reduce_2k_l(mp_int *a, const mp_int *n, const mp_int *d) MP_WUR;
 /* Y = G**X (mod P) */
 mp_err mp_exptmod(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y) MP_WUR;
 
+#if defined(MP_16BIT)
+typedef uint32_t mp_hval;
+#else
+typedef uint64_t mp_hval;
+#endif
+
+/* computes hash */
+mp_err mp_hash(mp_int *a, mp_hval *hash) MP_WUR;
+
 /* ---> Primes <--- */
 
 /* performs one Fermat test of "a" using base "b".
diff --git a/tommath_class.h b/tommath_class.h
index da9c5ea..becbcb1 100644
--- a/tommath_class.h
+++ b/tommath_class.h
@@ -53,6 +53,7 @@
 #   define MP_GET_MAG_U64_C
 #   define MP_GET_MAG_UL_C
 #   define MP_GROW_C
+#   define MP_HASH_C
 #   define MP_INIT_C
 #   define MP_INIT_COPY_C
 #   define MP_INIT_I32_C
@@ -386,6 +387,9 @@
 #   define S_MP_ZERO_DIGS_C
 #endif
 
+#if defined(MP_HASH_C)
+#endif
+
 #if defined(MP_INIT_C)
 #endif