aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2023-12-31 08:49:16 -0300
committerEuAndreh <eu@euandre.org>2025-06-04 20:11:50 -0300
commitbf54d3bc7be151ee3de15e3cc9fabea68369008e (patch)
tree6ee57bf4e6d57f3aaba9155a3088323a446d5df8 /src
parentAdd complete "Makefile" for standard packaging (diff)
downloadsiphash-bf54d3bc7be151ee3de15e3cc9fabea68369008e.tar.gz
siphash-bf54d3bc7be151ee3de15e3cc9fabea68369008e.tar.xz
Rewrite code, but keeping the exact same semantic and behaviourHEADmain
- assert that the generated `siphash.o` code is identical to the original code, available at `tests/assert-identical.sh`; - remove unneeded `#define`s; - rewrite code with the correct indentation, spacing and formatting; - use C99 constructs over C89 (for loop variable declarations inside the parentheses); - fix the public API.
Diffstat (limited to '')
-rw-r--r--src/impl.c180
-rw-r--r--src/impl.h8
-rw-r--r--src/lib.c78
-rw-r--r--src/lib.h15
-rw-r--r--src/main.c12
-rw-r--r--src/meta.h.in3
l---------src/siphash.h1
7 files changed, 297 insertions, 0 deletions
diff --git a/src/impl.c b/src/impl.c
new file mode 100644
index 0000000..5ba4bde
--- /dev/null
+++ b/src/impl.c
@@ -0,0 +1,180 @@
+#include <s.h>
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "impl.h"
+
+
+
+static const int cROUNDS = 2;
+static const int dROUNDS = 4;
+
+
+
+static inline uint64_t
+ROTL(uint64_t x, uint64_t b) {
+ return (x << b) | (x >> (64 - b));
+}
+
+static inline void
+U32TO8_LE(u8 *p, uint32_t v) {
+ p[0] = (u8)(v >> 0);
+ p[1] = (u8)(v >> 8);
+ p[2] = (u8)(v >> 16);
+ p[3] = (u8)(v >> 24);
+}
+
+static inline void
+U64TO8_LE(u8 *p, uint64_t v) {
+ U32TO8_LE(p + 0, (uint32_t)(v >> 0));
+ U32TO8_LE(p + 4, (uint32_t)(v >> 32));
+}
+
+static inline uint64_t
+U8TO64_LE(const u8 *p) {
+ return
+ (((uint64_t)p[0]) << 0) |
+ (((uint64_t)p[1]) << 8) |
+ (((uint64_t)p[2]) << 16) |
+ (((uint64_t)p[3]) << 24) |
+ (((uint64_t)p[4]) << 32) |
+ (((uint64_t)p[5]) << 40) |
+ (((uint64_t)p[6]) << 48) |
+ (((uint64_t)p[7]) << 56);
+}
+
+static inline void
+SIPROUND(uint64_t *v0, uint64_t *v1, uint64_t *v2, uint64_t *v3) {
+ *v0 += *v1;
+ *v1 = ROTL(*v1, 13);
+ *v1 ^= *v0;
+ *v0 = ROTL(*v0, 32);
+ *v2 += *v3;
+ *v3 = ROTL(*v3, 16);
+ *v3 ^= *v2;
+ *v0 += *v3;
+ *v3 = ROTL(*v3, 21);
+ *v3 ^= *v0;
+ *v2 += *v1;
+ *v1 = ROTL(*v1, 17);
+ *v1 ^= *v2;
+ *v2 = ROTL(*v2, 32);
+}
+
+/**
+ Computes a SipHash value:
+
+ - *in: pointer to input data (read-only)
+ - inlen: input data length in bytes (any size_t value)
+ - *key: pointer to the key data (read-only), must be 16 bytes
+ - *out: pointer to output data (write-only), outlen bytes must be allocated
+ - outlen: length of the output in bytes, must be 8 or 16
+*/
+int
+siphash_impl(
+ const void *const restrict in,
+ const size_t inlen,
+ const void *const restrict key,
+ u8 out[16U],
+ const size_t outlen
+) {
+ const unsigned char *ni = (const unsigned char *)in;
+ const unsigned char *kk = (const unsigned char *)key;
+
+ #line 95
+ assert((outlen == 8) || (outlen == 16));
+ #line 89
+ uint64_t v0 = UINT64_C(0x736f6d6570736575);
+ uint64_t v1 = UINT64_C(0x646f72616e646f6d);
+ uint64_t v2 = UINT64_C(0x6c7967656e657261);
+ uint64_t v3 = UINT64_C(0x7465646279746573);
+ const uint64_t k0 = U8TO64_LE(kk);
+ const uint64_t k1 = U8TO64_LE(kk + 8);
+ const unsigned char *end = ni + inlen - (inlen % sizeof(uint64_t));
+ const int left = inlen & 7;
+ uint64_t b = ((uint64_t)inlen) << 56;
+ v3 ^= k1;
+ v2 ^= k0;
+ v1 ^= k1;
+ v0 ^= k0;
+
+ if (outlen == 16) {
+ v1 ^= 0xee;
+ }
+
+ while (ni != end) {
+ uint64_t m = U8TO64_LE(ni);
+ v3 ^= m;
+
+ for (int i = 0; i < cROUNDS; i++) {
+ SIPROUND(&v0, &v1, &v2, &v3);
+ }
+
+ v0 ^= m;
+ ni += 8;
+ }
+
+ switch (left) {
+ case 7:
+ b |= ((uint64_t)ni[6]) << 48;
+ // fallthrough
+ case 6:
+ b |= ((uint64_t)ni[5]) << 40;
+ // fallthrough
+ case 5:
+ b |= ((uint64_t)ni[4]) << 32;
+ // fallthrough
+ case 4:
+ b |= ((uint64_t)ni[3]) << 24;
+ // fallthrough
+ case 3:
+ b |= ((uint64_t)ni[2]) << 16;
+ // fallthrough
+ case 2:
+ b |= ((uint64_t)ni[1]) << 8;
+ // fallthrough
+ case 1:
+ b |= ((uint64_t)ni[0]);
+ break;
+ case 0:
+ break;
+ }
+
+ v3 ^= b;
+
+ for (int i = 0; i < cROUNDS; i++) {
+ SIPROUND(&v0, &v1, &v2, &v3);
+ }
+
+ v0 ^= b;
+
+ if (outlen == 16) {
+ v2 ^= 0xee;
+ } else {
+ v2 ^= 0xff;
+ }
+
+ for (int i = 0; i < dROUNDS; i++) {
+ SIPROUND(&v0, &v1, &v2, &v3);
+ }
+
+ b = v0 ^ v1 ^ v2 ^ v3;
+ U64TO8_LE(out, b);
+
+ if (outlen == 8) {
+ return 0;
+ }
+
+ v1 ^= 0xdd;
+
+ for (int i = 0; i < dROUNDS; i++) {
+ SIPROUND(&v0, &v1, &v2, &v3);
+ }
+
+ b = v0 ^ v1 ^ v2 ^ v3;
+ U64TO8_LE(out + 8, b);
+
+ return 0;
+}
diff --git a/src/impl.h b/src/impl.h
new file mode 100644
index 0000000..166ccae
--- /dev/null
+++ b/src/impl.h
@@ -0,0 +1,8 @@
+int
+siphash_impl(
+ const void *const restrict in,
+ const size_t inlen,
+ const void *const restrict key,
+ u8 out[16U],
+ const size_t outlen
+);
diff --git a/src/lib.c b/src/lib.c
new file mode 100644
index 0000000..935ee26
--- /dev/null
+++ b/src/lib.c
@@ -0,0 +1,78 @@
+#include <s.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "impl.h"
+
+#include "lib.h"
+
+
+
+static int
+usage(FILE *restrict stream, const char *const argv0) {
+ return fprintf(stream, "Usage: %s KEY DATA\n", argv0) < 0;
+}
+
+void
+siphash(
+ const u8 key[SIPHASH_KEY_LENGTH],
+ const size_t inlen,
+ const void *const restrict in,
+ u8 out[SIPHASH_OUTPUT_LENGTH]
+) {
+ siphash_impl(in, inlen, key, out, SIPHASH_OUTPUT_LENGTH);
+}
+
+int
+siphash_main(int argc, char *argv[]) {
+ int rc = EXIT_USAGE;
+
+ if (argc != 3) {
+ if (usage(stderr, argv[0])) {
+ rc = EXIT_FAILURE;
+ perror("usage()");
+ goto out;
+ }
+ goto out;
+ }
+
+ const char *const key = argv[1];
+ const char *const data = argv[2];
+ const size_t keylen = strlen(key);
+ const size_t datalen = strlen(data);
+ if (keylen < SIPHASH_KEY_LENGTH) {
+ if (fprintf(
+ stderr,
+ "KEY (%lu) is smaller than SIPHASH_KEY_LENGTH (%d).\n",
+ keylen,
+ SIPHASH_KEY_LENGTH
+ ) < 0) {
+ rc = EXIT_FAILURE;
+ perror("fprintf()");
+ goto out;
+ }
+ goto out;
+ }
+
+ u8 out[SIPHASH_OUTPUT_LENGTH];
+ siphash((u8 *)key, datalen, data, out);
+ for (int i = 0; i < SIPHASH_OUTPUT_LENGTH; i++) {
+ const u8 c = out[i];
+ if (printf("%x", c) < 0) {
+ rc = EXIT_FAILURE;
+ perror("printf()");
+ goto out;
+ }
+ }
+ if (printf("\n") < 0) {
+ rc = EXIT_FAILURE;
+ perror("printf()");
+ goto out;
+ }
+
+ rc = EXIT_SUCCESS;
+out:
+ return rc;
+}
diff --git a/src/lib.h b/src/lib.h
new file mode 100644
index 0000000..ae16870
--- /dev/null
+++ b/src/lib.h
@@ -0,0 +1,15 @@
+enum {
+ SIPHASH_KEY_LENGTH = 16U,
+ SIPHASH_OUTPUT_LENGTH = 16U,
+};
+
+void
+siphash(
+ const u8 key[SIPHASH_KEY_LENGTH],
+ const size_t inlen,
+ const void *const restrict in,
+ u8 out[SIPHASH_OUTPUT_LENGTH]
+);
+
+int
+siphash_main(int argc, char *argv[]);
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..c218d8f
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,12 @@
+#include <s.h>
+
+#include <stddef.h>
+
+#include "siphash.h"
+
+
+
+int
+main(int argc, char *argv[]) {
+ return siphash_main(argc, argv);
+}
diff --git a/src/meta.h.in b/src/meta.h.in
new file mode 100644
index 0000000..9bd8c08
--- /dev/null
+++ b/src/meta.h.in
@@ -0,0 +1,3 @@
+#define VERSION "@VERSION@"
+#define DATE "@DATE@"
+#define NAME "@NAME@"
diff --git a/src/siphash.h b/src/siphash.h
new file mode 120000
index 0000000..a564cc1
--- /dev/null
+++ b/src/siphash.h
@@ -0,0 +1 @@
+lib.h \ No newline at end of file