123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- package siphash
- /*
- Copyright 2022 zhibog
- Made available under the BSD-3 license.
- List of contributors:
- zhibog: Initial implementation.
- Implementation of the SipHash hashing algorithm, as defined at <https://github.com/veorq/SipHash> and <https://www.aumasson.jp/siphash/siphash.pdf>
- Use the specific procedures for a certain setup. The generic procdedures will default to Siphash 2-4
- */
- import "core:crypto"
- import "core:crypto/util"
- /*
- High level API
- */
- KEY_SIZE :: 16
- DIGEST_SIZE :: 8
- // sum_string_1_3 will hash the given message with the key and return
- // the computed hash as a u64
- sum_string_1_3 :: proc(msg, key: string) -> u64 {
- return sum_bytes_1_3(transmute([]byte)(msg), transmute([]byte)(key))
- }
- // sum_bytes_1_3 will hash the given message with the key and return
- // the computed hash as a u64
- sum_bytes_1_3 :: proc (msg, key: []byte) -> u64 {
- ctx: Context
- hash: u64
- init(&ctx, key, 1, 3)
- update(&ctx, msg)
- final(&ctx, &hash)
- return hash
- }
- // sum_string_to_buffer_1_3 will hash the given message with the key and write
- // the computed hash into the provided destination buffer
- sum_string_to_buffer_1_3 :: proc(msg, key: string, dst: []byte) {
- sum_bytes_to_buffer_1_3(transmute([]byte)(msg), transmute([]byte)(key), dst)
- }
- // sum_bytes_to_buffer_1_3 will hash the given message with the key and write
- // the computed hash into the provided destination buffer
- sum_bytes_to_buffer_1_3 :: proc(msg, key, dst: []byte) {
- assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
- hash := sum_bytes_1_3(msg, key)
- _collect_output(dst[:], hash)
- }
- sum_1_3 :: proc {
- sum_string_1_3,
- sum_bytes_1_3,
- sum_string_to_buffer_1_3,
- sum_bytes_to_buffer_1_3,
- }
- // verify_u64_1_3 will check if the supplied tag matches with the output you
- // will get from the provided message and key
- verify_u64_1_3 :: proc (tag: u64 msg, key: []byte) -> bool {
- return sum_bytes_1_3(msg, key) == tag
- }
- // verify_bytes will check if the supplied tag matches with the output you
- // will get from the provided message and key
- verify_bytes_1_3 :: proc (tag, msg, key: []byte) -> bool {
- derived_tag: [8]byte
- sum_bytes_to_buffer_1_3(msg, key, derived_tag[:])
- return crypto.compare_constant_time(derived_tag[:], tag) == 1
- }
- verify_1_3 :: proc {
- verify_bytes_1_3,
- verify_u64_1_3,
- }
- // sum_string_2_4 will hash the given message with the key and return
- // the computed hash as a u64
- sum_string_2_4 :: proc(msg, key: string) -> u64 {
- return sum_bytes_2_4(transmute([]byte)(msg), transmute([]byte)(key))
- }
- // sum_bytes_2_4 will hash the given message with the key and return
- // the computed hash as a u64
- sum_bytes_2_4 :: proc (msg, key: []byte) -> u64 {
- ctx: Context
- hash: u64
- init(&ctx, key, 2, 4)
- update(&ctx, msg)
- final(&ctx, &hash)
- return hash
- }
- // sum_string_to_buffer_2_4 will hash the given message with the key and write
- // the computed hash into the provided destination buffer
- sum_string_to_buffer_2_4 :: proc(msg, key: string, dst: []byte) {
- sum_bytes_to_buffer_2_4(transmute([]byte)(msg), transmute([]byte)(key), dst)
- }
- // sum_bytes_to_buffer_2_4 will hash the given message with the key and write
- // the computed hash into the provided destination buffer
- sum_bytes_to_buffer_2_4 :: proc(msg, key, dst: []byte) {
- assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
- hash := sum_bytes_2_4(msg, key)
- _collect_output(dst[:], hash)
- }
- sum_2_4 :: proc {
- sum_string_2_4,
- sum_bytes_2_4,
- sum_string_to_buffer_2_4,
- sum_bytes_to_buffer_2_4,
- }
- sum_string :: sum_string_2_4
- sum_bytes :: sum_bytes_2_4
- sum_string_to_buffer :: sum_string_to_buffer_2_4
- sum_bytes_to_buffer :: sum_bytes_to_buffer_2_4
- sum :: proc {
- sum_string,
- sum_bytes,
- sum_string_to_buffer,
- sum_bytes_to_buffer,
- }
- // verify_u64_2_4 will check if the supplied tag matches with the output you
- // will get from the provided message and key
- verify_u64_2_4 :: proc (tag: u64 msg, key: []byte) -> bool {
- return sum_bytes_2_4(msg, key) == tag
- }
- // verify_bytes will check if the supplied tag matches with the output you
- // will get from the provided message and key
- verify_bytes_2_4 :: proc (tag, msg, key: []byte) -> bool {
- derived_tag: [8]byte
- sum_bytes_to_buffer_2_4(msg, key, derived_tag[:])
- return crypto.compare_constant_time(derived_tag[:], tag) == 1
- }
- verify_2_4 :: proc {
- verify_bytes_2_4,
- verify_u64_2_4,
- }
- verify_bytes :: verify_bytes_2_4
- verify_u64 :: verify_u64_2_4
- verify :: proc {
- verify_bytes,
- verify_u64,
- }
- // sum_string_4_8 will hash the given message with the key and return
- // the computed hash as a u64
- sum_string_4_8 :: proc(msg, key: string) -> u64 {
- return sum_bytes_4_8(transmute([]byte)(msg), transmute([]byte)(key))
- }
- // sum_bytes_4_8 will hash the given message with the key and return
- // the computed hash as a u64
- sum_bytes_4_8 :: proc (msg, key: []byte) -> u64 {
- ctx: Context
- hash: u64
- init(&ctx, key, 4, 8)
- update(&ctx, msg)
- final(&ctx, &hash)
- return hash
- }
- // sum_string_to_buffer_4_8 will hash the given message with the key and write
- // the computed hash into the provided destination buffer
- sum_string_to_buffer_4_8 :: proc(msg, key: string, dst: []byte) {
- sum_bytes_to_buffer_4_8(transmute([]byte)(msg), transmute([]byte)(key), dst)
- }
- // sum_bytes_to_buffer_4_8 will hash the given message with the key and write
- // the computed hash into the provided destination buffer
- sum_bytes_to_buffer_4_8 :: proc(msg, key, dst: []byte) {
- assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
- hash := sum_bytes_4_8(msg, key)
- _collect_output(dst[:], hash)
- }
- sum_4_8 :: proc {
- sum_string_4_8,
- sum_bytes_4_8,
- sum_string_to_buffer_4_8,
- sum_bytes_to_buffer_4_8,
- }
- // verify_u64_4_8 will check if the supplied tag matches with the output you
- // will get from the provided message and key
- verify_u64_4_8 :: proc (tag: u64 msg, key: []byte) -> bool {
- return sum_bytes_4_8(msg, key) == tag
- }
- // verify_bytes will check if the supplied tag matches with the output you
- // will get from the provided message and key
- verify_bytes_4_8 :: proc (tag, msg, key: []byte) -> bool {
- derived_tag: [8]byte
- sum_bytes_to_buffer_4_8(msg, key, derived_tag[:])
- return crypto.compare_constant_time(derived_tag[:], tag) == 1
- }
- verify_4_8 :: proc {
- verify_bytes_4_8,
- verify_u64_4_8,
- }
- /*
- Low level API
- */
- init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
- assert(len(key) == KEY_SIZE, "crypto/siphash: Invalid key size, want 16")
- ctx.c_rounds = c_rounds
- ctx.d_rounds = d_rounds
- is_valid_setting := (ctx.c_rounds == 1 && ctx.d_rounds == 3) ||
- (ctx.c_rounds == 2 && ctx.d_rounds == 4) ||
- (ctx.c_rounds == 4 && ctx.d_rounds == 8)
- assert(is_valid_setting, "crypto/siphash: Incorrect rounds set up. Valid pairs are (1,3), (2,4) and (4,8)")
- ctx.k0 = util.U64_LE(key[:8])
- ctx.k1 = util.U64_LE(key[8:])
- ctx.v0 = 0x736f6d6570736575 ~ ctx.k0
- ctx.v1 = 0x646f72616e646f6d ~ ctx.k1
- ctx.v2 = 0x6c7967656e657261 ~ ctx.k0
- ctx.v3 = 0x7465646279746573 ~ ctx.k1
- ctx.is_initialized = true
- }
- update :: proc(ctx: ^Context, data: []byte) {
- assert(ctx.is_initialized, "crypto/siphash: Context is not initialized")
- ctx.last_block = len(data) / 8 * 8
- ctx.buf = data
- i := 0
- m: u64
- for i < ctx.last_block {
- m = u64(ctx.buf[i] & 0xff)
- i += 1
- for r in u64(1)..<8 {
- m |= u64(ctx.buf[i] & 0xff) << (r * 8)
- i += 1
- }
- ctx.v3 ~= m
- for _ in 0..<ctx.c_rounds {
- _compress(ctx)
- }
- ctx.v0 ~= m
- }
- }
- final :: proc(ctx: ^Context, dst: ^u64) {
- m: u64
- for i := len(ctx.buf) - 1; i >= ctx.last_block; i -= 1 {
- m <<= 8
- m |= u64(ctx.buf[i] & 0xff)
- }
- m |= u64(len(ctx.buf) << 56)
- ctx.v3 ~= m
- for _ in 0..<ctx.c_rounds {
- _compress(ctx)
- }
- ctx.v0 ~= m
- ctx.v2 ~= 0xff
- for _ in 0..<ctx.d_rounds {
- _compress(ctx)
- }
- dst^ = ctx.v0 ~ ctx.v1 ~ ctx.v2 ~ ctx.v3
- reset(ctx)
- }
- reset :: proc(ctx: ^Context) {
- ctx.k0, ctx.k1 = 0, 0
- ctx.v0, ctx.v1 = 0, 0
- ctx.v2, ctx.v3 = 0, 0
- ctx.last_block = 0
- ctx.c_rounds = 0
- ctx.d_rounds = 0
- ctx.is_initialized = false
- }
- Context :: struct {
- v0, v1, v2, v3: u64, // State values
- k0, k1: u64, // Split key
- c_rounds: int, // Number of message rounds
- d_rounds: int, // Number of finalization rounds
- buf: []byte, // Provided data
- last_block: int, // Offset from the last block
- is_initialized: bool,
- }
- _get_byte :: #force_inline proc "contextless" (byte_num: byte, into: u64) -> byte {
- return byte(into >> (((~byte_num) & (size_of(u64) - 1)) << 3))
- }
- _collect_output :: #force_inline proc "contextless" (dst: []byte, hash: u64) {
- dst[0] = _get_byte(7, hash)
- dst[1] = _get_byte(6, hash)
- dst[2] = _get_byte(5, hash)
- dst[3] = _get_byte(4, hash)
- dst[4] = _get_byte(3, hash)
- dst[5] = _get_byte(2, hash)
- dst[6] = _get_byte(1, hash)
- dst[7] = _get_byte(0, hash)
- }
- _compress :: #force_inline proc "contextless" (ctx: ^Context) {
- ctx.v0 += ctx.v1
- ctx.v1 = util.ROTL64(ctx.v1, 13)
- ctx.v1 ~= ctx.v0
- ctx.v0 = util.ROTL64(ctx.v0, 32)
- ctx.v2 += ctx.v3
- ctx.v3 = util.ROTL64(ctx.v3, 16)
- ctx.v3 ~= ctx.v2
- ctx.v0 += ctx.v3
- ctx.v3 = util.ROTL64(ctx.v3, 21)
- ctx.v3 ~= ctx.v0
- ctx.v2 += ctx.v1
- ctx.v1 = util.ROTL64(ctx.v1, 17)
- ctx.v1 ~= ctx.v2
- ctx.v2 = util.ROTL64(ctx.v2, 32)
- }
|