Permalink
Cannot retrieve contributors at this time
489 lines (418 sloc)
10.9 KB
#include <string.h> | |
#include <stdio.h> | |
#include "siphash.h" | |
#ifndef SIP_HASH_STREAMING | |
#define SIP_HASH_STREAMING 1 | |
#endif | |
#if defined(__MINGW32__) | |
#include <sys/param.h> | |
/* MinGW only defines LITTLE_ENDIAN and BIG_ENDIAN macros */ | |
#define __LITTLE_ENDIAN LITTLE_ENDIAN | |
#define __BIG_ENDIAN BIG_ENDIAN | |
#elif defined(_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_AMD64) || \ | |
defined(__powerpc64__) || defined(__aarch64__) || \ | |
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++) { | |
#ifdef 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_ROUND(m, v0, v1, v2, v3) \ | |
do { \ | |
XOR64_TO((v3), (m)); \ | |
SIP_COMPRESS(v0, v1, v2, v3); \ | |
XOR64_TO((v0), (m)); \ | |
} while (0) | |
uint64_t | |
sip_hash13(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_ROUND(m, v0, v1, v2, v3); | |
} | |
} | |
#else | |
for (; data != end; data += sizeof(uint64_t)) { | |
m = U8TO64_LE(data); | |
SIP_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 | |
#ifdef HAVE_UINT64_T | |
last |= (uint64_t) ((uint32_t *) end)[0]; | |
#else | |
last.lo |= ((uint32_t *) end)[0]; | |
#endif | |
break; | |
#else | |
OR_BYTE(3); | |
#endif | |
case 3: | |
OR_BYTE(2); | |
case 2: | |
OR_BYTE(1); | |
case 1: | |
OR_BYTE(0); | |
break; | |
case 0: | |
break; | |
} | |
SIP_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); | |
XOR64_TO(v0, v1); | |
XOR64_TO(v0, v2); | |
XOR64_TO(v0, v3); | |
return v0; | |
} |