CVE-2021-41168 (published 2021-10-21T20:15Z)

Snudown is a reddit-specific fork of the Sundown Markdown parser used by GitHub, with Python integration added. In affected versions snudown was found to be vulnerable to denial of service attacks to its reference table implementation. References written in markdown ` [reference_name]: https://www.example.com` are inserted into a hash table which was found to have a weak hash function, meaning that an attacker can reliably generate a large number of collisions for it. This makes the hash table vulnerable to a hash-collision DoS attack, a type of algorithmic complexity attack. Further the hash table allowed for duplicate entries resulting in long retrieval times. Proofs of concept and further discussion of the hash collision issue are discussed on the snudown GHSA(https://github.com/reddit/snudown/security/advisories/GHSA-6gvv-9q92-w5f6). Users are advised to update to version 1.7.0.

Severity: MEDIUM
CVSS v2 base score: 4.0 (AV:N/AC:L/Au:S/C:N/I:N/A:P)
CVSS v3 base score: 6.5, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-407
cwe_name: Inefficient Algorithmic Complexity
description: An algorithm in a product has an inefficient worst-case computational complexity that may be detrimental to system performance and can be triggered by an attacker, typically using crafted manipulations that ensure that the worst case is being reached.
extended_description: 
            
         
         

Fix information:
repo_url: https://github.com/reddit/snudown
commit_details: repo_url: https://github.com/reddit/snudown
author: Spencer Koch
author_date: 2021-10-13 09:22:44-05:00
msg: Hash-collision denial-of-service vulnerabilities (#87)

* Add case insensitive SipHash implementation
* Replace ref hash function with SipHash
* Add label to link_ref struct.
* Update find_link_ref to compare link labels as well as hashes
* Update add_link_ref to disallow duplicate entries.
* cast to char from uint8_t for strncasecmp
* update README markdown, remove TODO
* add py2 wheel generation
* fix: add logic for older glibc not having getrandom, impacting staging

Co-authored-by: Nicolaas <nweidema@usc.edu>
repository: repo_url: https://github.com/reddit/snudown
repo_name: reddit/snudown
description: reddit's markdown renderer. based on sundown.
repo_language: C
owner: reddit
file_changes: filename: Dockerfile.wheel
change_type: ModificationType.MODIFY
diff: @@ -4,6 +4,9 @@ ENV DEBIAN_FRONTEND noninteractive
 
 RUN apt-get update && \
     apt-get install -y \
+        python \
+        python-dev \
+        python-pip \
         build-essential \
         gperf \
         software-properties-common
@@ -15,4 +18,4 @@ RUN mkdir -p $SRC_DIR $WHEEL_OUTPUT_DIR
 WORKDIR $SRC_DIR
 
 ADD . $SRC_DIR
-CMD pip3 wheel --wheel-dir=$WHEEL_OUTPUT_DIR .
+CMD pip3 wheel --wheel-dir=$WHEEL_OUTPUT_DIR . ; pip2 wheel --wheel-dir=$WHEEL_OUTPUT_DIR .

programming_language: Shell
filename: Makefile
change_type: ModificationType.MODIFY
diff: @@ -1,10 +1,11 @@
 PACKAGE := snudown
-VERSION := 1.6.0
+VERSION := $(shell grep -oP 'SNUDOWN_VERSION "\K\d.\d.\d' snudown.c)
 DOCKERFILE := Dockerfile.wheel
 
-default: build run
+default: clean build run
 
 build:
+	echo $(VERSION)
 	docker build \
     -t $(PACKAGE):$(VERSION) \
     -f $(DOCKERFILE) \
@@ -17,3 +18,7 @@ run:
 		-v `pwd`/dist:/tmp/dist \
 		-it \
     $(PACKAGE):$(VERSION)
+
+clean:
+	rm -rf build
+	rm -rf dist

programming_language: Shell
filename: README.markdown
change_type: ModificationType.MODIFY
diff: @@ -10,18 +10,22 @@ Setup for development on Mac OS X
 1. From `~/src/snudown` run `$ python setup.py build`
 2. If this is successful, there will now be a `snudown.so` file in the `/snudown/build/lib.< os info >-< python version number>` directory
 3. From within the `/lib.< os info >-< python version number>` directory, start a python interpreter
-    <!-- Make sure you can import snudown -->
-    >>> import snudown
-    <!-- verify that the build you just made is being used -->
-    >>> print(snudown.__file__)
-    snudown.so
-    <!-- Test the functionality of the build -->
-    >>> snudown.markdown('[hi](http://www.reddit.com)')
-    '<p><a href="http://www.reddit.com">hi</a></p>\n'
-    <!-- Great! You can exit now. -->
-    >>> quit()
+```
+<!-- Make sure you can import snudown -->
+>>> import snudown
+<!-- verify that the build you just made is being used -->
+>>> print(snudown.__file__)
+snudown.so
+<!-- Test the functionality of the build -->
+>>> snudown.markdown('[hi](http://www.reddit.com)')
+'<p><a href="http://www.reddit.com">hi</a></p>\n'
+<!-- Great! You can exit now. -->
+>>> quit()
+```
 4. Verify that the tests pass
-     $ PYTHONPATH="$(pwd)" python ../../test_snudown.py
+```
+$ PYTHONPATH="$(pwd)" python ../../test_snudown.py
+```
 5. Verify that all the previous steps work for both Python 2 AND Python 3
 
 

programming_language: Markdown
filename: snudown.c
change_type: ModificationType.MODIFY
diff: @@ -5,7 +5,7 @@
 #include "html.h"
 #include "autolink.h"
 
-#define SNUDOWN_VERSION "1.6.0"
+#define SNUDOWN_VERSION "1.7.0"
 
 enum snudown_renderer_mode {
 	RENDERER_USERTEXT = 0,

programming_language: C
filename: markdown.c
change_type: ModificationType.MODIFY
diff: @@ -19,12 +19,20 @@
 
 #include "markdown.h"
 #include "stack.h"
+#include "siphash.h"
 
 #include <assert.h>
 #include <string.h>
 #include <ctype.h>
 #include <stdio.h>
 
+#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 25
+#include <sys/random.h>
+#else
+# define getrandom backport_getrandom
+# include <sys/syscall.h>
+#endif
+
 #if defined(_WIN32)
 #define strncasecmp	_strnicmp
 #endif
@@ -51,6 +59,7 @@ struct link_ref {
 	unsigned int id;
 
 	struct buf *link;
+	struct buf *label;
 	struct buf *title;
 
 	struct link_ref *next;
@@ -123,10 +132,18 @@ struct sd_markdown {
 	int in_link_body;
 };
 
+int sip_hash_key_init = 0;
+uint8_t sip_hash_key[SIP_HASH_KEY_LEN];
+
 /***************************
  * HELPER FUNCTIONS *
  ***************************/
 
+int backport_getrandom(void *buf, size_t buflen, unsigned int flags)
+{
+	return (int)syscall(SYS_getrandom, buf, buflen, flags);
+}
+
 static inline struct buf *
 rndr_newbuf(struct sd_markdown *rndr, int type)
 {
@@ -175,13 +192,7 @@ unscape_text(struct buf *ob, struct buf *src)
 static unsigned int
 hash_link_ref(const uint8_t *link_ref, size_t length)
 {
-	size_t i;
-	unsigned int hash = 0;
-
-	for (i = 0; i < length; ++i)
-		hash = tolower(link_ref[i]) + (hash << 6) + (hash << 16) - hash;
-
-	return hash;
+	return siphash_nocase(link_ref, length, sip_hash_key);
 }
 
 static struct link_ref *
@@ -189,12 +200,27 @@ add_link_ref(
 	struct link_ref **references,
 	const uint8_t *name, size_t name_size)
 {
-	struct link_ref *ref = calloc(1, sizeof(struct link_ref));
+	unsigned int hash;
+	struct link_ref *ref;
+	hash = hash_link_ref(name, name_size);
+	ref = references[hash % REF_TABLE_SIZE];
+	while (ref != NULL) {
+		/* If a reference with the same label exists already, replace it with the new reference */
+		if (ref->id == hash && ref->label->size == name_size) {
+			if (strncasecmp((char *)ref->label->data, (char *) name, name_size) == 0) {
+				bufrelease(ref->label);
+				bufrelease(ref->link);
+				bufrelease(ref->title);
+				return ref;
+			}
+		}
 
+		ref = ref->next;
+	}
+	ref = calloc(1, sizeof(struct link_ref));
 	if (!ref)
 		return NULL;
-
-	ref->id = hash_link_ref(name, name_size);
+	ref->id = hash;
 	ref->next = references[ref->id % REF_TABLE_SIZE];
 
 	references[ref->id % REF_TABLE_SIZE] = ref;
@@ -210,8 +236,11 @@ find_link_ref(struct link_ref **references, uint8_t *name, size_t length)
 	ref = references[hash % REF_TABLE_SIZE];
 
 	while (ref != NULL) {
-		if (ref->id == hash)
-			return ref;
+		if (ref->id == hash && ref->label->size == length) {
+			if (strncasecmp((char *)ref->label->data, (char *) name, length) == 0) {
+				return ref;
+			}
+		}
 
 		ref = ref->next;
 	}
@@ -230,6 +259,7 @@ free_link_refs(struct link_ref **references)
 
 		while (r) {
 			next = r->next;
+			bufrelease(r->label);
 			bufrelease(r->link);
 			bufrelease(r->title);
 			free(r);
@@ -2565,6 +2595,8 @@ is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_re
 		if (!ref)
 			return 0;
 
+		ref->label = bufnew(id_end - id_offset);
+		bufput(ref->label, data + id_offset, id_end - id_offset);
 		ref->link = bufnew(link_end - link_offset);
 		bufput(ref->link, data + link_offset, link_end - link_offset);
 
@@ -2622,6 +2654,12 @@ sd_markdown_new(
 	if (!md)
 		return NULL;
 
+	if (!sip_hash_key_init) {
+		if (getrandom(sip_hash_key, SIP_HASH_KEY_LEN, 0) < SIP_HASH_KEY_LEN)
+			return NULL;
+		sip_hash_key_init = 1;
+	}
+
 	memcpy(&md->cb, callbacks, sizeof(struct sd_callbacks));
 
 	stack_init(&md->work_bufs[BUFFER_BLOCK], 4);

programming_language: C
filename: siphash.c
change_type: ModificationType.ADD
diff: @@ -0,0 +1,329 @@
+/*
+   SipHash reference C implementation
+
+   Copyright (c) 2012-2016 Jean-Philippe Aumasson
+   <jeanphilippe.aumasson@gmail.com>
+   Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
+   Copyright (c) 2017 Salvatore Sanfilippo <antirez@gmail.com>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along
+   with this software. If not, see
+   <http://creativecommons.org/publicdomain/zero/1.0/>.
+
+   ----------------------------------------------------------------------------
+
+   This version was modified by Salvatore Sanfilippo <antirez@gmail.com>
+   in the following ways:
+
+   1. Hard-code 2-4 rounds in the hope the compiler can optimize it more
+      in this raw from. Anyway we always want the standard 2-4 variant.
+   2. Modify the prototype and implementation so that the function directly
+      returns an uint64_t value, the hash itself, instead of receiving an
+      output buffer. This also means that the output size is set to 8 bytes
+      and the 16 bytes output code handling was removed.
+   3. Provide a case insensitive variant to be used when hashing strings that
+      must be considered identical by the hash table regardless of the case.
+      If we don't have directly a case insensitive hash function, we need to
+      perform a text transformation in some temporary buffer, which is costly.
+   4. Remove debugging code.
+   5. Modified the original test.c file to be a stand-alone function testing
+      the function in the new form (returing an uint64_t) using just the
+      relevant test vector.
+ */
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
+
+#define U32TO8_LE(p, v)                                                        \
+    (p)[0] = (uint8_t)((v));                                                   \
+    (p)[1] = (uint8_t)((v) >> 8);                                              \
+    (p)[2] = (uint8_t)((v) >> 16);                                             \
+    (p)[3] = (uint8_t)((v) >> 24);
+
+#define U64TO8_LE(p, v)                                                        \
+    U32TO8_LE((p), (uint32_t)((v)));                                           \
+    U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
+
+#define U8TO64_LE(p)                                                           \
+    (((uint64_t)((p)[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))
+
+#define U8TO64_LE_NOCASE(p)                                                    \
+    (((uint64_t)(tolower((p)[0]))) |                                           \
+     ((uint64_t)(tolower((p)[1])) << 8) |                                      \
+     ((uint64_t)(tolower((p)[2])) << 16) |                                     \
+     ((uint64_t)(tolower((p)[3])) << 24) |                                     \
+     ((uint64_t)(tolower((p)[4])) << 32) |                                              \
+     ((uint64_t)(tolower((p)[5])) << 40) |                                              \
+     ((uint64_t)(tolower((p)[6])) << 48) |                                              \
+     ((uint64_t)(tolower((p)[7])) << 56))
+
+#define SIPROUND                                                               \
+    do {                                                                       \
+        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);                                                     \
+    } while (0)
+
+uint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k) {
+    uint64_t hash;
+    uint8_t *out = (uint8_t*) &hash;
+    uint64_t v0 = 0x736f6d6570736575ULL;
+    uint64_t v1 = 0x646f72616e646f6dULL;
+    uint64_t v2 = 0x6c7967656e657261ULL;
+    uint64_t v3 = 0x7465646279746573ULL;
+    uint64_t k0 = U8TO64_LE(k);
+    uint64_t k1 = U8TO64_LE(k + 8);
+    uint64_t m;
+    const uint8_t *end = in + 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;
+
+    for (; in != end; in += 8) {
+        m = U8TO64_LE(in);
+        v3 ^= m;
+
+        SIPROUND;
+        SIPROUND;
+
+        v0 ^= m;
+    }
+
+    switch (left) {
+    case 7: b |= ((uint64_t)in[6]) << 48;
+    case 6: b |= ((uint64_t)in[5]) << 40;
+    case 5: b |= ((uint64_t)in[4]) << 32;
+    case 4: b |= ((uint64_t)in[3]) << 24;
+    case 3: b |= ((uint64_t)in[2]) << 16;
+    case 2: b |= ((uint64_t)in[1]) << 8;
+    case 1: b |= ((uint64_t)in[0]); break;
+    case 0: break;
+    }
+
+    v3 ^= b;
+
+    SIPROUND;
+    SIPROUND;
+
+    v0 ^= b;
+    v2 ^= 0xff;
+
+    SIPROUND;
+    SIPROUND;
+    SIPROUND;
+    SIPROUND;
+
+    b = v0 ^ v1 ^ v2 ^ v3;
+    U64TO8_LE(out, b);
+
+    return hash;
+}
+
+uint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k)
+{
+    uint64_t hash;
+    uint8_t *out = (uint8_t*) &hash;
+    uint64_t v0 = 0x736f6d6570736575ULL;
+    uint64_t v1 = 0x646f72616e646f6dULL;
+    uint64_t v2 = 0x6c7967656e657261ULL;
+    uint64_t v3 = 0x7465646279746573ULL;
+    uint64_t k0 = U8TO64_LE(k);
+    uint64_t k1 = U8TO64_LE(k + 8);
+    uint64_t m;
+    const uint8_t *end = in + 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;
+
+    for (; in != end; in += 8) {
+        m = U8TO64_LE_NOCASE(in);
+        v3 ^= m;
+
+        SIPROUND;
+        SIPROUND;
+
+        v0 ^= m;
+    }
+
+    switch (left) {
+    case 7: b |= ((uint64_t)tolower(in[6])) << 48;
+    case 6: b |= ((uint64_t)tolower(in[5])) << 40;
+    case 5: b |= ((uint64_t)tolower(in[4])) << 32;
+    case 4: b |= ((uint64_t)tolower(in[3])) << 24;
+    case 3: b |= ((uint64_t)tolower(in[2])) << 16;
+    case 2: b |= ((uint64_t)tolower(in[1])) << 8;
+    case 1: b |= ((uint64_t)tolower(in[0])); break;
+    case 0: break;
+    }
+
+    v3 ^= b;
+
+    SIPROUND;
+    SIPROUND;
+
+    v0 ^= b;
+    v2 ^= 0xff;
+
+    SIPROUND;
+    SIPROUND;
+    SIPROUND;
+    SIPROUND;
+
+    b = v0 ^ v1 ^ v2 ^ v3;
+    U64TO8_LE(out, b);
+
+    return hash;
+}
+
+
+/* --------------------------------- TEST ------------------------------------ */
+
+#ifdef SIPHASH_TEST
+
+const uint8_t vectors_sip64[64][8] = {
+    { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, },
+    { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, },
+    { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, },
+    { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, },
+    { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, },
+    { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, },
+    { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, },
+    { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, },
+    { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, },
+    { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, },
+    { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, },
+    { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, },
+    { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, },
+    { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, },
+    { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, },
+    { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, },
+    { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, },
+    { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, },
+    { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, },
+    { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, },
+    { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, },
+    { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, },
+    { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, },
+    { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, },
+    { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, },
+    { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, },
+    { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, },
+    { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, },
+    { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, },
+    { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, },
+    { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, },
+    { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, },
+    { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, },
+    { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, },
+    { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, },
+    { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, },
+    { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, },
+    { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, },
+    { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, },
+    { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, },
+    { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, },
+    { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, },
+    { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, },
+    { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, },
+    { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, },
+    { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, },
+    { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, },
+    { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, },
+    { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, },
+    { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, },
+    { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, },
+    { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, },
+    { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, },
+    { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, },
+    { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, },
+    { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, },
+    { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, },
+    { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, },
+    { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, },
+    { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, },
+    { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, },
+    { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, },
+    { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, },
+    { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, },
+};
+
+
+/* Test siphash using a test vector. Returns 0 if the function passed
+ * all the tests, otherwise 1 is returned. */
+int siphash_test(void) {
+    uint8_t in[64], k[16];
+    int i;
+    int fails = 0;
+
+    for (i = 0; i < 16; ++i)
+        k[i] = i;
+
+    for (i = 0; i < 64; ++i) {
+        in[i] = i;
+        uint64_t hash = siphash(in, i, k);
+        const uint8_t *v = NULL;
+        v = (uint8_t *)vectors_sip64;
+        if (memcmp(&hash, v + (i * 8), 8)) {
+            /* printf("fail for %d bytes\n", i); */
+            fails++;
+        }
+    }
+
+    /* Run a few basic tests with the case insensitive version. */
+    uint64_t h1, h2;
+    h1 = siphash((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678");
+    h2 = siphash_nocase((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678");
+    if (h1 != h2) fails++;
+
+    h1 = siphash((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678");
+    h2 = siphash_nocase((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678");
+    if (h1 != h2) fails++;
+
+    h1 = siphash((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678");
+    h2 = siphash_nocase((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678");
+    if (h1 == h2) fails++;
+
+    if (!fails) return 0;
+    return 1;
+}
+
+int main(void) {
+    if (siphash_test() == 0) {
+        printf("SipHash test: OK\n");
+        return 0;
+    } else {
+        printf("SipHash test: FAILED\n");
+        return 1;
+    }
+}
+
+#endif
+

programming_language: C++
filename: siphash.h
change_type: ModificationType.ADD
diff: @@ -0,0 +1,41 @@
+/*
+   SipHash reference C implementation
+
+   Copyright (c) 2012-2016 Jean-Philippe Aumasson
+   <jeanphilippe.aumasson@gmail.com>
+   Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
+   Copyright (c) 2017 Salvatore Sanfilippo <antirez@gmail.com>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along
+   with this software. If not, see
+   <http://creativecommons.org/publicdomain/zero/1.0/>.
+
+   ----------------------------------------------------------------------------
+
+   This version was modified by Salvatore Sanfilippo <antirez@gmail.com>
+   in the following ways:
+
+   1. Hard-code 2-4 rounds in the hope the compiler can optimize it more
+      in this raw from. Anyway we always want the standard 2-4 variant.
+   2. Modify the prototype and implementation so that the function directly
+      returns an uint64_t value, the hash itself, instead of receiving an
+      output buffer. This also means that the output size is set to 8 bytes
+      and the 16 bytes output code handling was removed.
+   3. Provide a case insensitive variant to be used when hashing strings that
+      must be considered identical by the hash table regardless of the case.
+      If we don't have directly a case insensitive hash function, we need to
+      perform a text transformation in some temporary buffer, which is costly.
+   4. Remove debugging code.
+   5. Modified the original test.c file to be a stand-alone function testing
+      the function in the new form (returing an uint64_t) using just the
+      relevant test vector.
+ */
+
+#define SIP_HASH_KEY_LEN 16
+
+uint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k);
+uint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k);

programming_language: Markdown