191 lines
6.4 KiB
C
191 lines
6.4 KiB
C
// vim:fileencoding=utf-8:foldmethod=marker
|
|
|
|
#include "siphash.h"
|
|
#include "hasher.h"
|
|
#include "../../../common/aliases/aliases.h"
|
|
#include "../../../common/assert/assert.h"
|
|
#include "../../../common/misc/misc_utils.h"
|
|
#include "../../../prng/xorshift/xorshift.h"
|
|
|
|
wp_intern inline void siphash(WpStream *bytes, const WpHasherCtx *ctx, u8 *out, u64 outlen);
|
|
wp_intern inline void validateSipHasherCtx(const WpHasherCtx *ctx);
|
|
wp_intern inline b8 checkRounds(const WpHasherCtx *ctx);
|
|
|
|
#define _u32To8LE(p, v) \
|
|
(p)[0] = (u8)((v)); \
|
|
(p)[1] = (u8)((v) >> 8); \
|
|
(p)[2] = (u8)((v) >> 16); \
|
|
(p)[3] = (u8)((v) >> 24);
|
|
|
|
#define _u64To8LE(p, v) \
|
|
_u32To8LE((p), (u32)((v))); \
|
|
_u32To8LE((p) + 4, (u32)((v) >> 32));
|
|
|
|
#define _u8To64LE(p) \
|
|
(((u64)((p)[0])) | ((u64)((p)[1]) << 8) | \
|
|
((u64)((p)[2]) << 16) | ((u64)((p)[3]) << 24) | \
|
|
((u64)((p)[4]) << 32) | ((u64)((p)[5]) << 40) | \
|
|
((u64)((p)[6]) << 48) | ((u64)((p)[7]) << 56))
|
|
|
|
#define _sipRound \
|
|
do { \
|
|
v0 += v1; \
|
|
v1 = wpMiscUtilsRotl64(v1, 13); \
|
|
v1 ^= v0; \
|
|
v0 = wpMiscUtilsRotl64(v0, 32); \
|
|
v2 += v3; \
|
|
v3 = wpMiscUtilsRotl64(v3, 16); \
|
|
v3 ^= v2; \
|
|
v0 += v3; \
|
|
v3 = wpMiscUtilsRotl64(v3, 21); \
|
|
v3 ^= v0; \
|
|
v2 += v1; \
|
|
v1 = wpMiscUtilsRotl64(v1, 17); \
|
|
v1 ^= v2; \
|
|
v2 = wpMiscUtilsRotl64(v2, 32); \
|
|
} while (0)
|
|
|
|
WpHasher wpSip24Hasher(void) {
|
|
WpXor256State state = wpPrngXorshiftInit();
|
|
WpU128 key = { .value = { wpPrngXorshift256(&state), wpPrngXorshift256(&state) } };
|
|
return wpSip24HasherWithKey(key);
|
|
}
|
|
|
|
WpHasher wpSip48Hasher(void) {
|
|
WpXor256State state = wpPrngXorshiftInit();
|
|
WpU128 key = { .value = { wpPrngXorshift256(&state), wpPrngXorshift256(&state) } };
|
|
return wpSip48HasherWithKey(key);
|
|
}
|
|
|
|
WpHasher wpSip24HasherWithKey(WpU128 key) {
|
|
return (WpHasher){
|
|
.func = wpSipGetHash128,
|
|
.ctx = (WpHasherCtx){
|
|
.type = WP_HASHER_CTX_SIPHASH,
|
|
.siphash = (WpSiphashCtx){ .key = key, .compression = 2, .finalisation = 4 },
|
|
},
|
|
};
|
|
}
|
|
|
|
WpHasher wpSip48HasherWithKey(WpU128 key) {
|
|
return (WpHasher){
|
|
.func = wpSipGetHash128,
|
|
.ctx = (WpHasherCtx){
|
|
.type = WP_HASHER_CTX_SIPHASH,
|
|
.siphash = (WpSiphashCtx){ .key = key, .compression = 4, .finalisation = 8 },
|
|
},
|
|
};
|
|
}
|
|
|
|
WpU128 wpSipGetHash128(WpStream *bytes, WpHasherCtx ctx) {
|
|
validateSipHasherCtx(&ctx);
|
|
WpU128 output = {0};
|
|
siphash(bytes, &ctx, (u8 *)&output.value, sizeof(u64) * 2);
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* Implementation of the 128-variant of the siphash algorithm
|
|
*/
|
|
wp_intern inline void siphash(WpStream *bytes, const WpHasherCtx *ctx, u8 *out, u64 outlen) {
|
|
const u8 *kk = (const u8 *)&ctx->siphash.key;
|
|
u64 inlen = bytes->count * bytes->item_size;
|
|
|
|
wpRuntimeAssert(outlen == 16, "outlen should be 16");
|
|
|
|
u64 v0 = wpU64Const(0x736f6d6570736575);
|
|
u64 v1 = wpU64Const(0x646f72616e646f6d);
|
|
u64 v2 = wpU64Const(0x6c7967656e657261);
|
|
u64 v3 = wpU64Const(0x7465646279746573);
|
|
u64 k0 = _u8To64LE(kk);
|
|
u64 k1 = _u8To64LE(kk + 8);
|
|
u64 m;
|
|
u64 i;
|
|
const i32 left = inlen & 7;
|
|
u64 b = ((u64)inlen) << 56;
|
|
v3 ^= k1;
|
|
v2 ^= k0;
|
|
v1 ^= k1;
|
|
v0 ^= k0;
|
|
|
|
v1 ^= 0xee;
|
|
|
|
u8 *data;
|
|
while (wpStreamHasCount(bytes, sizeof(u64))) {
|
|
data = wpStreamConsumeCount(u8, bytes, sizeof(u64));
|
|
m = _u8To64LE(data);
|
|
v3 ^= m;
|
|
|
|
for (i = 0; i < ctx->siphash.compression; ++i) {
|
|
_sipRound;
|
|
}
|
|
|
|
v0 ^= m;
|
|
}
|
|
|
|
data = wpStreamConsume(u8, bytes);
|
|
switch (left) {
|
|
case 7:
|
|
b |= ((u64)data[6]) << 48;
|
|
/* FALLTHRU */
|
|
case 6:
|
|
b |= ((u64)data[5]) << 40;
|
|
/* FALLTHRU */
|
|
case 5:
|
|
b |= ((u64)data[4]) << 32;
|
|
/* FALLTHRU */
|
|
case 4:
|
|
b |= ((u64)data[3]) << 24;
|
|
/* FALLTHRU */
|
|
case 3:
|
|
b |= ((u64)data[2]) << 16;
|
|
/* FALLTHRU */
|
|
case 2:
|
|
b |= ((u64)data[1]) << 8;
|
|
/* FALLTHRU */
|
|
case 1:
|
|
b |= ((u64)data[0]);
|
|
break;
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
v3 ^= b;
|
|
|
|
for (i = 0; i < ctx->siphash.compression; ++i) {
|
|
_sipRound;
|
|
}
|
|
|
|
v0 ^= b;
|
|
|
|
v2 ^= 0xee;
|
|
|
|
for (i = 0; i < ctx->siphash.finalisation; ++i) {
|
|
_sipRound;
|
|
}
|
|
|
|
b = v0 ^ v1 ^ v2 ^ v3;
|
|
_u64To8LE(out, b);
|
|
|
|
v1 ^= 0xdd;
|
|
|
|
for (i = 0; i < ctx->siphash.finalisation; ++i) {
|
|
_sipRound;
|
|
}
|
|
|
|
b = v0 ^ v1 ^ v2 ^ v3;
|
|
_u64To8LE(out + 8, b);
|
|
}
|
|
|
|
wp_intern inline void validateSipHasherCtx(const WpHasherCtx *ctx) {
|
|
b8 magic_match = ctx->type == WP_HASHER_CTX_SIPHASH;
|
|
b8 rounds = checkRounds(ctx);
|
|
wpRuntimeAssert(magic_match && rounds, "Invalid Siphash context");
|
|
}
|
|
|
|
wp_intern inline b8 checkRounds(const WpHasherCtx *ctx) {
|
|
b8 rounds24 = ctx->siphash.compression == 2 && ctx->siphash.finalisation == 4;
|
|
b8 rounds48 = ctx->siphash.compression == 4 && ctx->siphash.finalisation == 8;
|
|
return rounds24 || rounds48;
|
|
}
|