// 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; }