Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

484 lines (414 sloc) 10.989 kb
#include <string.h>
#include <stdio.h>
#include "siphash.h"
#ifndef SIP_HASH_STREAMING
#define SIP_HASH_STREAMING 1
#endif
#ifdef _WIN32
#define BYTE_ORDER __LITTLE_ENDIAN
#elif !defined BYTE_ORDER
#include <endian.h>
#endif
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN __LITTLE_ENDIAN
#endif
#ifndef BIG_ENDIAN
#define BIG_ENDIAN __BIG_ENDIAN
#endif
#if BYTE_ORDER == LITTLE_ENDIAN
#define lo u32[0]
#define hi u32[1]
#elif BYTE_ORDER == BIG_ENDIAN
#define hi u32[0]
#define lo u32[1]
#else
#error "Only strictly little or big endian supported"
#endif
#ifndef UNALIGNED_WORD_ACCESS
# if defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD86) || \
defined(__mc68020__)
# define UNALIGNED_WORD_ACCESS 1
# endif
#endif
#ifndef UNALIGNED_WORD_ACCESS
# define UNALIGNED_WORD_ACCESS 0
#endif
#define U8TO32_LE(p) \
(((uint32_t)((p)[0]) ) | ((uint32_t)((p)[1]) << 8) | \
((uint32_t)((p)[2]) << 16) | ((uint32_t)((p)[3]) << 24)) \
#define U32TO8_LE(p, v) \
do { \
(p)[0] = (uint8_t)((v) ); \
(p)[1] = (uint8_t)((v) >> 8); \
(p)[2] = (uint8_t)((v) >> 16); \
(p)[3] = (uint8_t)((v) >> 24); \
} while (0)
#ifdef HAVE_UINT64_T
#define U8TO64_LE(p) \
((uint64_t)U8TO32_LE(p) | ((uint64_t)U8TO32_LE((p) + 4)) << 32 )
#define U64TO8_LE(p, v) \
do { \
U32TO8_LE((p), (uint32_t)((v) )); \
U32TO8_LE((p) + 4, (uint32_t)((v) >> 32)); \
} while (0)
#define ROTL64(v, s) \
((v) << (s)) | ((v) >> (64 - (s)))
#define ROTL64_TO(v, s) ((v) = ROTL64((v), (s)))
#define ADD64_TO(v, s) ((v) += (s))
#define XOR64_TO(v, s) ((v) ^= (s))
#define XOR64_INT(v, x) ((v) ^= (x))
#else
#define U8TO64_LE(p) u8to64_le(p)
static inline uint64_t
u8to64_le(const uint8_t *p)
{
uint64_t ret;
ret.lo = U8TO32_LE(p);
ret.hi = U8TO32_LE(p + 4);
return ret;
}
#define U64TO8_LE(p, v) u64to8_le(p, v)
static inline void
u64to8_le(uint8_t *p, uint64_t v)
{
U32TO8_LE(p, v.lo);
U32TO8_LE(p + 4, v.hi);
}
#define ROTL64_TO(v, s) ((s) > 32 ? rotl64_swap(rotl64_to(&(v), (s) - 32)) : \
(s) == 32 ? rotl64_swap(&(v)) : rotl64_to(&(v), (s)))
static inline uint64_t *
rotl64_to(uint64_t *v, unsigned int s)
{
uint32_t uhi = (v->hi << s) | (v->lo >> (32 - s));
uint32_t ulo = (v->lo << s) | (v->hi >> (32 - s));
v->hi = uhi;
v->lo = ulo;
return v;
}
static inline uint64_t *
rotl64_swap(uint64_t *v)
{
uint32_t t = v->lo;
v->lo = v->hi;
v->hi = t;
return v;
}
#define ADD64_TO(v, s) add64_to(&(v), (s))
static inline uint64_t *
add64_to(uint64_t *v, const uint64_t s)
{
v->lo += s.lo;
v->hi += s.hi;
if (v->lo < s.lo) v->hi++;
return v;
}
#define XOR64_TO(v, s) xor64_to(&(v), (s))
static inline uint64_t *
xor64_to(uint64_t *v, const uint64_t s)
{
v->lo ^= s.lo;
v->hi ^= s.hi;
return v;
}
#define XOR64_INT(v, x) ((v).lo ^= (x))
#endif
static const union {
char bin[32];
uint64_t u64[4];
} sip_init_state_bin = {"uespemos""modnarod""arenegyl""setybdet"};
#define sip_init_state sip_init_state_bin.u64
#if SIP_HASH_STREAMING
struct sip_interface_st {
void (*init)(sip_state *s, const uint8_t *key);
void (*update)(sip_state *s, const uint8_t *data, size_t len);
void (*final)(sip_state *s, uint64_t *digest);
};
static void int_sip_init(sip_state *state, const uint8_t *key);
static void int_sip_update(sip_state *state, const uint8_t *data, size_t len);
static void int_sip_final(sip_state *state, uint64_t *digest);
static const sip_interface sip_methods = {
int_sip_init,
int_sip_update,
int_sip_final
};
#endif /* SIP_HASH_STREAMING */
#define SIP_COMPRESS(v0, v1, v2, v3) \
do { \
ADD64_TO((v0), (v1)); \
ADD64_TO((v2), (v3)); \
ROTL64_TO((v1), 13); \
ROTL64_TO((v3), 16); \
XOR64_TO((v1), (v0)); \
XOR64_TO((v3), (v2)); \
ROTL64_TO((v0), 32); \
ADD64_TO((v2), (v1)); \
ADD64_TO((v0), (v3)); \
ROTL64_TO((v1), 17); \
ROTL64_TO((v3), 21); \
XOR64_TO((v1), (v2)); \
XOR64_TO((v3), (v0)); \
ROTL64_TO((v2), 32); \
} while(0)
#if SIP_HASH_STREAMING
static void
int_sip_dump(sip_state *state)
{
int v;
for (v = 0; v < 4; v++) {
#if HAVE_UINT64_T
printf("v%d: %" PRIx64 "\n", v, state->v[v]);
#else
printf("v%d: %" PRIx32 "%.8" PRIx32 "\n", v, state->v[v].hi, state->v[v].lo);
#endif
}
}
static void
int_sip_init(sip_state *state, const uint8_t key[16])
{
uint64_t k0, k1;
k0 = U8TO64_LE(key);
k1 = U8TO64_LE(key + sizeof(uint64_t));
state->v[0] = k0; XOR64_TO(state->v[0], sip_init_state[0]);
state->v[1] = k1; XOR64_TO(state->v[1], sip_init_state[1]);
state->v[2] = k0; XOR64_TO(state->v[2], sip_init_state[2]);
state->v[3] = k1; XOR64_TO(state->v[3], sip_init_state[3]);
}
static inline void
int_sip_round(sip_state *state, int n)
{
int i;
for (i = 0; i < n; i++) {
SIP_COMPRESS(state->v[0], state->v[1], state->v[2], state->v[3]);
}
}
static inline void
int_sip_update_block(sip_state *state, uint64_t m)
{
XOR64_TO(state->v[3], m);
int_sip_round(state, state->c);
XOR64_TO(state->v[0], m);
}
static inline void
int_sip_pre_update(sip_state *state, const uint8_t **pdata, size_t *plen)
{
int to_read;
uint64_t m;
if (!state->buflen) return;
to_read = sizeof(uint64_t) - state->buflen;
memcpy(state->buf + state->buflen, *pdata, to_read);
m = U8TO64_LE(state->buf);
int_sip_update_block(state, m);
*pdata += to_read;
*plen -= to_read;
state->buflen = 0;
}
static inline void
int_sip_post_update(sip_state *state, const uint8_t *data, size_t len)
{
uint8_t r = len % sizeof(uint64_t);
if (r) {
memcpy(state->buf, data + len - r, r);
state->buflen = r;
}
}
static void
int_sip_update(sip_state *state, const uint8_t *data, size_t len)
{
uint64_t *end;
uint64_t *data64;
state->msglen_byte = state->msglen_byte + (len % 256);
data64 = (uint64_t *) data;
int_sip_pre_update(state, &data, &len);
end = data64 + (len / sizeof(uint64_t));
#if BYTE_ORDER == LITTLE_ENDIAN
while (data64 != end) {
int_sip_update_block(state, *data64++);
}
#elif BYTE_ORDER == BIG_ENDIAN
{
uint64_t m;
uint8_t *data8 = data;
for (; data8 != (uint8_t *) end; data8 += sizeof(uint64_t)) {
m = U8TO64_LE(data8);
int_sip_update_block(state, m);
}
}
#endif
int_sip_post_update(state, data, len);
}
static inline void
int_sip_pad_final_block(sip_state *state)
{
int i;
/* pad with 0's and finalize with msg_len mod 256 */
for (i = state->buflen; i < sizeof(uint64_t); i++) {
state->buf[i] = 0x00;
}
state->buf[sizeof(uint64_t) - 1] = state->msglen_byte;
}
static void
int_sip_final(sip_state *state, uint64_t *digest)
{
uint64_t m;
int_sip_pad_final_block(state);
m = U8TO64_LE(state->buf);
int_sip_update_block(state, m);
XOR64_INT(state->v[2], 0xff);
int_sip_round(state, state->d);
*digest = state->v[0];
XOR64_TO(*digest, state->v[1]);
XOR64_TO(*digest, state->v[2]);
XOR64_TO(*digest, state->v[3]);
}
sip_hash *
sip_hash_new(const uint8_t key[16], int c, int d)
{
sip_hash *h = NULL;
if (!(h = (sip_hash *) malloc(sizeof(sip_hash)))) return NULL;
return sip_hash_init(h, key, c, d);
}
sip_hash *
sip_hash_init(sip_hash *h, const uint8_t key[16], int c, int d)
{
h->state->c = c;
h->state->d = d;
h->state->buflen = 0;
h->state->msglen_byte = 0;
h->methods = &sip_methods;
h->methods->init(h->state, key);
return h;
}
int
sip_hash_update(sip_hash *h, const uint8_t *msg, size_t len)
{
h->methods->update(h->state, msg, len);
return 1;
}
int
sip_hash_final(sip_hash *h, uint8_t **digest, size_t* len)
{
uint64_t digest64;
uint8_t *ret;
h->methods->final(h->state, &digest64);
if (!(ret = (uint8_t *)malloc(sizeof(uint64_t)))) return 0;
U64TO8_LE(ret, digest64);
*len = sizeof(uint64_t);
*digest = ret;
return 1;
}
int
sip_hash_final_integer(sip_hash *h, uint64_t *digest)
{
h->methods->final(h->state, digest);
return 1;
}
int
sip_hash_digest(sip_hash *h, const uint8_t *data, size_t data_len, uint8_t **digest, size_t *digest_len)
{
if (!sip_hash_update(h, data, data_len)) return 0;
return sip_hash_final(h, digest, digest_len);
}
int
sip_hash_digest_integer(sip_hash *h, const uint8_t *data, size_t data_len, uint64_t *digest)
{
if (!sip_hash_update(h, data, data_len)) return 0;
return sip_hash_final_integer(h, digest);
}
void
sip_hash_free(sip_hash *h)
{
free(h);
}
void
sip_hash_dump(sip_hash *h)
{
int_sip_dump(h->state);
}
#endif /* SIP_HASH_STREAMING */
#define SIP_2_ROUND(m, v0, v1, v2, v3) \
do { \
XOR64_TO((v3), (m)); \
SIP_COMPRESS(v0, v1, v2, v3); \
SIP_COMPRESS(v0, v1, v2, v3); \
XOR64_TO((v0), (m)); \
} while (0)
uint64_t
sip_hash24(const uint8_t key[16], const uint8_t *data, size_t len)
{
uint64_t k0, k1;
uint64_t v0, v1, v2, v3;
uint64_t m, last;
const uint8_t *end = data + len - (len % sizeof(uint64_t));
k0 = U8TO64_LE(key);
k1 = U8TO64_LE(key + sizeof(uint64_t));
v0 = k0; XOR64_TO(v0, sip_init_state[0]);
v1 = k1; XOR64_TO(v1, sip_init_state[1]);
v2 = k0; XOR64_TO(v2, sip_init_state[2]);
v3 = k1; XOR64_TO(v3, sip_init_state[3]);
#if BYTE_ORDER == LITTLE_ENDIAN && UNALIGNED_WORD_ACCESS
{
uint64_t *data64 = (uint64_t *)data;
while (data64 != (uint64_t *) end) {
m = *data64++;
SIP_2_ROUND(m, v0, v1, v2, v3);
}
}
#elif BYTE_ORDER == BIG_ENDIAN
for (; data != end; data += sizeof(uint64_t)) {
m = U8TO64_LE(data);
SIP_2_ROUND(m, v0, v1, v2, v3);
}
#endif
#ifdef HAVE_UINT64_T
last = (uint64_t)len << 56;
#define OR_BYTE(n) (last |= ((uint64_t) end[n]) << ((n) * 8))
#else
last.hi = len << 24;
last.lo = 0;
#define OR_BYTE(n) do { \
if (n >= 4) \
last.hi |= ((uint32_t) end[n]) << ((n) >= 4 ? (n) * 8 - 32 : 0); \
else \
last.lo |= ((uint32_t) end[n]) << ((n) >= 4 ? 0 : (n) * 8); \
} while (0)
#endif
switch (len % sizeof(uint64_t)) {
case 7:
OR_BYTE(6);
case 6:
OR_BYTE(5);
case 5:
OR_BYTE(4);
case 4:
#if BYTE_ORDER == LITTLE_ENDIAN && UNALIGNED_WORD_ACCESS
#if HAVE_UINT64_T
last |= (uint64_t) ((uint32_t *) end)[0];
#else
last.lo |= ((uint32_t *) end)[0];
#endif
break;
#elif BYTE_ORDER == BIG_ENDIAN
OR_BYTE(3);
#endif
case 3:
OR_BYTE(2);
case 2:
OR_BYTE(1);
case 1:
OR_BYTE(0);
break;
case 0:
break;
}
SIP_2_ROUND(last, v0, v1, v2, v3);
XOR64_INT(v2, 0xff);
SIP_COMPRESS(v0, v1, v2, v3);
SIP_COMPRESS(v0, v1, v2, v3);
SIP_COMPRESS(v0, v1, v2, v3);
SIP_COMPRESS(v0, v1, v2, v3);
XOR64_TO(v0, v1);
XOR64_TO(v0, v2);
XOR64_TO(v0, v3);
return v0;
}
Jump to Line
Something went wrong with that request. Please try again.