diff --git a/include/cryb/hash.h b/include/cryb/hash.h index ff294fa..f1103ad 100644 --- a/include/cryb/hash.h +++ b/include/cryb/hash.h @@ -42,6 +42,12 @@ const char *cryb_hash_version(void); #define fletcher16_hash cryb_fletcher16_hash #define fletcher32_hash cryb_fletcher32_hash #define fletcher64_hash cryb_fletcher64_hash +#define fnv0_32_hash cryb_fnv0_32_hash +#define fnv0_64_hash cryb_fnv0_64_hash +#define fnv1_32_hash cryb_fnv1_32_hash +#define fnv1_64_hash cryb_fnv1_64_hash +#define fnv1a_32_hash cryb_fnv1a_32_hash +#define fnv1a_64_hash cryb_fnv1a_64_hash #define murmur3_32_hash cryb_murmur3_32_hash #define pearson_hash cryb_pearson_hash #define pearson_hash_str cryb_pearson_hash_str @@ -50,6 +56,12 @@ uint32_t adler32_hash(const void *, size_t); uint16_t fletcher16_hash(const void *, size_t); uint32_t fletcher32_hash(const void *, size_t); uint64_t fletcher64_hash(const void *, size_t); +uint32_t fnv0_32_hash(const void *, size_t); +uint64_t fnv0_64_hash(const void *, size_t); +uint32_t fnv1_32_hash(const void *, size_t); +uint64_t fnv1_64_hash(const void *, size_t); +uint32_t fnv1a_32_hash(const void *, size_t); +uint64_t fnv1a_64_hash(const void *, size_t); uint32_t murmur3_32_hash(const void *, size_t, uint32_t); uint8_t pearson_hash(const void *, size_t); uint8_t pearson_hash_str(const char *); diff --git a/lib/hash/Makefile.am b/lib/hash/Makefile.am index fd2d207..559b4b3 100644 --- a/lib/hash/Makefile.am +++ b/lib/hash/Makefile.am @@ -7,6 +7,7 @@ libcryb_hash_la_SOURCES = \ cryb_fletcher16.c \ cryb_fletcher32.c \ cryb_fletcher64.c \ + cryb_fnv.c \ cryb_murmur3_32.c \ cryb_pearson.c \ \ @@ -15,10 +16,14 @@ libcryb_hash_la_SOURCES = \ dist_man3_MANS = \ cryb_adler32.3 \ cryb_fletcher.3 \ + cryb_fnv.3 \ cryb_pearson.3 \ \ cryb_hash.3 +noinst_HEADERS = \ + cryb_fnv_impl.h + libcryb_hash_la_CFLAGS = \ $(CRYB_CORE_CFLAGS) diff --git a/lib/hash/cryb_fnv.3 b/lib/hash/cryb_fnv.3 new file mode 100644 index 0000000..d3d6512 --- /dev/null +++ b/lib/hash/cryb_fnv.3 @@ -0,0 +1,107 @@ +.\"- +.\" Copyright (c) 2022 Dag-Erling Smørgrav +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS 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 ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd May 15, 2022 +.Dt CRYB_FNV 3 +.Os +.Sh NAME +.Nm cryb_fnv0_32_hash , +.Nm cryb_fnv0_64_hash , +.Nm cryb_fnv1_32_hash , +.Nm cryb_fnv1_64_hash , +.Nm cryb_fnv1a_32_hash , +.Nm cryb_fnv1a_64_hash +.Nd non-cryptographic hash functions +.Sh LIBRARY +.Lb libcryb-hash +.Sh SYNOPSIS +.In stddef.h +.In stdint.h +.In cryb/hash.h +.Ft uint32_t +.Fn cryb_fnv0_32_hash "const void *data" "size_t len" +.Ft uint64_t +.Fn cryb_fnv0_64_hash "const void *data" "size_t len" +.Ft uint32_t +.Fn cryb_fnv1_32_hash "const void *data" "size_t len" +.Ft uint64_t +.Fn cryb_fnv1_64_hash "const void *data" "size_t len" +.Ft uint32_t +.Fn cryb_fnv1a_32_hash "const void *data" "size_t len" +.Ft uint64_t +.Fn cryb_fnv1a_64_hash "const void *data" "size_t len" +.Sh DESCRIPTION +The +.Fn cryb_fnv0_32_hash , +.Fn cryb_fnv1_32_hash , +and +.Fn cryb_fnv1a_32_hash , +functions return a 32-bit hash of the +.Va len +first bytes of the object pointed to by +.Va data +using the Fowler-Noll-Vo (FNV) 0, 1, and 1a algorithms respectively. +.Pp +The +.Fn cryb_fnv0_64_hash , +.Fn cryb_fnv1_64_hash , +and +.Fn cryb_fnv1a_64_hash , +functions return a 64-bit hash of the +.Va len +first bytes of the object pointed to by +.Va data +using the FNV 0, 1, and 1a algorithms respectively. +.Sh IMPLEMENTATION NOTES +The +.In cryb/hash.h +header provides macros which allows these functions to be referred to +without their +.Dq Li cryb_ +prefix. +.Sh SEE ALSO +.Xr cryb_hash 3 +.Sh STANDARDS +.Rs +.%A Fowler, Glenn +.%A Noll, Landon Curt +.%A Vo, Kiem-Phong +.%A Eastlake, Donald +.%A Hansen, Tony +.%T The FNV Non-Cryptographic Hash Algorithm +.%D May 29, 2019 +.%O draft-eastlake-fnv-17 +.%U https://datatracker.ietf.org/doc/html/draft-eastlake-fnv-17 +.Re +.Sh AUTHORS +The +.Fn cryb_pearson_hash +and +.Fn cryb_pearson_hash_str +functions and this manual page were written by +.An Dag-Erling Sm\(/orgrav Aq Mt des@des.no . diff --git a/lib/hash/cryb_fnv.c b/lib/hash/cryb_fnv.c new file mode 100644 index 0000000..362adad --- /dev/null +++ b/lib/hash/cryb_fnv.c @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 2022 Dag-Erling Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS 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 ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cryb/impl.h" + +#include +#include + +#include + +#include "cryb_fnv_impl.h" + +/* + * Implementations of the 32- and 64-bit Fowler-Noll-Vo (FNV) 0, 1, and 1a hashes. + */ + +uint32_t fnv0_32_hash(const void *data, size_t len) +{ + const uint8_t *p; + uint32_t h; + + for (p = data, h = 0; len > 0; ++p, --len) { + h *= FNV_32_PRIME; + h ^= *p; + } + return (h); +} + +uint64_t fnv0_64_hash(const void *data, size_t len) +{ + const uint8_t *p; + uint64_t h; + + for (p = data, h = 0; len > 0; ++p, --len) { + h *= FNV_64_PRIME; + h ^= *p; + } + return (h); +} + +uint32_t fnv1_32_hash(const void *data, size_t len) +{ + const uint8_t *p; + uint32_t h; + + for (p = data, h = FNV_32_OFFSET_BASIS; len > 0; ++p, --len) { + h *= FNV_32_PRIME; + h ^= *p; + } + return (h); +} + +uint64_t fnv1_64_hash(const void *data, size_t len) +{ + const uint8_t *p; + uint64_t h; + + for (p = data, h = FNV_64_OFFSET_BASIS; len > 0; ++p, --len) { + h *= FNV_64_PRIME; + h ^= *p; + } + return (h); +} + +uint32_t fnv1a_32_hash(const void *data, size_t len) +{ + const uint8_t *p; + uint32_t h; + + for (p = data, h = FNV_32_OFFSET_BASIS; len > 0; ++p, --len) { + h ^= *p; + h *= FNV_32_PRIME; + } + return (h); +} + +uint64_t fnv1a_64_hash(const void *data, size_t len) +{ + const uint8_t *p; + uint64_t h; + + for (p = data, h = FNV_64_OFFSET_BASIS; len > 0; ++p, --len) { + h ^= *p; + h *= FNV_64_PRIME; + } + return (h); +} diff --git a/lib/hash/cryb_fnv_impl.h b/lib/hash/cryb_fnv_impl.h new file mode 100644 index 0000000..e266c1a --- /dev/null +++ b/lib/hash/cryb_fnv_impl.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2022 Dag-Erling Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS 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 ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define FNV_32_PRIME 0x01000193UL +#define FNV_32_OFFSET_BASIS 0x811c9dc5UL + +#define FNV_64_PRIME 0x00000100000001B3ULL +#define FNV_64_OFFSET_BASIS 0xcbf29ce484222325ULL + +#define FNV_SIGNATURE "chongo /\\../\\" diff --git a/t/.gitignore b/t/.gitignore index fe24f7d..7cdeb9d 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -12,6 +12,7 @@ /t_endian /t_ffs_fls /t_fletcher +/t_fnv /t_hash /t_hmac_sha1 /t_hmac_sha1_openssl diff --git a/t/Makefile.am b/t/Makefile.am index bc7e5e8..fe8d80d 100644 --- a/t/Makefile.am +++ b/t/Makefile.am @@ -179,9 +179,11 @@ endif CRYB_ENC if CRYB_HASH TESTS += t_hash t_hash_LDADD = $(libt) $(libhash) -TESTS += t_adler t_fletcher t_murmur3_32 t_pearson +TESTS += t_adler t_fletcher t_fnv t_murmur3_32 t_pearson t_adler_LDADD = $(libt) $(libhash) t_fletcher_LDADD = $(libt) $(libhash) +t_fnv_CFLAGS = -I$(top_srcdir)/lib/hash +t_fnv_LDADD = $(libt) $(libhash) t_murmur3_32_LDADD = $(libt) $(libhash) t_pearson_LDADD = $(libt) $(libhash) endif CRYB_HASH diff --git a/t/t_fnv.c b/t/t_fnv.c new file mode 100644 index 0000000..2559bb8 --- /dev/null +++ b/t/t_fnv.c @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 2014-2022 Dag-Erling Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS 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 ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cryb/impl.h" + +#include +#include +#include +#include + +#include + +#include + +#include "cryb_fnv_impl.h" + +struct t_case { + const char *str; + uint32_t h0_32, h1_32, h1a_32; + uint64_t h0_64, h1_64, h1a_64; +}; + +/*************************************************************************** + * Test cases + */ + +/* + * Test vectors from the IETF draft, expanded to also cover FNV-0 and FNV-1. + */ +static struct t_case t_cases[] = { + { + .str = "", + .h0_32 = 0x00000000UL, + .h1_32 = 0x811c9dc5UL, + .h1a_32 = 0x811c9dc5UL, + .h0_64 = 0x0000000000000000ULL, + .h1_64 = 0xcbf29ce484222325ULL, + .h1a_64 = 0xcbf29ce484222325ULL, + }, + { + .str = "a", + .h0_32 = 0x00000061UL, + .h1_32 = 0x050c5d7eUL, + .h1a_32 = 0xe40c292cUL, + .h0_64 = 0x0000000000000061ULL, + .h1_64 = 0xaf63bd4c8601b7beULL, + .h1a_64 = 0xaf63dc4c8601ec8cULL, + }, + { + .str = "foobar", + .h0_32 = 0xb74bb5efUL, + .h1_32 = 0x31f0b262UL, + .h1a_32 = 0xbf9cf968UL, + .h0_64 = 0x0b91ae3f7ccdc5efULL, + .h1_64 = 0x340d8765a4dda9c2ULL, + .h1a_64 = 0x85944171f73967e8ULL, + }, +}; + +/* + * Test case for the 32- and 64-bit offset bases. + */ +static struct t_case t_offset_basis = { + .str = FNV_SIGNATURE, + .h0_32 = FNV_32_OFFSET_BASIS, + .h0_64 = FNV_64_OFFSET_BASIS, +}; + +/*************************************************************************** + * Test functions + */ +#define T_FNV(V, B) \ + static int \ + t_fnv##V##_##B(char **desc, void *arg) \ + { \ + struct t_case *t = arg; \ + uint##B##_t h; \ + \ + if (!**desc) { \ + (void)asprintf(desc, \ + "FNV-" #V "-" #B " \"%s\"", t->str); \ + } \ + h = fnv##V##_##B##_hash(t->str, strlen(t->str)); \ + return (t_compare_x##B(t->h##V##_##B, h)); \ + } + +T_FNV(0, 32) +T_FNV(0, 64) +T_FNV(1, 32) +T_FNV(1, 64) +T_FNV(1a, 32) +T_FNV(1a, 64) + + +/*************************************************************************** + * Boilerplate + */ + +static int +t_prepare(int argc, char *argv[]) +{ + int i, n; + + (void)argc; + (void)argv; + t_add_test(t_fnv0_32, &t_offset_basis, "FNV-32 offset basis"); + t_add_test(t_fnv0_64, &t_offset_basis, "FNV-64 offset basis"); + n = sizeof t_cases / sizeof t_cases[0]; + for (i = 0; i < n; ++i) { + t_add_test(t_fnv0_32, &t_cases[i], ""); + t_add_test(t_fnv0_64, &t_cases[i], ""); + t_add_test(t_fnv1_32, &t_cases[i], ""); + t_add_test(t_fnv1_64, &t_cases[i], ""); + t_add_test(t_fnv1a_32, &t_cases[i], ""); + t_add_test(t_fnv1a_64, &t_cases[i], ""); + } + return (0); +} + +int +main(int argc, char *argv[]) +{ + + t_main(t_prepare, NULL, argc, argv); +}