#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_AMD64) || \ defined(__powerpc64__) || \ 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); } } #else 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; #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_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; }