From 77be7144c373ae28deb97a48822edf3909e7c2bd Mon Sep 17 00:00:00 2001 From: zhibog Date: Thu, 14 Oct 2021 22:20:55 +0200 Subject: [PATCH] Add crypto library. Additional information is included in the README.md --- core/crypto/README.md | 94 + core/crypto/_blake2/_blake2.odin | 2786 +++++++++++++++++++++++ core/crypto/_ctx/_ctx.odin | 78 + core/crypto/_sha3/_sha3.odin | 170 ++ core/crypto/_tiger/_tiger.odin | 411 ++++ core/crypto/blake/blake.odin | 846 +++++++ core/crypto/blake2b/blake2b.odin | 189 ++ core/crypto/blake2s/blake2s.odin | 189 ++ core/crypto/botan/botan.lib | Bin 0 -> 3298832 bytes core/crypto/botan/botan.odin | 480 ++++ core/crypto/botan/hash.odin | 471 ++++ core/crypto/gost/gost.odin | 460 ++++ core/crypto/groestl/groestl.odin | 742 ++++++ core/crypto/haval/haval.odin | 1372 +++++++++++ core/crypto/jh/jh.odin | 673 ++++++ core/crypto/keccak/keccak.odin | 441 ++++ core/crypto/md2/md2.odin | 265 +++ core/crypto/md4/md4.odin | 345 +++ core/crypto/md5/md5.odin | 368 +++ core/crypto/ripemd/ripemd.odin | 1060 +++++++++ core/crypto/sha1/sha1.odin | 329 +++ core/crypto/sha2/sha2.odin | 797 +++++++ core/crypto/sha3/sha3.odin | 440 ++++ core/crypto/shake/shake.odin | 279 +++ core/crypto/skein/skein.odin | 496 ++++ core/crypto/sm3/sm3.odin | 336 +++ core/crypto/streebog/streebog.odin | 602 +++++ core/crypto/tiger/tiger.odin | 340 +++ core/crypto/tiger2/tiger2.odin | 340 +++ core/crypto/util/util.odin | 144 ++ core/crypto/whirlpool/whirlpool.odin | 873 +++++++ tests/core/build.bat | 7 +- tests/core/crypto/test_core_crypto.odin | 1279 +++++++++++ 33 files changed, 17701 insertions(+), 1 deletion(-) create mode 100644 core/crypto/README.md create mode 100644 core/crypto/_blake2/_blake2.odin create mode 100644 core/crypto/_ctx/_ctx.odin create mode 100644 core/crypto/_sha3/_sha3.odin create mode 100644 core/crypto/_tiger/_tiger.odin create mode 100644 core/crypto/blake/blake.odin create mode 100644 core/crypto/blake2b/blake2b.odin create mode 100644 core/crypto/blake2s/blake2s.odin create mode 100644 core/crypto/botan/botan.lib create mode 100644 core/crypto/botan/botan.odin create mode 100644 core/crypto/botan/hash.odin create mode 100644 core/crypto/gost/gost.odin create mode 100644 core/crypto/groestl/groestl.odin create mode 100644 core/crypto/haval/haval.odin create mode 100644 core/crypto/jh/jh.odin create mode 100644 core/crypto/keccak/keccak.odin create mode 100644 core/crypto/md2/md2.odin create mode 100644 core/crypto/md4/md4.odin create mode 100644 core/crypto/md5/md5.odin create mode 100644 core/crypto/ripemd/ripemd.odin create mode 100644 core/crypto/sha1/sha1.odin create mode 100644 core/crypto/sha2/sha2.odin create mode 100644 core/crypto/sha3/sha3.odin create mode 100644 core/crypto/shake/shake.odin create mode 100644 core/crypto/skein/skein.odin create mode 100644 core/crypto/sm3/sm3.odin create mode 100644 core/crypto/streebog/streebog.odin create mode 100644 core/crypto/tiger/tiger.odin create mode 100644 core/crypto/tiger2/tiger2.odin create mode 100644 core/crypto/util/util.odin create mode 100644 core/crypto/whirlpool/whirlpool.odin create mode 100644 tests/core/crypto/test_core_crypto.odin diff --git a/core/crypto/README.md b/core/crypto/README.md new file mode 100644 index 000000000..362a6299b --- /dev/null +++ b/core/crypto/README.md @@ -0,0 +1,94 @@ +# crypto +A crypto library for the Odin language + +## Supported +This library offers various algorithms available in either native Odin or via bindings to the [Botan](https://botan.randombit.net/) crypto library. +Please see the chart below for the options. +**Note:** All crypto hash algorithms, offered by [Botan\'s FFI](https://botan.randombit.net/handbook/api_ref/hash.html), have been added. + +## Hashing algorithms +| Algorithm | Odin | Botan | +|:-------------------------------------------------------------------------------------------------------------|:-----------------|:---------------------| +| [BLAKE](https://web.archive.org/web/20190915215948/https://131002.net/blake) | ✔️ | | +| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ | ✔️ | +| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ | | +| [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | ✔️ | ✔️ | +| [Grøstl](http://www.groestl.info/Groestl.zip) | ✔️ | | +| [HAVAL](https://web.archive.org/web/20150111210116/http://labs.calyptix.com/haval.php) | ✔️ | | +| [JH](https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html) | ✔️ | | +| [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ | ✔️ | +| [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | ✔️ | | +| [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | ✔️ | ✔️ | +| [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | ✔️ | ✔️ | +| [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | ✔️ | ✔️\* | +| [SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | ✔️ | ✔️ | +| [SHA-2](https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf) | ✔️ | ✔️ | +| [SHA-3](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ | ✔️ | +| [SHAKE](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ | ✔️ | +| [Skein](https://www.schneier.com/academic/skein/) | | ✔️\*\* | +| [SM3](https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02) | ✔️ | ✔️ | +| [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | ✔️ | ✔️ | +| [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ | ✔️ | +| [Tiger2](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ | | +| [Whirlpool](https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html) | ✔️ | ✔️ | + +\* Only `RIPEMD-160` +\*\* Only `SKEIN-512` + +#### High level API +Each hash algorithm contains a procedure group named `hash`, or if the algorithm provides more than one digest size `hash_`\*\*\*. +Included in these groups are four procedures. +* `hash_string` - Hash a given string and return the computed hash. Just calls `hash_bytes` internally +* `hash_bytes` - Hash a given byte slice and return the computed hash +* `hash_stream` - Takes a stream from io.Stream and returns the computed hash from it +* `hash_file` - Hashes a file. A second boolean parameter controls if the file is streamed (set to false) or read at once (set to true) + +\*\*\* On some algorithms there is another part to the name, since they might offer control about additional parameters. +For instance, `HAVAL` offers different sizes as well as three different round amounts. +Computing a 256-bit hash with 3 rounds is therefore achieved by calling `haval.hash_256_3(...)`. + +#### Low level API +The above mentioned procedures internally call three procedures: `init`, `update` and `final`. +You may also directly call them, if you wish. + +#### Context system +The library uses a context system internally to be able to switch between Odin / Botan implementations freely. +When an Odin implementation is available, it is the default. +You may change what is used during runtime by calling `foo.use_botan()` or `foo.use_odin()`. +It is also possible to set this during compile time via `USE_BOTAN_LIB=true`. +Internally a vtable is used to set the appropriate procedures when switching. This works for all the procedures mentioned in the APIs above. + +#### Example +```odin +package crypto_example + +// Import the desired package +import "core:crypto/md4" + +main :: proc() { + input := "foo" + // Compute the hash via Odin implementation + computed_hash := md4.hash(input) + // Switch to Botan + md4.use_botan() + // Compute the hash via Botan bindings + computed_hash_botan := md4.hash(input) +} +``` +For example uses of all available algorithms, please see the tests within `tests/core/crypto`. + +### Disclaimer +The algorithms were ported out of curiosity and due to interest in the field. +We have not had any of the code verified by a third party or tested/fuzzed by any automatic means. +Whereever we were able to find official test vectors, those were used to verify the implementation. +We do not recommend using them in a production environment, without any additional testing and/or verification. + +### ToDo +* Ciphers (Symmetric, Asymmetric) +* MACs (Message Authentication Code) +* CSPRNGs (Cryptographically Secure PseudoRandom Number Generator) +* KDFs (Key Derivation Function) +* KEAs (Key Exchange Algorithm) + +### License +This library is made available under the BSD-3 license. \ No newline at end of file diff --git a/core/crypto/_blake2/_blake2.odin b/core/crypto/_blake2/_blake2.odin new file mode 100644 index 000000000..b2e18d6c7 --- /dev/null +++ b/core/crypto/_blake2/_blake2.odin @@ -0,0 +1,2786 @@ +package _blake2 + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the BLAKE2 hashing algorithm, as defined in and +*/ + +import "../util" + +BLAKE2S_BLOCK_SIZE :: 64 +BLAKE2S_SIZE :: 32 +BLAKE2B_BLOCK_SIZE :: 128 +BLAKE2B_SIZE :: 64 + +Blake2s_Context :: struct { + h: [8]u32, + t: [2]u32, + f: [2]u32, + x: [BLAKE2S_BLOCK_SIZE]byte, + nx: int, + ih: [8]u32, + padded_key: [BLAKE2S_BLOCK_SIZE]byte, + is_keyed: bool, + size: byte, + is_last_node: bool, + cfg: Blake2_Config, +} + +Blake2b_Context :: struct { + h: [8]u64, + t: [2]u64, + f: [2]u64, + x: [BLAKE2B_BLOCK_SIZE]byte, + nx: int, + ih: [8]u64, + padded_key: [BLAKE2B_BLOCK_SIZE]byte, + is_keyed: bool, + size: byte, + is_last_node: bool, + cfg: Blake2_Config, +} + +Blake2_Config :: struct { + size: byte, + key: []byte, + salt: []byte, + person: []byte, + tree: union{Blake2_Tree}, +} + +Blake2_Tree :: struct { + fanout: byte, + max_depth: byte, + leaf_size: u32, + node_offset: u64, + node_depth: byte, + inner_hash_size: byte, + is_last_node: bool, +} + +BLAKE2S_IV := [8]u32 { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, +} + +BLAKE2B_IV := [8]u64 { + 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179, +} + +init_odin :: proc(ctx: ^$T) { + when T == Blake2s_Context { + block_size :: BLAKE2S_BLOCK_SIZE + } else when T == Blake2b_Context { + block_size :: BLAKE2B_BLOCK_SIZE + } + + p := make([]byte, block_size) + defer delete(p) + + p[0] = ctx.cfg.size + p[1] = byte(len(ctx.cfg.key)) + + if ctx.cfg.salt != nil { + when T == Blake2s_Context { + copy(p[16:], ctx.cfg.salt) + } else when T == Blake2b_Context { + copy(p[32:], ctx.cfg.salt) + } + } + if ctx.cfg.person != nil { + when T == Blake2s_Context { + copy(p[24:], ctx.cfg.person) + } else when T == Blake2b_Context { + copy(p[48:], ctx.cfg.person) + } + } + + if ctx.cfg.tree != nil { + p[2] = ctx.cfg.tree.(Blake2_Tree).fanout + p[3] = ctx.cfg.tree.(Blake2_Tree).max_depth + util.PUT_U32_LE(p[4:], ctx.cfg.tree.(Blake2_Tree).leaf_size) + when T == Blake2s_Context { + p[8] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset) + p[9] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 8) + p[10] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 16) + p[11] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 24) + p[12] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 32) + p[13] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 40) + p[14] = ctx.cfg.tree.(Blake2_Tree).node_depth + p[15] = ctx.cfg.tree.(Blake2_Tree).inner_hash_size + } else when T == Blake2b_Context { + util.PUT_U64_LE(p[8:], ctx.cfg.tree.(Blake2_Tree).node_offset) + p[16] = ctx.cfg.tree.(Blake2_Tree).node_depth + p[17] = ctx.cfg.tree.(Blake2_Tree).inner_hash_size + } + } else { + p[2], p[3] = 1, 1 + } + ctx.size = ctx.cfg.size + for i := 0; i < 8; i += 1 { + when T == Blake2s_Context { + ctx.h[i] = BLAKE2S_IV[i] ~ util.U32_LE(p[i * 4:]) + } + when T == Blake2b_Context { + ctx.h[i] = BLAKE2B_IV[i] ~ util.U64_LE(p[i * 8:]) + } + } + if ctx.cfg.tree != nil && ctx.cfg.tree.(Blake2_Tree).is_last_node { + ctx.is_last_node = true + } + if len(ctx.cfg.key) > 0 { + copy(ctx.padded_key[:], ctx.cfg.key) + update_odin(ctx, ctx.padded_key[:]) + ctx.is_keyed = true + } + copy(ctx.ih[:], ctx.h[:]) + copy(ctx.h[:], ctx.ih[:]) + if ctx.is_keyed { + update_odin(ctx, ctx.padded_key[:]) + } +} + +update_odin :: proc(ctx: ^$T, p: []byte) { + p := p + when T == Blake2s_Context { + block_size :: BLAKE2S_BLOCK_SIZE + } else when T == Blake2b_Context { + block_size :: BLAKE2B_BLOCK_SIZE + } + + left := block_size - ctx.nx + if len(p) > left { + copy(ctx.x[ctx.nx:], p[:left]) + p = p[left:] + blake2_blocks(ctx, ctx.x[:]) + ctx.nx = 0 + } + if len(p) > block_size { + n := len(p) &~ (block_size - 1) + if n == len(p) { + n -= block_size + } + blake2_blocks(ctx, p[:n]) + p = p[n:] + } + ctx.nx += copy(ctx.x[ctx.nx:], p) +} + +blake2s_final_odin :: proc(ctx: $T, hash: []byte) { + if ctx.is_keyed { + for i := 0; i < len(ctx.padded_key); i += 1 { + ctx.padded_key[i] = 0 + } + } + + dec := BLAKE2S_BLOCK_SIZE - u32(ctx.nx) + if ctx.t[0] < dec { + ctx.t[1] -= 1 + } + ctx.t[0] -= dec + + ctx.f[0] = 0xffffffff + if ctx.is_last_node { + ctx.f[1] = 0xffffffff + } + + blake2_blocks(ctx, ctx.x[:]) + + j := 0 + for s, _ in ctx.h[:(ctx.size - 1) / 4 + 1] { + hash[j + 0] = byte(s >> 0) + hash[j + 1] = byte(s >> 8) + hash[j + 2] = byte(s >> 16) + hash[j + 3] = byte(s >> 24) + j += 4 + } +} + +blake2b_final_odin :: proc(ctx: $T, hash: []byte) { + if ctx.is_keyed { + for i := 0; i < len(ctx.padded_key); i += 1 { + ctx.padded_key[i] = 0 + } + } + + dec := BLAKE2B_BLOCK_SIZE - u64(ctx.nx) + if ctx.t[0] < dec { + ctx.t[1] -= 1 + } + ctx.t[0] -= dec + + ctx.f[0] = 0xffffffffffffffff + if ctx.is_last_node { + ctx.f[1] = 0xffffffffffffffff + } + + blake2_blocks(ctx, ctx.x[:]) + + j := 0 + for s, _ in ctx.h[:(ctx.size - 1) / 8 + 1] { + hash[j + 0] = byte(s >> 0) + hash[j + 1] = byte(s >> 8) + hash[j + 2] = byte(s >> 16) + hash[j + 3] = byte(s >> 24) + hash[j + 4] = byte(s >> 32) + hash[j + 5] = byte(s >> 40) + hash[j + 6] = byte(s >> 48) + hash[j + 7] = byte(s >> 56) + j += 8 + } +} + +blake2_blocks :: proc(ctx: ^$T, p: []byte) { + when T == Blake2s_Context { + blake2s_blocks(ctx, p) + } + when T == Blake2b_Context { + blake2b_blocks(ctx, p) + } +} + +blake2s_blocks :: #force_inline proc "contextless"(ctx: ^Blake2s_Context, p: []byte) { + h0, h1, h2, h3, h4, h5, h6, h7 := ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] + p := p + for len(p) >= BLAKE2S_BLOCK_SIZE { + ctx.t[0] += BLAKE2S_BLOCK_SIZE + if ctx.t[0] < BLAKE2S_BLOCK_SIZE { + ctx.t[1] += 1 + } + v0, v1, v2, v3, v4, v5, v6, v7 := h0, h1, h2, h3, h4, h5, h6, h7 + v8 := BLAKE2S_IV[0] + v9 := BLAKE2S_IV[1] + v10 := BLAKE2S_IV[2] + v11 := BLAKE2S_IV[3] + v12 := BLAKE2S_IV[4] ~ ctx.t[0] + v13 := BLAKE2S_IV[5] ~ ctx.t[1] + v14 := BLAKE2S_IV[6] ~ ctx.f[0] + v15 := BLAKE2S_IV[7] ~ ctx.f[1] + m: [16]u32 + j := 0 + for i := 0; i < 16; i += 1 { + m[i] = u32(p[j]) | u32(p[j + 1]) << 8 | u32(p[j + 2]) << 16 | u32(p[j + 3]) << 24 + j += 4 + } + v0 += m[0] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 12) | v4 >> 12 + v1 += m[2] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 12) | v5 >> 12 + v2 += m[4] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 12) | v6 >> 12 + v3 += m[6] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 12) | v7 >> 12 + v2 += m[5] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 8) | v14 >> 8 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 7) | v6 >> 7 + v3 += m[7] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 8) | v15 >> 8 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 7) | v7 >> 7 + v1 += m[3] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 8) | v13 >> 8 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[1] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 8) | v12 >> 8 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 7) | v4 >> 7 + v0 += m[8] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 12) | v5 >> 12 + v1 += m[10] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 12) | v6 >> 12 + v2 += m[12] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 12) | v7 >> 12 + v3 += m[14] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 12) | v4 >> 12 + v2 += m[13] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 8) | v13 >> 8 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 7) | v7 >> 7 + v3 += m[15] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 8) | v14 >> 8 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 7) | v4 >> 7 + v1 += m[11] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 8) | v12 >> 8 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 7) | v6 >> 7 + v0 += m[9] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 8) | v15 >> 8 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[14] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 12) | v4 >> 12 + v1 += m[4] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 12) | v5 >> 12 + v2 += m[9] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 12) | v6 >> 12 + v3 += m[13] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 12) | v7 >> 12 + v2 += m[15] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 8) | v14 >> 8 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 7) | v6 >> 7 + v3 += m[6] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 8) | v15 >> 8 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 7) | v7 >> 7 + v1 += m[8] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 8) | v13 >> 8 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[10] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 8) | v12 >> 8 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 7) | v4 >> 7 + v0 += m[1] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 12) | v5 >> 12 + v1 += m[0] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 12) | v6 >> 12 + v2 += m[11] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 12) | v7 >> 12 + v3 += m[5] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 12) | v4 >> 12 + v2 += m[7] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 8) | v13 >> 8 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 7) | v7 >> 7 + v3 += m[3] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 8) | v14 >> 8 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 7) | v4 >> 7 + v1 += m[2] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 8) | v12 >> 8 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 7) | v6 >> 7 + v0 += m[12] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 8) | v15 >> 8 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[11] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 12) | v4 >> 12 + v1 += m[12] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 12) | v5 >> 12 + v2 += m[5] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 12) | v6 >> 12 + v3 += m[15] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 12) | v7 >> 12 + v2 += m[2] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 8) | v14 >> 8 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 7) | v6 >> 7 + v3 += m[13] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 8) | v15 >> 8 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 7) | v7 >> 7 + v1 += m[0] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 8) | v13 >> 8 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[8] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 8) | v12 >> 8 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 7) | v4 >> 7 + v0 += m[10] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 12) | v5 >> 12 + v1 += m[3] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 12) | v6 >> 12 + v2 += m[7] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 12) | v7 >> 12 + v3 += m[9] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 12) | v4 >> 12 + v2 += m[1] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 8) | v13 >> 8 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 7) | v7 >> 7 + v3 += m[4] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 8) | v14 >> 8 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 7) | v4 >> 7 + v1 += m[6] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 8) | v12 >> 8 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 7) | v6 >> 7 + v0 += m[14] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 8) | v15 >> 8 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[7] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 12) | v4 >> 12 + v1 += m[3] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 12) | v5 >> 12 + v2 += m[13] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 12) | v6 >> 12 + v3 += m[11] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 12) | v7 >> 12 + v2 += m[12] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 8) | v14 >> 8 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 7) | v6 >> 7 + v3 += m[14] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 8) | v15 >> 8 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 7) | v7 >> 7 + v1 += m[1] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 8) | v13 >> 8 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[9] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 8) | v12 >> 8 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 7) | v4 >> 7 + v0 += m[2] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 12) | v5 >> 12 + v1 += m[5] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 12) | v6 >> 12 + v2 += m[4] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 12) | v7 >> 12 + v3 += m[15] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 12) | v4 >> 12 + v2 += m[0] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 8) | v13 >> 8 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 7) | v7 >> 7 + v3 += m[8] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 8) | v14 >> 8 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 7) | v4 >> 7 + v1 += m[10] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 8) | v12 >> 8 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 7) | v6 >> 7 + v0 += m[6] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 8) | v15 >> 8 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[9] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 12) | v4 >> 12 + v1 += m[5] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 12) | v5 >> 12 + v2 += m[2] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 12) | v6 >> 12 + v3 += m[10] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 12) | v7 >> 12 + v2 += m[4] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 8) | v14 >> 8 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 7) | v6 >> 7 + v3 += m[15] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 8) | v15 >> 8 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 7) | v7 >> 7 + v1 += m[7] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 8) | v13 >> 8 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[0] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 8) | v12 >> 8 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 7) | v4 >> 7 + v0 += m[14] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 12) | v5 >> 12 + v1 += m[11] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 12) | v6 >> 12 + v2 += m[6] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 12) | v7 >> 12 + v3 += m[3] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 12) | v4 >> 12 + v2 += m[8] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 8) | v13 >> 8 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 7) | v7 >> 7 + v3 += m[13] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 8) | v14 >> 8 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 7) | v4 >> 7 + v1 += m[12] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 8) | v12 >> 8 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 7) | v6 >> 7 + v0 += m[1] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 8) | v15 >> 8 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[2] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 12) | v4 >> 12 + v1 += m[6] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 12) | v5 >> 12 + v2 += m[0] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 12) | v6 >> 12 + v3 += m[8] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 12) | v7 >> 12 + v2 += m[11] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 8) | v14 >> 8 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 7) | v6 >> 7 + v3 += m[3] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 8) | v15 >> 8 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 7) | v7 >> 7 + v1 += m[10] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 8) | v13 >> 8 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[12] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 8) | v12 >> 8 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 7) | v4 >> 7 + v0 += m[4] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 12) | v5 >> 12 + v1 += m[7] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 12) | v6 >> 12 + v2 += m[15] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 12) | v7 >> 12 + v3 += m[1] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 12) | v4 >> 12 + v2 += m[14] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 8) | v13 >> 8 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 7) | v7 >> 7 + v3 += m[9] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 8) | v14 >> 8 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 7) | v4 >> 7 + v1 += m[5] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 8) | v12 >> 8 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 7) | v6 >> 7 + v0 += m[13] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 8) | v15 >> 8 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[12] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 12) | v4 >> 12 + v1 += m[1] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 12) | v5 >> 12 + v2 += m[14] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 12) | v6 >> 12 + v3 += m[4] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 12) | v7 >> 12 + v2 += m[13] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 8) | v14 >> 8 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 7) | v6 >> 7 + v3 += m[10] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 8) | v15 >> 8 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 7) | v7 >> 7 + v1 += m[15] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 8) | v13 >> 8 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[5] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 8) | v12 >> 8 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 7) | v4 >> 7 + v0 += m[0] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 12) | v5 >> 12 + v1 += m[6] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 12) | v6 >> 12 + v2 += m[9] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 12) | v7 >> 12 + v3 += m[8] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 12) | v4 >> 12 + v2 += m[2] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 8) | v13 >> 8 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 7) | v7 >> 7 + v3 += m[11] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 8) | v14 >> 8 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 7) | v4 >> 7 + v1 += m[3] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 8) | v12 >> 8 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 7) | v6 >> 7 + v0 += m[7] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 8) | v15 >> 8 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[13] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 12) | v4 >> 12 + v1 += m[7] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 12) | v5 >> 12 + v2 += m[12] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 12) | v6 >> 12 + v3 += m[3] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 12) | v7 >> 12 + v2 += m[1] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 8) | v14 >> 8 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 7) | v6 >> 7 + v3 += m[9] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 8) | v15 >> 8 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 7) | v7 >> 7 + v1 += m[14] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 8) | v13 >> 8 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[11] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 8) | v12 >> 8 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 7) | v4 >> 7 + v0 += m[5] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 12) | v5 >> 12 + v1 += m[15] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 12) | v6 >> 12 + v2 += m[8] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 12) | v7 >> 12 + v3 += m[2] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 12) | v4 >> 12 + v2 += m[6] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 8) | v13 >> 8 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 7) | v7 >> 7 + v3 += m[10] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 8) | v14 >> 8 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 7) | v4 >> 7 + v1 += m[4] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 8) | v12 >> 8 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 7) | v6 >> 7 + v0 += m[0] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 8) | v15 >> 8 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[6] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 12) | v4 >> 12 + v1 += m[14] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 12) | v5 >> 12 + v2 += m[11] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 12) | v6 >> 12 + v3 += m[0] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 12) | v7 >> 12 + v2 += m[3] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 8) | v14 >> 8 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 7) | v6 >> 7 + v3 += m[8] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 8) | v15 >> 8 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 7) | v7 >> 7 + v1 += m[9] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 8) | v13 >> 8 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[15] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 8) | v12 >> 8 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 7) | v4 >> 7 + v0 += m[12] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 12) | v5 >> 12 + v1 += m[13] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 12) | v6 >> 12 + v2 += m[1] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 12) | v7 >> 12 + v3 += m[10] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 12) | v4 >> 12 + v2 += m[4] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 8) | v13 >> 8 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 7) | v7 >> 7 + v3 += m[5] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 8) | v14 >> 8 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 7) | v4 >> 7 + v1 += m[7] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 8) | v12 >> 8 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 7) | v6 >> 7 + v0 += m[2] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 8) | v15 >> 8 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[10] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 12) | v4 >> 12 + v1 += m[8] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 12) | v5 >> 12 + v2 += m[7] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 12) | v6 >> 12 + v3 += m[1] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 12) | v7 >> 12 + v2 += m[6] + v2 += v6 + v14 ~= v2 + v14 = v14 << (32 - 8) | v14 >> 8 + v10 += v14 + v6 ~= v10 + v6 = v6 << (32 - 7) | v6 >> 7 + v3 += m[5] + v3 += v7 + v15 ~= v3 + v15 = v15 << (32 - 8) | v15 >> 8 + v11 += v15 + v7 ~= v11 + v7 = v7 << (32 - 7) | v7 >> 7 + v1 += m[4] + v1 += v5 + v13 ~= v1 + v13 = v13 << (32 - 8) | v13 >> 8 + v9 += v13 + v5 ~= v9 + v5 = v5 << (32 - 7) | v5 >> 7 + v0 += m[2] + v0 += v4 + v12 ~= v0 + v12 = v12 << (32 - 8) | v12 >> 8 + v8 += v12 + v4 ~= v8 + v4 = v4 << (32 - 7) | v4 >> 7 + v0 += m[15] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 12) | v5 >> 12 + v1 += m[9] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 12) | v6 >> 12 + v2 += m[3] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 12) | v7 >> 12 + v3 += m[13] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 12) | v4 >> 12 + v2 += m[12] + v2 += v7 + v13 ~= v2 + v13 = v13 << (32 - 8) | v13 >> 8 + v8 += v13 + v7 ~= v8 + v7 = v7 << (32 - 7) | v7 >> 7 + v3 += m[0] + v3 += v4 + v14 ~= v3 + v14 = v14 << (32 - 8) | v14 >> 8 + v9 += v14 + v4 ~= v9 + v4 = v4 << (32 - 7) | v4 >> 7 + v1 += m[14] + v1 += v6 + v12 ~= v1 + v12 = v12 << (32 - 8) | v12 >> 8 + v11 += v12 + v6 ~= v11 + v6 = v6 << (32 - 7) | v6 >> 7 + v0 += m[11] + v0 += v5 + v15 ~= v0 + v15 = v15 << (32 - 8) | v15 >> 8 + v10 += v15 + v5 ~= v10 + v5 = v5 << (32 - 7) | v5 >> 7 + h0 ~= v0 ~ v8 + h1 ~= v1 ~ v9 + h2 ~= v2 ~ v10 + h3 ~= v3 ~ v11 + h4 ~= v4 ~ v12 + h5 ~= v5 ~ v13 + h6 ~= v6 ~ v14 + h7 ~= v7 ~ v15 + p = p[BLAKE2S_BLOCK_SIZE:] + } + ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] = h0, h1, h2, h3, h4, h5, h6, h7 +} + +blake2b_blocks :: #force_inline proc "contextless"(ctx: ^Blake2b_Context, p: []byte) { + h0, h1, h2, h3, h4, h5, h6, h7 := ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] + p := p + for len(p) >= BLAKE2B_BLOCK_SIZE { + ctx.t[0] += BLAKE2B_BLOCK_SIZE + if ctx.t[0] < BLAKE2B_BLOCK_SIZE { + ctx.t[1]+=1 + } + v0, v1, v2, v3, v4, v5, v6, v7 := h0, h1, h2, h3, h4, h5, h6, h7 + v8 := BLAKE2B_IV[0] + v9 := BLAKE2B_IV[1] + v10 := BLAKE2B_IV[2] + v11 := BLAKE2B_IV[3] + v12 := BLAKE2B_IV[4] ~ ctx.t[0] + v13 := BLAKE2B_IV[5] ~ ctx.t[1] + v14 := BLAKE2B_IV[6] ~ ctx.f[0] + v15 := BLAKE2B_IV[7] ~ ctx.f[1] + m: [16]u64 = --- + j := 0 + for i := 0; i < 16; i+=1 { + m[i] = u64(p[j]) | u64(p[j + 1]) << 8 | u64(p[j + 2]) << 16 | u64(p[j + 3]) << 24 | + u64(p[j + 4]) << 32 | u64(p[j + 5]) << 40 | u64(p[j + 6]) << 48 | u64(p[j + 7]) << 56 + j += 8 + } + v0 += m[0] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 32) | v12 >> 32 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 24) | v4 >> 24 + v1 += m[2] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 32) | v13 >> 32 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 24) | v5 >> 24 + v2 += m[4] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 32) | v14 >> 32 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 24) | v6 >> 24 + v3 += m[6] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 32) | v15 >> 32 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 24) | v7 >> 24 + v2 += m[5] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 63) | v6 >> 63 + v3 += m[7] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 63) | v7 >> 63 + v1 += m[3] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[1] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 63) | v4 >> 63 + v0 += m[8] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 32) | v15 >> 32 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 24) | v5 >> 24 + v1 += m[10] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 32) | v12 >> 32 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 24) | v6 >> 24 + v2 += m[12] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 32) | v13 >> 32 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 24) | v7 >> 24 + v3 += m[14] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 32) | v14 >> 32 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 24) | v4 >> 24 + v2 += m[13] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 63) | v7 >> 63 + v3 += m[15] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 63) | v4 >> 63 + v1 += m[11] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 63) | v6 >> 63 + v0 += m[9] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[14] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 32) | v12 >> 32 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 24) | v4 >> 24 + v1 += m[4] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 32) | v13 >> 32 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 24) | v5 >> 24 + v2 += m[9] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 32) | v14 >> 32 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 24) | v6 >> 24 + v3 += m[13] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 32) | v15 >> 32 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 24) | v7 >> 24 + v2 += m[15] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 63) | v6 >> 63 + v3 += m[6] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 63) | v7 >> 63 + v1 += m[8] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[10] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 63) | v4 >> 63 + v0 += m[1] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 32) | v15 >> 32 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 24) | v5 >> 24 + v1 += m[0] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 32) | v12 >> 32 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 24) | v6 >> 24 + v2 += m[11] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 32) | v13 >> 32 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 24) | v7 >> 24 + v3 += m[5] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 32) | v14 >> 32 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 24) | v4 >> 24 + v2 += m[7] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 63) | v7 >> 63 + v3 += m[3] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 63) | v4 >> 63 + v1 += m[2] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 63) | v6 >> 63 + v0 += m[12] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[11] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 32) | v12 >> 32 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 24) | v4 >> 24 + v1 += m[12] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 32) | v13 >> 32 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 24) | v5 >> 24 + v2 += m[5] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 32) | v14 >> 32 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 24) | v6 >> 24 + v3 += m[15] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 32) | v15 >> 32 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 24) | v7 >> 24 + v2 += m[2] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 63) | v6 >> 63 + v3 += m[13] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 63) | v7 >> 63 + v1 += m[0] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[8] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 63) | v4 >> 63 + v0 += m[10] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 32) | v15 >> 32 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 24) | v5 >> 24 + v1 += m[3] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 32) | v12 >> 32 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 24) | v6 >> 24 + v2 += m[7] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 32) | v13 >> 32 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 24) | v7 >> 24 + v3 += m[9] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 32) | v14 >> 32 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 24) | v4 >> 24 + v2 += m[1] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 63) | v7 >> 63 + v3 += m[4] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 63) | v4 >> 63 + v1 += m[6] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 63) | v6 >> 63 + v0 += m[14] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[7] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 32) | v12 >> 32 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 24) | v4 >> 24 + v1 += m[3] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 32) | v13 >> 32 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 24) | v5 >> 24 + v2 += m[13] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 32) | v14 >> 32 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 24) | v6 >> 24 + v3 += m[11] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 32) | v15 >> 32 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 24) | v7 >> 24 + v2 += m[12] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 63) | v6 >> 63 + v3 += m[14] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 63) | v7 >> 63 + v1 += m[1] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[9] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 63) | v4 >> 63 + v0 += m[2] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 32) | v15 >> 32 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 24) | v5 >> 24 + v1 += m[5] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 32) | v12 >> 32 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 24) | v6 >> 24 + v2 += m[4] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 32) | v13 >> 32 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 24) | v7 >> 24 + v3 += m[15] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 32) | v14 >> 32 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 24) | v4 >> 24 + v2 += m[0] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 63) | v7 >> 63 + v3 += m[8] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 63) | v4 >> 63 + v1 += m[10] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 63) | v6 >> 63 + v0 += m[6] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[9] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 32) | v12 >> 32 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 24) | v4 >> 24 + v1 += m[5] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 32) | v13 >> 32 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 24) | v5 >> 24 + v2 += m[2] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 32) | v14 >> 32 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 24) | v6 >> 24 + v3 += m[10] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 32) | v15 >> 32 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 24) | v7 >> 24 + v2 += m[4] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 63) | v6 >> 63 + v3 += m[15] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 63) | v7 >> 63 + v1 += m[7] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[0] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 63) | v4 >> 63 + v0 += m[14] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 32) | v15 >> 32 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 24) | v5 >> 24 + v1 += m[11] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 32) | v12 >> 32 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 24) | v6 >> 24 + v2 += m[6] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 32) | v13 >> 32 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 24) | v7 >> 24 + v3 += m[3] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 32) | v14 >> 32 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 24) | v4 >> 24 + v2 += m[8] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 63) | v7 >> 63 + v3 += m[13] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 63) | v4 >> 63 + v1 += m[12] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 63) | v6 >> 63 + v0 += m[1] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[2] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 32) | v12 >> 32 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 24) | v4 >> 24 + v1 += m[6] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 32) | v13 >> 32 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 24) | v5 >> 24 + v2 += m[0] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 32) | v14 >> 32 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 24) | v6 >> 24 + v3 += m[8] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 32) | v15 >> 32 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 24) | v7 >> 24 + v2 += m[11] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 63) | v6 >> 63 + v3 += m[3] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 63) | v7 >> 63 + v1 += m[10] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[12] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 63) | v4 >> 63 + v0 += m[4] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 32) | v15 >> 32 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 24) | v5 >> 24 + v1 += m[7] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 32) | v12 >> 32 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 24) | v6 >> 24 + v2 += m[15] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 32) | v13 >> 32 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 24) | v7 >> 24 + v3 += m[1] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 32) | v14 >> 32 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 24) | v4 >> 24 + v2 += m[14] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 63) | v7 >> 63 + v3 += m[9] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 63) | v4 >> 63 + v1 += m[5] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 63) | v6 >> 63 + v0 += m[13] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[12] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 32) | v12 >> 32 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 24) | v4 >> 24 + v1 += m[1] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 32) | v13 >> 32 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 24) | v5 >> 24 + v2 += m[14] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 32) | v14 >> 32 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 24) | v6 >> 24 + v3 += m[4] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 32) | v15 >> 32 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 24) | v7 >> 24 + v2 += m[13] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 63) | v6 >> 63 + v3 += m[10] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 63) | v7 >> 63 + v1 += m[15] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[5] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 63) | v4 >> 63 + v0 += m[0] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 32) | v15 >> 32 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 24) | v5 >> 24 + v1 += m[6] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 32) | v12 >> 32 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 24) | v6 >> 24 + v2 += m[9] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 32) | v13 >> 32 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 24) | v7 >> 24 + v3 += m[8] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 32) | v14 >> 32 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 24) | v4 >> 24 + v2 += m[2] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 63) | v7 >> 63 + v3 += m[11] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 63) | v4 >> 63 + v1 += m[3] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 63) | v6 >> 63 + v0 += m[7] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[13] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 32) | v12 >> 32 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 24) | v4 >> 24 + v1 += m[7] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 32) | v13 >> 32 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 24) | v5 >> 24 + v2 += m[12] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 32) | v14 >> 32 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 24) | v6 >> 24 + v3 += m[3] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 32) | v15 >> 32 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 24) | v7 >> 24 + v2 += m[1] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 63) | v6 >> 63 + v3 += m[9] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 63) | v7 >> 63 + v1 += m[14] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[11] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 63) | v4 >> 63 + v0 += m[5] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 32) | v15 >> 32 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 24) | v5 >> 24 + v1 += m[15] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 32) | v12 >> 32 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 24) | v6 >> 24 + v2 += m[8] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 32) | v13 >> 32 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 24) | v7 >> 24 + v3 += m[2] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 32) | v14 >> 32 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 24) | v4 >> 24 + v2 += m[6] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 63) | v7 >> 63 + v3 += m[10] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 63) | v4 >> 63 + v1 += m[4] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 63) | v6 >> 63 + v0 += m[0] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[6] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 32) | v12 >> 32 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 24) | v4 >> 24 + v1 += m[14] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 32) | v13 >> 32 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 24) | v5 >> 24 + v2 += m[11] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 32) | v14 >> 32 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 24) | v6 >> 24 + v3 += m[0] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 32) | v15 >> 32 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 24) | v7 >> 24 + v2 += m[3] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 63) | v6 >> 63 + v3 += m[8] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 63) | v7 >> 63 + v1 += m[9] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[15] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 63) | v4 >> 63 + v0 += m[12] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 32) | v15 >> 32 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 24) | v5 >> 24 + v1 += m[13] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 32) | v12 >> 32 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 24) | v6 >> 24 + v2 += m[1] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 32) | v13 >> 32 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 24) | v7 >> 24 + v3 += m[10] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 32) | v14 >> 32 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 24) | v4 >> 24 + v2 += m[4] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 63) | v7 >> 63 + v3 += m[5] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 63) | v4 >> 63 + v1 += m[7] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 63) | v6 >> 63 + v0 += m[2] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[10] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 32) | v12 >> 32 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 24) | v4 >> 24 + v1 += m[8] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 32) | v13 >> 32 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 24) | v5 >> 24 + v2 += m[7] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 32) | v14 >> 32 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 24) | v6 >> 24 + v3 += m[1] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 32) | v15 >> 32 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 24) | v7 >> 24 + v2 += m[6] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 63) | v6 >> 63 + v3 += m[5] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 63) | v7 >> 63 + v1 += m[4] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[2] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 63) | v4 >> 63 + v0 += m[15] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 32) | v15 >> 32 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 24) | v5 >> 24 + v1 += m[9] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 32) | v12 >> 32 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 24) | v6 >> 24 + v2 += m[3] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 32) | v13 >> 32 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 24) | v7 >> 24 + v3 += m[13] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 32) | v14 >> 32 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 24) | v4 >> 24 + v2 += m[12] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 63) | v7 >> 63 + v3 += m[0] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 63) | v4 >> 63 + v1 += m[14] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 63) | v6 >> 63 + v0 += m[11] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[0] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 32) | v12 >> 32 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 24) | v4 >> 24 + v1 += m[2] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 32) | v13 >> 32 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 24) | v5 >> 24 + v2 += m[4] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 32) | v14 >> 32 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 24) | v6 >> 24 + v3 += m[6] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 32) | v15 >> 32 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 24) | v7 >> 24 + v2 += m[5] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 63) | v6 >> 63 + v3 += m[7] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 63) | v7 >> 63 + v1 += m[3] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[1] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 63) | v4 >> 63 + v0 += m[8] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 32) | v15 >> 32 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 24) | v5 >> 24 + v1 += m[10] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 32) | v12 >> 32 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 24) | v6 >> 24 + v2 += m[12] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 32) | v13 >> 32 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 24) | v7 >> 24 + v3 += m[14] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 32) | v14 >> 32 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 24) | v4 >> 24 + v2 += m[13] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 63) | v7 >> 63 + v3 += m[15] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 63) | v4 >> 63 + v1 += m[11] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 63) | v6 >> 63 + v0 += m[9] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[14] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 32) | v12 >> 32 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 24) | v4 >> 24 + v1 += m[4] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 32) | v13 >> 32 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 24) | v5 >> 24 + v2 += m[9] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 32) | v14 >> 32 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 24) | v6 >> 24 + v3 += m[13] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 32) | v15 >> 32 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 24) | v7 >> 24 + v2 += m[15] + v2 += v6 + v14 ~= v2 + v14 = v14 << (64 - 16) | v14 >> 16 + v10 += v14 + v6 ~= v10 + v6 = v6 << (64 - 63) | v6 >> 63 + v3 += m[6] + v3 += v7 + v15 ~= v3 + v15 = v15 << (64 - 16) | v15 >> 16 + v11 += v15 + v7 ~= v11 + v7 = v7 << (64 - 63) | v7 >> 63 + v1 += m[8] + v1 += v5 + v13 ~= v1 + v13 = v13 << (64 - 16) | v13 >> 16 + v9 += v13 + v5 ~= v9 + v5 = v5 << (64 - 63) | v5 >> 63 + v0 += m[10] + v0 += v4 + v12 ~= v0 + v12 = v12 << (64 - 16) | v12 >> 16 + v8 += v12 + v4 ~= v8 + v4 = v4 << (64 - 63) | v4 >> 63 + v0 += m[1] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 32) | v15 >> 32 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 24) | v5 >> 24 + v1 += m[0] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 32) | v12 >> 32 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 24) | v6 >> 24 + v2 += m[11] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 32) | v13 >> 32 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 24) | v7 >> 24 + v3 += m[5] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 32) | v14 >> 32 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 24) | v4 >> 24 + v2 += m[7] + v2 += v7 + v13 ~= v2 + v13 = v13 << (64 - 16) | v13 >> 16 + v8 += v13 + v7 ~= v8 + v7 = v7 << (64 - 63) | v7 >> 63 + v3 += m[3] + v3 += v4 + v14 ~= v3 + v14 = v14 << (64 - 16) | v14 >> 16 + v9 += v14 + v4 ~= v9 + v4 = v4 << (64 - 63) | v4 >> 63 + v1 += m[2] + v1 += v6 + v12 ~= v1 + v12 = v12 << (64 - 16) | v12 >> 16 + v11 += v12 + v6 ~= v11 + v6 = v6 << (64 - 63) | v6 >> 63 + v0 += m[12] + v0 += v5 + v15 ~= v0 + v15 = v15 << (64 - 16) | v15 >> 16 + v10 += v15 + v5 ~= v10 + v5 = v5 << (64 - 63) | v5 >> 63 + h0 ~= v0 ~ v8 + h1 ~= v1 ~ v9 + h2 ~= v2 ~ v10 + h3 ~= v3 ~ v11 + h4 ~= v4 ~ v12 + h5 ~= v5 ~ v13 + h6 ~= v6 ~ v14 + h7 ~= v7 ~ v15 + p = p[BLAKE2B_BLOCK_SIZE:] + } + ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] = h0, h1, h2, h3, h4, h5, h6, h7 +} \ No newline at end of file diff --git a/core/crypto/_ctx/_ctx.odin b/core/crypto/_ctx/_ctx.odin new file mode 100644 index 000000000..2632e8c7e --- /dev/null +++ b/core/crypto/_ctx/_ctx.odin @@ -0,0 +1,78 @@ +package _ctx + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog: Initial creation and testing of the bindings. + + Implementation of the context, used internally by the crypto library. +*/ + +import "core:io" + +Hash_Size :: enum { + _16, + _20, + _24, + _28, + _32, + _40, + _48, + _64, + _128, +} + +Hash_Context :: struct { + botan_hash_algo: cstring, + external_ctx: any, + internal_ctx: any, + hash_size: Hash_Size, + hash_size_val: int, + is_using_odin: bool, + using vtbl: ^Hash_Context_Vtable, +} + +Hash_Context_Vtable :: struct { + hash_bytes_16 : proc (ctx: ^Hash_Context, input: []byte) -> [16]byte, + hash_bytes_20 : proc (ctx: ^Hash_Context, input: []byte) -> [20]byte, + hash_bytes_24 : proc (ctx: ^Hash_Context, input: []byte) -> [24]byte, + hash_bytes_28 : proc (ctx: ^Hash_Context, input: []byte) -> [28]byte, + hash_bytes_32 : proc (ctx: ^Hash_Context, input: []byte) -> [32]byte, + hash_bytes_40 : proc (ctx: ^Hash_Context, input: []byte) -> [40]byte, + hash_bytes_48 : proc (ctx: ^Hash_Context, input: []byte) -> [48]byte, + hash_bytes_64 : proc (ctx: ^Hash_Context, input: []byte) -> [64]byte, + hash_bytes_128 : proc (ctx: ^Hash_Context, input: []byte) -> [128]byte, + hash_file_16 : proc (ctx: ^Hash_Context, path: string, load_at_once: bool) -> ([16]byte, bool), + hash_file_20 : proc (ctx: ^Hash_Context, path: string, load_at_once: bool) -> ([20]byte, bool), + hash_file_24 : proc (ctx: ^Hash_Context, path: string, load_at_once: bool) -> ([24]byte, bool), + hash_file_28 : proc (ctx: ^Hash_Context, path: string, load_at_once: bool) -> ([28]byte, bool), + hash_file_32 : proc (ctx: ^Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool), + hash_file_40 : proc (ctx: ^Hash_Context, path: string, load_at_once: bool) -> ([40]byte, bool), + hash_file_48 : proc (ctx: ^Hash_Context, path: string, load_at_once: bool) -> ([48]byte, bool), + hash_file_64 : proc (ctx: ^Hash_Context, path: string, load_at_once: bool) -> ([64]byte, bool), + hash_file_128 : proc (ctx: ^Hash_Context, path: string, load_at_once: bool) -> ([128]byte, bool), + hash_stream_16 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([16]byte, bool), + hash_stream_20 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([20]byte, bool), + hash_stream_24 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([24]byte, bool), + hash_stream_28 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([28]byte, bool), + hash_stream_32 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([32]byte, bool), + hash_stream_40 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([40]byte, bool), + hash_stream_48 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([48]byte, bool), + hash_stream_64 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([64]byte, bool), + hash_stream_128 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([128]byte, bool), + hash_bytes_slice : proc (ctx: ^Hash_Context, input: []byte, out_size: int, allocator := context.allocator) -> []byte, + hash_file_slice : proc (ctx: ^Hash_Context, path: string, out_size: int, load_at_once: bool, allocator := context.allocator) -> ([]byte, bool), + hash_stream_slice : proc (ctx: ^Hash_Context, s: io.Stream, out_size: int, allocator := context.allocator) -> ([]byte, bool), + init : proc (ctx: ^Hash_Context), + update : proc (ctx: ^Hash_Context, data: []byte), + final : proc (ctx: ^Hash_Context, hash: []byte), +} + +_init_vtable :: #force_inline proc() -> ^Hash_Context { + ctx := new(Hash_Context) + vtbl := new(Hash_Context_Vtable) + ctx.vtbl = vtbl + return ctx +} \ No newline at end of file diff --git a/core/crypto/_sha3/_sha3.odin b/core/crypto/_sha3/_sha3.odin new file mode 100644 index 000000000..1038eb77c --- /dev/null +++ b/core/crypto/_sha3/_sha3.odin @@ -0,0 +1,170 @@ +package _sha3 + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the Keccak hashing algorithm, standardized as SHA3 in + To use the original Keccak padding, set the is_keccak bool to true, otherwise it will use SHA3 padding. +*/ + +import "../util" + +ROUNDS :: 24 + +Sha3_Context :: struct { + st: struct #raw_union { + b: [200]u8, + q: [25]u64, + }, + pt: int, + rsiz: int, + mdlen: int, + is_keccak: bool, +} + +keccakf :: proc "contextless" (st: ^[25]u64) { + keccakf_rndc := [?]u64 { + 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, + 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, + 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, + 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, + 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, + 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, + 0x8000000000008080, 0x0000000080000001, 0x8000000080008008, + } + + keccakf_rotc := [?]i32 { + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, + 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44, + } + + keccakf_piln := [?]i32 { + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1, + } + + i, j, r: i32 = ---, ---, --- + t: u64 = --- + bc: [5]u64 = --- + + when ODIN_ENDIAN != "little" { + v: uintptr = --- + for i = 0; i < 25; i += 1 { + v := uintptr(&st[i]) + st[i] = u64((^u8)(v + 0)^ << 0) | u64((^u8)(v + 1)^ << 8) | + u64((^u8)(v + 2)^ << 16) | u64((^u8)(v + 3)^ << 24) | + u64((^u8)(v + 4)^ << 32) | u64((^u8)(v + 5)^ << 40) | + u64((^u8)(v + 6)^ << 48) | u64((^u8)(v + 7)^ << 56) + } + } + + for r = 0; r < ROUNDS; r += 1 { + // theta + for i = 0; i < 5; i += 1 { + bc[i] = st[i] ~ st[i + 5] ~ st[i + 10] ~ st[i + 15] ~ st[i + 20] + } + + for i = 0; i < 5; i += 1 { + t = bc[(i + 4) % 5] ~ util.ROTL64(bc[(i + 1) % 5], 1) + for j = 0; j < 25; j += 5 { + st[j + i] ~= t + } + } + + // rho pi + t = st[1] + for i = 0; i < 24; i += 1 { + j = keccakf_piln[i] + bc[0] = st[j] + st[j] = util.ROTL64(t, u64(keccakf_rotc[i])) + t = bc[0] + } + + // chi + for j = 0; j < 25; j += 5 { + for i = 0; i < 5; i += 1 { + bc[i] = st[j + i] + } + for i = 0; i < 5; i += 1 { + st[j + i] ~= ~bc[(i + 1) % 5] & bc[(i + 2) % 5] + } + } + + st[0] ~= keccakf_rndc[r] + } + + when ODIN_ENDIAN != "little" { + for i = 0; i < 25; i += 1 { + v = uintptr(&st[i]) + t = st[i] + (^u8)(v + 0)^ = (t >> 0) & 0xff + (^u8)(v + 1)^ = (t >> 8) & 0xff + (^u8)(v + 2)^ = (t >> 16) & 0xff + (^u8)(v + 3)^ = (t >> 24) & 0xff + (^u8)(v + 4)^ = (t >> 32) & 0xff + (^u8)(v + 5)^ = (t >> 40) & 0xff + (^u8)(v + 6)^ = (t >> 48) & 0xff + (^u8)(v + 7)^ = (t >> 56) & 0xff + } + } +} + +init_odin :: proc "contextless" (c: ^Sha3_Context) { + for i := 0; i < 25; i += 1 { + c.st.q[i] = 0 + } + c.rsiz = 200 - 2 * c.mdlen +} + +update_odin :: proc "contextless" (c: ^Sha3_Context, data: []byte) { + j := c.pt + for i := 0; i < len(data); i += 1 { + c.st.b[j] ~= data[i] + j += 1 + if j >= c.rsiz { + keccakf(&c.st.q) + j = 0 + } + } + c.pt = j +} + +final_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) { + if c.is_keccak { + c.st.b[c.pt] ~= 0x01 + } else { + c.st.b[c.pt] ~= 0x06 + } + + c.st.b[c.rsiz - 1] ~= 0x80 + keccakf(&c.st.q) + for i := 0; i < c.mdlen; i += 1 { + hash[i] = c.st.b[i] + } +} + +shake_xof_odin :: proc "contextless" (c: ^Sha3_Context) { + c.st.b[c.pt] ~= 0x1F + c.st.b[c.rsiz - 1] ~= 0x80 + keccakf(&c.st.q) + c.pt = 0 +} + +shake_out_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) { + j := c.pt + for i := 0; i < len(hash); i += 1 { + if j >= c.rsiz { + keccakf(&c.st.q) + j = 0 + } + hash[i] = c.st.b[j] + j += 1 + } + c.pt = j +} diff --git a/core/crypto/_tiger/_tiger.odin b/core/crypto/_tiger/_tiger.odin new file mode 100644 index 000000000..50caf0944 --- /dev/null +++ b/core/crypto/_tiger/_tiger.odin @@ -0,0 +1,411 @@ +package _tiger + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the Tiger hashing algorithm, as defined in +*/ + +import "../util" + +T1 := [?]u64 { + 0x02aab17cf7e90c5e, 0xac424b03e243a8ec, 0x72cd5be30dd5fcd3, 0x6d019b93f6f97f3a, + 0xcd9978ffd21f9193, 0x7573a1c9708029e2, 0xb164326b922a83c3, 0x46883eee04915870, + 0xeaace3057103ece6, 0xc54169b808a3535c, 0x4ce754918ddec47c, 0x0aa2f4dfdc0df40c, + 0x10b76f18a74dbefa, 0xc6ccb6235ad1ab6a, 0x13726121572fe2ff, 0x1a488c6f199d921e, + 0x4bc9f9f4da0007ca, 0x26f5e6f6e85241c7, 0x859079dbea5947b6, 0x4f1885c5c99e8c92, + 0xd78e761ea96f864b, 0x8e36428c52b5c17d, 0x69cf6827373063c1, 0xb607c93d9bb4c56e, + 0x7d820e760e76b5ea, 0x645c9cc6f07fdc42, 0xbf38a078243342e0, 0x5f6b343c9d2e7d04, + 0xf2c28aeb600b0ec6, 0x6c0ed85f7254bcac, 0x71592281a4db4fe5, 0x1967fa69ce0fed9f, + 0xfd5293f8b96545db, 0xc879e9d7f2a7600b, 0x860248920193194e, 0xa4f9533b2d9cc0b3, + 0x9053836c15957613, 0xdb6dcf8afc357bf1, 0x18beea7a7a370f57, 0x037117ca50b99066, + 0x6ab30a9774424a35, 0xf4e92f02e325249b, 0x7739db07061ccae1, 0xd8f3b49ceca42a05, + 0xbd56be3f51382f73, 0x45faed5843b0bb28, 0x1c813d5c11bf1f83, 0x8af0e4b6d75fa169, + 0x33ee18a487ad9999, 0x3c26e8eab1c94410, 0xb510102bc0a822f9, 0x141eef310ce6123b, + 0xfc65b90059ddb154, 0xe0158640c5e0e607, 0x884e079826c3a3cf, 0x930d0d9523c535fd, + 0x35638d754e9a2b00, 0x4085fccf40469dd5, 0xc4b17ad28be23a4c, 0xcab2f0fc6a3e6a2e, + 0x2860971a6b943fcd, 0x3dde6ee212e30446, 0x6222f32ae01765ae, 0x5d550bb5478308fe, + 0xa9efa98da0eda22a, 0xc351a71686c40da7, 0x1105586d9c867c84, 0xdcffee85fda22853, + 0xccfbd0262c5eef76, 0xbaf294cb8990d201, 0xe69464f52afad975, 0x94b013afdf133e14, + 0x06a7d1a32823c958, 0x6f95fe5130f61119, 0xd92ab34e462c06c0, 0xed7bde33887c71d2, + 0x79746d6e6518393e, 0x5ba419385d713329, 0x7c1ba6b948a97564, 0x31987c197bfdac67, + 0xde6c23c44b053d02, 0x581c49fed002d64d, 0xdd474d6338261571, 0xaa4546c3e473d062, + 0x928fce349455f860, 0x48161bbacaab94d9, 0x63912430770e6f68, 0x6ec8a5e602c6641c, + 0x87282515337ddd2b, 0x2cda6b42034b701b, 0xb03d37c181cb096d, 0xe108438266c71c6f, + 0x2b3180c7eb51b255, 0xdf92b82f96c08bbc, 0x5c68c8c0a632f3ba, 0x5504cc861c3d0556, + 0xabbfa4e55fb26b8f, 0x41848b0ab3baceb4, 0xb334a273aa445d32, 0xbca696f0a85ad881, + 0x24f6ec65b528d56c, 0x0ce1512e90f4524a, 0x4e9dd79d5506d35a, 0x258905fac6ce9779, + 0x2019295b3e109b33, 0xf8a9478b73a054cc, 0x2924f2f934417eb0, 0x3993357d536d1bc4, + 0x38a81ac21db6ff8b, 0x47c4fbf17d6016bf, 0x1e0faadd7667e3f5, 0x7abcff62938beb96, + 0xa78dad948fc179c9, 0x8f1f98b72911e50d, 0x61e48eae27121a91, 0x4d62f7ad31859808, + 0xeceba345ef5ceaeb, 0xf5ceb25ebc9684ce, 0xf633e20cb7f76221, 0xa32cdf06ab8293e4, + 0x985a202ca5ee2ca4, 0xcf0b8447cc8a8fb1, 0x9f765244979859a3, 0xa8d516b1a1240017, + 0x0bd7ba3ebb5dc726, 0xe54bca55b86adb39, 0x1d7a3afd6c478063, 0x519ec608e7669edd, + 0x0e5715a2d149aa23, 0x177d4571848ff194, 0xeeb55f3241014c22, 0x0f5e5ca13a6e2ec2, + 0x8029927b75f5c361, 0xad139fabc3d6e436, 0x0d5df1a94ccf402f, 0x3e8bd948bea5dfc8, + 0xa5a0d357bd3ff77e, 0xa2d12e251f74f645, 0x66fd9e525e81a082, 0x2e0c90ce7f687a49, + 0xc2e8bcbeba973bc5, 0x000001bce509745f, 0x423777bbe6dab3d6, 0xd1661c7eaef06eb5, + 0xa1781f354daacfd8, 0x2d11284a2b16affc, 0xf1fc4f67fa891d1f, 0x73ecc25dcb920ada, + 0xae610c22c2a12651, 0x96e0a810d356b78a, 0x5a9a381f2fe7870f, 0xd5ad62ede94e5530, + 0xd225e5e8368d1427, 0x65977b70c7af4631, 0x99f889b2de39d74f, 0x233f30bf54e1d143, + 0x9a9675d3d9a63c97, 0x5470554ff334f9a8, 0x166acb744a4f5688, 0x70c74caab2e4aead, + 0xf0d091646f294d12, 0x57b82a89684031d1, 0xefd95a5a61be0b6b, 0x2fbd12e969f2f29a, + 0x9bd37013feff9fe8, 0x3f9b0404d6085a06, 0x4940c1f3166cfe15, 0x09542c4dcdf3defb, + 0xb4c5218385cd5ce3, 0xc935b7dc4462a641, 0x3417f8a68ed3b63f, 0xb80959295b215b40, + 0xf99cdaef3b8c8572, 0x018c0614f8fcb95d, 0x1b14accd1a3acdf3, 0x84d471f200bb732d, + 0xc1a3110e95e8da16, 0x430a7220bf1a82b8, 0xb77e090d39df210e, 0x5ef4bd9f3cd05e9d, + 0x9d4ff6da7e57a444, 0xda1d60e183d4a5f8, 0xb287c38417998e47, 0xfe3edc121bb31886, + 0xc7fe3ccc980ccbef, 0xe46fb590189bfd03, 0x3732fd469a4c57dc, 0x7ef700a07cf1ad65, + 0x59c64468a31d8859, 0x762fb0b4d45b61f6, 0x155baed099047718, 0x68755e4c3d50baa6, + 0xe9214e7f22d8b4df, 0x2addbf532eac95f4, 0x32ae3909b4bd0109, 0x834df537b08e3450, + 0xfa209da84220728d, 0x9e691d9b9efe23f7, 0x0446d288c4ae8d7f, 0x7b4cc524e169785b, + 0x21d87f0135ca1385, 0xcebb400f137b8aa5, 0x272e2b66580796be, 0x3612264125c2b0de, + 0x057702bdad1efbb2, 0xd4babb8eacf84be9, 0x91583139641bc67b, 0x8bdc2de08036e024, + 0x603c8156f49f68ed, 0xf7d236f7dbef5111, 0x9727c4598ad21e80, 0xa08a0896670a5fd7, + 0xcb4a8f4309eba9cb, 0x81af564b0f7036a1, 0xc0b99aa778199abd, 0x959f1ec83fc8e952, + 0x8c505077794a81b9, 0x3acaaf8f056338f0, 0x07b43f50627a6778, 0x4a44ab49f5eccc77, + 0x3bc3d6e4b679ee98, 0x9cc0d4d1cf14108c, 0x4406c00b206bc8a0, 0x82a18854c8d72d89, + 0x67e366b35c3c432c, 0xb923dd61102b37f2, 0x56ab2779d884271d, 0xbe83e1b0ff1525af, + 0xfb7c65d4217e49a9, 0x6bdbe0e76d48e7d4, 0x08df828745d9179e, 0x22ea6a9add53bd34, + 0xe36e141c5622200a, 0x7f805d1b8cb750ee, 0xafe5c7a59f58e837, 0xe27f996a4fb1c23c, + 0xd3867dfb0775f0d0, 0xd0e673de6e88891a, 0x123aeb9eafb86c25, 0x30f1d5d5c145b895, + 0xbb434a2dee7269e7, 0x78cb67ecf931fa38, 0xf33b0372323bbf9c, 0x52d66336fb279c74, + 0x505f33ac0afb4eaa, 0xe8a5cd99a2cce187, 0x534974801e2d30bb, 0x8d2d5711d5876d90, + 0x1f1a412891bc038e, 0xd6e2e71d82e56648, 0x74036c3a497732b7, 0x89b67ed96361f5ab, + 0xffed95d8f1ea02a2, 0xe72b3bd61464d43d, 0xa6300f170bdc4820, 0xebc18760ed78a77a, +} + +T2 := [?]u64 { + 0xe6a6be5a05a12138, 0xb5a122a5b4f87c98, 0x563c6089140b6990, 0x4c46cb2e391f5dd5, + 0xd932addbc9b79434, 0x08ea70e42015aff5, 0xd765a6673e478cf1, 0xc4fb757eab278d99, + 0xdf11c6862d6e0692, 0xddeb84f10d7f3b16, 0x6f2ef604a665ea04, 0x4a8e0f0ff0e0dfb3, + 0xa5edeef83dbcba51, 0xfc4f0a2a0ea4371e, 0xe83e1da85cb38429, 0xdc8ff882ba1b1ce2, + 0xcd45505e8353e80d, 0x18d19a00d4db0717, 0x34a0cfeda5f38101, 0x0be77e518887caf2, + 0x1e341438b3c45136, 0xe05797f49089ccf9, 0xffd23f9df2591d14, 0x543dda228595c5cd, + 0x661f81fd99052a33, 0x8736e641db0f7b76, 0x15227725418e5307, 0xe25f7f46162eb2fa, + 0x48a8b2126c13d9fe, 0xafdc541792e76eea, 0x03d912bfc6d1898f, 0x31b1aafa1b83f51b, + 0xf1ac2796e42ab7d9, 0x40a3a7d7fcd2ebac, 0x1056136d0afbbcc5, 0x7889e1dd9a6d0c85, + 0xd33525782a7974aa, 0xa7e25d09078ac09b, 0xbd4138b3eac6edd0, 0x920abfbe71eb9e70, + 0xa2a5d0f54fc2625c, 0xc054e36b0b1290a3, 0xf6dd59ff62fe932b, 0x3537354511a8ac7d, + 0xca845e9172fadcd4, 0x84f82b60329d20dc, 0x79c62ce1cd672f18, 0x8b09a2add124642c, + 0xd0c1e96a19d9e726, 0x5a786a9b4ba9500c, 0x0e020336634c43f3, 0xc17b474aeb66d822, + 0x6a731ae3ec9baac2, 0x8226667ae0840258, 0x67d4567691caeca5, 0x1d94155c4875adb5, + 0x6d00fd985b813fdf, 0x51286efcb774cd06, 0x5e8834471fa744af, 0xf72ca0aee761ae2e, + 0xbe40e4cdaee8e09a, 0xe9970bbb5118f665, 0x726e4beb33df1964, 0x703b000729199762, + 0x4631d816f5ef30a7, 0xb880b5b51504a6be, 0x641793c37ed84b6c, 0x7b21ed77f6e97d96, + 0x776306312ef96b73, 0xae528948e86ff3f4, 0x53dbd7f286a3f8f8, 0x16cadce74cfc1063, + 0x005c19bdfa52c6dd, 0x68868f5d64d46ad3, 0x3a9d512ccf1e186a, 0x367e62c2385660ae, + 0xe359e7ea77dcb1d7, 0x526c0773749abe6e, 0x735ae5f9d09f734b, 0x493fc7cc8a558ba8, + 0xb0b9c1533041ab45, 0x321958ba470a59bd, 0x852db00b5f46c393, 0x91209b2bd336b0e5, + 0x6e604f7d659ef19f, 0xb99a8ae2782ccb24, 0xccf52ab6c814c4c7, 0x4727d9afbe11727b, + 0x7e950d0c0121b34d, 0x756f435670ad471f, 0xf5add442615a6849, 0x4e87e09980b9957a, + 0x2acfa1df50aee355, 0xd898263afd2fd556, 0xc8f4924dd80c8fd6, 0xcf99ca3d754a173a, + 0xfe477bacaf91bf3c, 0xed5371f6d690c12d, 0x831a5c285e687094, 0xc5d3c90a3708a0a4, + 0x0f7f903717d06580, 0x19f9bb13b8fdf27f, 0xb1bd6f1b4d502843, 0x1c761ba38fff4012, + 0x0d1530c4e2e21f3b, 0x8943ce69a7372c8a, 0xe5184e11feb5ce66, 0x618bdb80bd736621, + 0x7d29bad68b574d0b, 0x81bb613e25e6fe5b, 0x071c9c10bc07913f, 0xc7beeb7909ac2d97, + 0xc3e58d353bc5d757, 0xeb017892f38f61e8, 0xd4effb9c9b1cc21a, 0x99727d26f494f7ab, + 0xa3e063a2956b3e03, 0x9d4a8b9a4aa09c30, 0x3f6ab7d500090fb4, 0x9cc0f2a057268ac0, + 0x3dee9d2dedbf42d1, 0x330f49c87960a972, 0xc6b2720287421b41, 0x0ac59ec07c00369c, + 0xef4eac49cb353425, 0xf450244eef0129d8, 0x8acc46e5caf4deb6, 0x2ffeab63989263f7, + 0x8f7cb9fe5d7a4578, 0x5bd8f7644e634635, 0x427a7315bf2dc900, 0x17d0c4aa2125261c, + 0x3992486c93518e50, 0xb4cbfee0a2d7d4c3, 0x7c75d6202c5ddd8d, 0xdbc295d8e35b6c61, + 0x60b369d302032b19, 0xce42685fdce44132, 0x06f3ddb9ddf65610, 0x8ea4d21db5e148f0, + 0x20b0fce62fcd496f, 0x2c1b912358b0ee31, 0xb28317b818f5a308, 0xa89c1e189ca6d2cf, + 0x0c6b18576aaadbc8, 0xb65deaa91299fae3, 0xfb2b794b7f1027e7, 0x04e4317f443b5beb, + 0x4b852d325939d0a6, 0xd5ae6beefb207ffc, 0x309682b281c7d374, 0xbae309a194c3b475, + 0x8cc3f97b13b49f05, 0x98a9422ff8293967, 0x244b16b01076ff7c, 0xf8bf571c663d67ee, + 0x1f0d6758eee30da1, 0xc9b611d97adeb9b7, 0xb7afd5887b6c57a2, 0x6290ae846b984fe1, + 0x94df4cdeacc1a5fd, 0x058a5bd1c5483aff, 0x63166cc142ba3c37, 0x8db8526eb2f76f40, + 0xe10880036f0d6d4e, 0x9e0523c9971d311d, 0x45ec2824cc7cd691, 0x575b8359e62382c9, + 0xfa9e400dc4889995, 0xd1823ecb45721568, 0xdafd983b8206082f, 0xaa7d29082386a8cb, + 0x269fcd4403b87588, 0x1b91f5f728bdd1e0, 0xe4669f39040201f6, 0x7a1d7c218cf04ade, + 0x65623c29d79ce5ce, 0x2368449096c00bb1, 0xab9bf1879da503ba, 0xbc23ecb1a458058e, + 0x9a58df01bb401ecc, 0xa070e868a85f143d, 0x4ff188307df2239e, 0x14d565b41a641183, + 0xee13337452701602, 0x950e3dcf3f285e09, 0x59930254b9c80953, 0x3bf299408930da6d, + 0xa955943f53691387, 0xa15edecaa9cb8784, 0x29142127352be9a0, 0x76f0371fff4e7afb, + 0x0239f450274f2228, 0xbb073af01d5e868b, 0xbfc80571c10e96c1, 0xd267088568222e23, + 0x9671a3d48e80b5b0, 0x55b5d38ae193bb81, 0x693ae2d0a18b04b8, 0x5c48b4ecadd5335f, + 0xfd743b194916a1ca, 0x2577018134be98c4, 0xe77987e83c54a4ad, 0x28e11014da33e1b9, + 0x270cc59e226aa213, 0x71495f756d1a5f60, 0x9be853fb60afef77, 0xadc786a7f7443dbf, + 0x0904456173b29a82, 0x58bc7a66c232bd5e, 0xf306558c673ac8b2, 0x41f639c6b6c9772a, + 0x216defe99fda35da, 0x11640cc71c7be615, 0x93c43694565c5527, 0xea038e6246777839, + 0xf9abf3ce5a3e2469, 0x741e768d0fd312d2, 0x0144b883ced652c6, 0xc20b5a5ba33f8552, + 0x1ae69633c3435a9d, 0x97a28ca4088cfdec, 0x8824a43c1e96f420, 0x37612fa66eeea746, + 0x6b4cb165f9cf0e5a, 0x43aa1c06a0abfb4a, 0x7f4dc26ff162796b, 0x6cbacc8e54ed9b0f, + 0xa6b7ffefd2bb253e, 0x2e25bc95b0a29d4f, 0x86d6a58bdef1388c, 0xded74ac576b6f054, + 0x8030bdbc2b45805d, 0x3c81af70e94d9289, 0x3eff6dda9e3100db, 0xb38dc39fdfcc8847, + 0x123885528d17b87e, 0xf2da0ed240b1b642, 0x44cefadcd54bf9a9, 0x1312200e433c7ee6, + 0x9ffcc84f3a78c748, 0xf0cd1f72248576bb, 0xec6974053638cfe4, 0x2ba7b67c0cec4e4c, + 0xac2f4df3e5ce32ed, 0xcb33d14326ea4c11, 0xa4e9044cc77e58bc, 0x5f513293d934fcef, + 0x5dc9645506e55444, 0x50de418f317de40a, 0x388cb31a69dde259, 0x2db4a83455820a86, + 0x9010a91e84711ae9, 0x4df7f0b7b1498371, 0xd62a2eabc0977179, 0x22fac097aa8d5c0e, +} + +T3 := [?]u64 { + 0xf49fcc2ff1daf39b, 0x487fd5c66ff29281, 0xe8a30667fcdca83f, 0x2c9b4be3d2fcce63, + 0xda3ff74b93fbbbc2, 0x2fa165d2fe70ba66, 0xa103e279970e93d4, 0xbecdec77b0e45e71, + 0xcfb41e723985e497, 0xb70aaa025ef75017, 0xd42309f03840b8e0, 0x8efc1ad035898579, + 0x96c6920be2b2abc5, 0x66af4163375a9172, 0x2174abdcca7127fb, 0xb33ccea64a72ff41, + 0xf04a4933083066a5, 0x8d970acdd7289af5, 0x8f96e8e031c8c25e, 0xf3fec02276875d47, + 0xec7bf310056190dd, 0xf5adb0aebb0f1491, 0x9b50f8850fd58892, 0x4975488358b74de8, + 0xa3354ff691531c61, 0x0702bbe481d2c6ee, 0x89fb24057deded98, 0xac3075138596e902, + 0x1d2d3580172772ed, 0xeb738fc28e6bc30d, 0x5854ef8f63044326, 0x9e5c52325add3bbe, + 0x90aa53cf325c4623, 0xc1d24d51349dd067, 0x2051cfeea69ea624, 0x13220f0a862e7e4f, + 0xce39399404e04864, 0xd9c42ca47086fcb7, 0x685ad2238a03e7cc, 0x066484b2ab2ff1db, + 0xfe9d5d70efbf79ec, 0x5b13b9dd9c481854, 0x15f0d475ed1509ad, 0x0bebcd060ec79851, + 0xd58c6791183ab7f8, 0xd1187c5052f3eee4, 0xc95d1192e54e82ff, 0x86eea14cb9ac6ca2, + 0x3485beb153677d5d, 0xdd191d781f8c492a, 0xf60866baa784ebf9, 0x518f643ba2d08c74, + 0x8852e956e1087c22, 0xa768cb8dc410ae8d, 0x38047726bfec8e1a, 0xa67738b4cd3b45aa, + 0xad16691cec0dde19, 0xc6d4319380462e07, 0xc5a5876d0ba61938, 0x16b9fa1fa58fd840, + 0x188ab1173ca74f18, 0xabda2f98c99c021f, 0x3e0580ab134ae816, 0x5f3b05b773645abb, + 0x2501a2be5575f2f6, 0x1b2f74004e7e8ba9, 0x1cd7580371e8d953, 0x7f6ed89562764e30, + 0xb15926ff596f003d, 0x9f65293da8c5d6b9, 0x6ecef04dd690f84c, 0x4782275fff33af88, + 0xe41433083f820801, 0xfd0dfe409a1af9b5, 0x4325a3342cdb396b, 0x8ae77e62b301b252, + 0xc36f9e9f6655615a, 0x85455a2d92d32c09, 0xf2c7dea949477485, 0x63cfb4c133a39eba, + 0x83b040cc6ebc5462, 0x3b9454c8fdb326b0, 0x56f56a9e87ffd78c, 0x2dc2940d99f42bc6, + 0x98f7df096b096e2d, 0x19a6e01e3ad852bf, 0x42a99ccbdbd4b40b, 0xa59998af45e9c559, + 0x366295e807d93186, 0x6b48181bfaa1f773, 0x1fec57e2157a0a1d, 0x4667446af6201ad5, + 0xe615ebcacfb0f075, 0xb8f31f4f68290778, 0x22713ed6ce22d11e, 0x3057c1a72ec3c93b, + 0xcb46acc37c3f1f2f, 0xdbb893fd02aaf50e, 0x331fd92e600b9fcf, 0xa498f96148ea3ad6, + 0xa8d8426e8b6a83ea, 0xa089b274b7735cdc, 0x87f6b3731e524a11, 0x118808e5cbc96749, + 0x9906e4c7b19bd394, 0xafed7f7e9b24a20c, 0x6509eadeeb3644a7, 0x6c1ef1d3e8ef0ede, + 0xb9c97d43e9798fb4, 0xa2f2d784740c28a3, 0x7b8496476197566f, 0x7a5be3e6b65f069d, + 0xf96330ed78be6f10, 0xeee60de77a076a15, 0x2b4bee4aa08b9bd0, 0x6a56a63ec7b8894e, + 0x02121359ba34fef4, 0x4cbf99f8283703fc, 0x398071350caf30c8, 0xd0a77a89f017687a, + 0xf1c1a9eb9e423569, 0x8c7976282dee8199, 0x5d1737a5dd1f7abd, 0x4f53433c09a9fa80, + 0xfa8b0c53df7ca1d9, 0x3fd9dcbc886ccb77, 0xc040917ca91b4720, 0x7dd00142f9d1dcdf, + 0x8476fc1d4f387b58, 0x23f8e7c5f3316503, 0x032a2244e7e37339, 0x5c87a5d750f5a74b, + 0x082b4cc43698992e, 0xdf917becb858f63c, 0x3270b8fc5bf86dda, 0x10ae72bb29b5dd76, + 0x576ac94e7700362b, 0x1ad112dac61efb8f, 0x691bc30ec5faa427, 0xff246311cc327143, + 0x3142368e30e53206, 0x71380e31e02ca396, 0x958d5c960aad76f1, 0xf8d6f430c16da536, + 0xc8ffd13f1be7e1d2, 0x7578ae66004ddbe1, 0x05833f01067be646, 0xbb34b5ad3bfe586d, + 0x095f34c9a12b97f0, 0x247ab64525d60ca8, 0xdcdbc6f3017477d1, 0x4a2e14d4decad24d, + 0xbdb5e6d9be0a1eeb, 0x2a7e70f7794301ab, 0xdef42d8a270540fd, 0x01078ec0a34c22c1, + 0xe5de511af4c16387, 0x7ebb3a52bd9a330a, 0x77697857aa7d6435, 0x004e831603ae4c32, + 0xe7a21020ad78e312, 0x9d41a70c6ab420f2, 0x28e06c18ea1141e6, 0xd2b28cbd984f6b28, + 0x26b75f6c446e9d83, 0xba47568c4d418d7f, 0xd80badbfe6183d8e, 0x0e206d7f5f166044, + 0xe258a43911cbca3e, 0x723a1746b21dc0bc, 0xc7caa854f5d7cdd3, 0x7cac32883d261d9c, + 0x7690c26423ba942c, 0x17e55524478042b8, 0xe0be477656a2389f, 0x4d289b5e67ab2da0, + 0x44862b9c8fbbfd31, 0xb47cc8049d141365, 0x822c1b362b91c793, 0x4eb14655fb13dfd8, + 0x1ecbba0714e2a97b, 0x6143459d5cde5f14, 0x53a8fbf1d5f0ac89, 0x97ea04d81c5e5b00, + 0x622181a8d4fdb3f3, 0xe9bcd341572a1208, 0x1411258643cce58a, 0x9144c5fea4c6e0a4, + 0x0d33d06565cf620f, 0x54a48d489f219ca1, 0xc43e5eac6d63c821, 0xa9728b3a72770daf, + 0xd7934e7b20df87ef, 0xe35503b61a3e86e5, 0xcae321fbc819d504, 0x129a50b3ac60bfa6, + 0xcd5e68ea7e9fb6c3, 0xb01c90199483b1c7, 0x3de93cd5c295376c, 0xaed52edf2ab9ad13, + 0x2e60f512c0a07884, 0xbc3d86a3e36210c9, 0x35269d9b163951ce, 0x0c7d6e2ad0cdb5fa, + 0x59e86297d87f5733, 0x298ef221898db0e7, 0x55000029d1a5aa7e, 0x8bc08ae1b5061b45, + 0xc2c31c2b6c92703a, 0x94cc596baf25ef42, 0x0a1d73db22540456, 0x04b6a0f9d9c4179a, + 0xeffdafa2ae3d3c60, 0xf7c8075bb49496c4, 0x9cc5c7141d1cd4e3, 0x78bd1638218e5534, + 0xb2f11568f850246a, 0xedfabcfa9502bc29, 0x796ce5f2da23051b, 0xaae128b0dc93537c, + 0x3a493da0ee4b29ae, 0xb5df6b2c416895d7, 0xfcabbd25122d7f37, 0x70810b58105dc4b1, + 0xe10fdd37f7882a90, 0x524dcab5518a3f5c, 0x3c9e85878451255b, 0x4029828119bd34e2, + 0x74a05b6f5d3ceccb, 0xb610021542e13eca, 0x0ff979d12f59e2ac, 0x6037da27e4f9cc50, + 0x5e92975a0df1847d, 0xd66de190d3e623fe, 0x5032d6b87b568048, 0x9a36b7ce8235216e, + 0x80272a7a24f64b4a, 0x93efed8b8c6916f7, 0x37ddbff44cce1555, 0x4b95db5d4b99bd25, + 0x92d3fda169812fc0, 0xfb1a4a9a90660bb6, 0x730c196946a4b9b2, 0x81e289aa7f49da68, + 0x64669a0f83b1a05f, 0x27b3ff7d9644f48b, 0xcc6b615c8db675b3, 0x674f20b9bcebbe95, + 0x6f31238275655982, 0x5ae488713e45cf05, 0xbf619f9954c21157, 0xeabac46040a8eae9, + 0x454c6fe9f2c0c1cd, 0x419cf6496412691c, 0xd3dc3bef265b0f70, 0x6d0e60f5c3578a9e, +} + +T4 := [?]u64 { + 0x5b0e608526323c55, 0x1a46c1a9fa1b59f5, 0xa9e245a17c4c8ffa, 0x65ca5159db2955d7, + 0x05db0a76ce35afc2, 0x81eac77ea9113d45, 0x528ef88ab6ac0a0d, 0xa09ea253597be3ff, + 0x430ddfb3ac48cd56, 0xc4b3a67af45ce46f, 0x4ececfd8fbe2d05e, 0x3ef56f10b39935f0, + 0x0b22d6829cd619c6, 0x17fd460a74df2069, 0x6cf8cc8e8510ed40, 0xd6c824bf3a6ecaa7, + 0x61243d581a817049, 0x048bacb6bbc163a2, 0xd9a38ac27d44cc32, 0x7fddff5baaf410ab, + 0xad6d495aa804824b, 0xe1a6a74f2d8c9f94, 0xd4f7851235dee8e3, 0xfd4b7f886540d893, + 0x247c20042aa4bfda, 0x096ea1c517d1327c, 0xd56966b4361a6685, 0x277da5c31221057d, + 0x94d59893a43acff7, 0x64f0c51ccdc02281, 0x3d33bcc4ff6189db, 0xe005cb184ce66af1, + 0xff5ccd1d1db99bea, 0xb0b854a7fe42980f, 0x7bd46a6a718d4b9f, 0xd10fa8cc22a5fd8c, + 0xd31484952be4bd31, 0xc7fa975fcb243847, 0x4886ed1e5846c407, 0x28cddb791eb70b04, + 0xc2b00be2f573417f, 0x5c9590452180f877, 0x7a6bddfff370eb00, 0xce509e38d6d9d6a4, + 0xebeb0f00647fa702, 0x1dcc06cf76606f06, 0xe4d9f28ba286ff0a, 0xd85a305dc918c262, + 0x475b1d8732225f54, 0x2d4fb51668ccb5fe, 0xa679b9d9d72bba20, 0x53841c0d912d43a5, + 0x3b7eaa48bf12a4e8, 0x781e0e47f22f1ddf, 0xeff20ce60ab50973, 0x20d261d19dffb742, + 0x16a12b03062a2e39, 0x1960eb2239650495, 0x251c16fed50eb8b8, 0x9ac0c330f826016e, + 0xed152665953e7671, 0x02d63194a6369570, 0x5074f08394b1c987, 0x70ba598c90b25ce1, + 0x794a15810b9742f6, 0x0d5925e9fcaf8c6c, 0x3067716cd868744e, 0x910ab077e8d7731b, + 0x6a61bbdb5ac42f61, 0x93513efbf0851567, 0xf494724b9e83e9d5, 0xe887e1985c09648d, + 0x34b1d3c675370cfd, 0xdc35e433bc0d255d, 0xd0aab84234131be0, 0x08042a50b48b7eaf, + 0x9997c4ee44a3ab35, 0x829a7b49201799d0, 0x263b8307b7c54441, 0x752f95f4fd6a6ca6, + 0x927217402c08c6e5, 0x2a8ab754a795d9ee, 0xa442f7552f72943d, 0x2c31334e19781208, + 0x4fa98d7ceaee6291, 0x55c3862f665db309, 0xbd0610175d53b1f3, 0x46fe6cb840413f27, + 0x3fe03792df0cfa59, 0xcfe700372eb85e8f, 0xa7be29e7adbce118, 0xe544ee5cde8431dd, + 0x8a781b1b41f1873e, 0xa5c94c78a0d2f0e7, 0x39412e2877b60728, 0xa1265ef3afc9a62c, + 0xbcc2770c6a2506c5, 0x3ab66dd5dce1ce12, 0xe65499d04a675b37, 0x7d8f523481bfd216, + 0x0f6f64fcec15f389, 0x74efbe618b5b13c8, 0xacdc82b714273e1d, 0xdd40bfe003199d17, + 0x37e99257e7e061f8, 0xfa52626904775aaa, 0x8bbbf63a463d56f9, 0xf0013f1543a26e64, + 0xa8307e9f879ec898, 0xcc4c27a4150177cc, 0x1b432f2cca1d3348, 0xde1d1f8f9f6fa013, + 0x606602a047a7ddd6, 0xd237ab64cc1cb2c7, 0x9b938e7225fcd1d3, 0xec4e03708e0ff476, + 0xfeb2fbda3d03c12d, 0xae0bced2ee43889a, 0x22cb8923ebfb4f43, 0x69360d013cf7396d, + 0x855e3602d2d4e022, 0x073805bad01f784c, 0x33e17a133852f546, 0xdf4874058ac7b638, + 0xba92b29c678aa14a, 0x0ce89fc76cfaadcd, 0x5f9d4e0908339e34, 0xf1afe9291f5923b9, + 0x6e3480f60f4a265f, 0xeebf3a2ab29b841c, 0xe21938a88f91b4ad, 0x57dfeff845c6d3c3, + 0x2f006b0bf62caaf2, 0x62f479ef6f75ee78, 0x11a55ad41c8916a9, 0xf229d29084fed453, + 0x42f1c27b16b000e6, 0x2b1f76749823c074, 0x4b76eca3c2745360, 0x8c98f463b91691bd, + 0x14bcc93cf1ade66a, 0x8885213e6d458397, 0x8e177df0274d4711, 0xb49b73b5503f2951, + 0x10168168c3f96b6b, 0x0e3d963b63cab0ae, 0x8dfc4b5655a1db14, 0xf789f1356e14de5c, + 0x683e68af4e51dac1, 0xc9a84f9d8d4b0fd9, 0x3691e03f52a0f9d1, 0x5ed86e46e1878e80, + 0x3c711a0e99d07150, 0x5a0865b20c4e9310, 0x56fbfc1fe4f0682e, 0xea8d5de3105edf9b, + 0x71abfdb12379187a, 0x2eb99de1bee77b9c, 0x21ecc0ea33cf4523, 0x59a4d7521805c7a1, + 0x3896f5eb56ae7c72, 0xaa638f3db18f75dc, 0x9f39358dabe9808e, 0xb7defa91c00b72ac, + 0x6b5541fd62492d92, 0x6dc6dee8f92e4d5b, 0x353f57abc4beea7e, 0x735769d6da5690ce, + 0x0a234aa642391484, 0xf6f9508028f80d9d, 0xb8e319a27ab3f215, 0x31ad9c1151341a4d, + 0x773c22a57bef5805, 0x45c7561a07968633, 0xf913da9e249dbe36, 0xda652d9b78a64c68, + 0x4c27a97f3bc334ef, 0x76621220e66b17f4, 0x967743899acd7d0b, 0xf3ee5bcae0ed6782, + 0x409f753600c879fc, 0x06d09a39b5926db6, 0x6f83aeb0317ac588, 0x01e6ca4a86381f21, + 0x66ff3462d19f3025, 0x72207c24ddfd3bfb, 0x4af6b6d3e2ece2eb, 0x9c994dbec7ea08de, + 0x49ace597b09a8bc4, 0xb38c4766cf0797ba, 0x131b9373c57c2a75, 0xb1822cce61931e58, + 0x9d7555b909ba1c0c, 0x127fafdd937d11d2, 0x29da3badc66d92e4, 0xa2c1d57154c2ecbc, + 0x58c5134d82f6fe24, 0x1c3ae3515b62274f, 0xe907c82e01cb8126, 0xf8ed091913e37fcb, + 0x3249d8f9c80046c9, 0x80cf9bede388fb63, 0x1881539a116cf19e, 0x5103f3f76bd52457, + 0x15b7e6f5ae47f7a8, 0xdbd7c6ded47e9ccf, 0x44e55c410228bb1a, 0xb647d4255edb4e99, + 0x5d11882bb8aafc30, 0xf5098bbb29d3212a, 0x8fb5ea14e90296b3, 0x677b942157dd025a, + 0xfb58e7c0a390acb5, 0x89d3674c83bd4a01, 0x9e2da4df4bf3b93b, 0xfcc41e328cab4829, + 0x03f38c96ba582c52, 0xcad1bdbd7fd85db2, 0xbbb442c16082ae83, 0xb95fe86ba5da9ab0, + 0xb22e04673771a93f, 0x845358c9493152d8, 0xbe2a488697b4541e, 0x95a2dc2dd38e6966, + 0xc02c11ac923c852b, 0x2388b1990df2a87b, 0x7c8008fa1b4f37be, 0x1f70d0c84d54e503, + 0x5490adec7ece57d4, 0x002b3c27d9063a3a, 0x7eaea3848030a2bf, 0xc602326ded2003c0, + 0x83a7287d69a94086, 0xc57a5fcb30f57a8a, 0xb56844e479ebe779, 0xa373b40f05dcbce9, + 0xd71a786e88570ee2, 0x879cbacdbde8f6a0, 0x976ad1bcc164a32f, 0xab21e25e9666d78b, + 0x901063aae5e5c33c, 0x9818b34448698d90, 0xe36487ae3e1e8abb, 0xafbdf931893bdcb4, + 0x6345a0dc5fbbd519, 0x8628fe269b9465ca, 0x1e5d01603f9c51ec, 0x4de44006a15049b7, + 0xbf6c70e5f776cbb1, 0x411218f2ef552bed, 0xcb0c0708705a36a3, 0xe74d14754f986044, + 0xcd56d9430ea8280e, 0xc12591d7535f5065, 0xc83223f1720aef96, 0xc3a0396f7363a51f, +} + +Tiger_Context :: struct { + a: u64, + b: u64, + c: u64, + x: [64]byte, + nx: int, + length: u64, + ver: int, +} + +round :: #force_inline proc "contextless"(a, b, c, x, mul: u64) -> (u64, u64, u64) { + a, b, c := a, b, c + c ~= x + a -= T1[c & 0xff] ~ T2[(c >> 16) & 0xff] ~ T3[(c >> 32) & 0xff] ~ T4[(c >> 48) & 0xff] + b += T4[(c >> 8) & 0xff] ~ T3[(c >> 24) & 0xff] ~ T2[(c >> 40) & 0xff] ~ T1[(c >> 56) & 0xff] + b *= mul + return a, b, c +} + +pass :: #force_inline proc "contextless"(a, b, c: u64, d: []u64, mul: u64) -> (x, y, z: u64) { + x, y, z = round(a, b, c, d[0], mul) + y, z, x = round(y, z, x, d[1], mul) + z, x, y = round(z, x, y, d[2], mul) + x, y, z = round(x, y, z, d[3], mul) + y, z, x = round(y, z, x, d[4], mul) + z, x, y = round(z, x, y, d[5], mul) + x, y, z = round(x, y, z, d[6], mul) + y, z, x = round(y, z, x, d[7], mul) + return +} + +key_schedule :: #force_inline proc "contextless"(x: []u64) { + x[0] -= x[7] ~ 0xa5a5a5a5a5a5a5a5 + x[1] ~= x[0] + x[2] += x[1] + x[3] -= x[2] ~ ((~x[1]) << 19) + x[4] ~= x[3] + x[5] += x[4] + x[6] -= x[5] ~ ((~x[4]) >> 23) + x[7] ~= x[6] + x[0] += x[7] + x[1] -= x[0] ~ ((~x[7]) << 19) + x[2] ~= x[1] + x[3] += x[2] + x[4] -= x[3] ~ ((~x[2]) >> 23) + x[5] ~= x[4] + x[6] += x[5] + x[7] -= x[6] ~ 0x0123456789abcdef +} + +compress :: #force_inline proc "contextless"(ctx: ^Tiger_Context, data: []byte) { + a := ctx.a + b := ctx.b + c := ctx.c + x := util.cast_slice([]u64, data) + ctx.a, ctx.b, ctx.c = pass(ctx.a, ctx.b, ctx.c, x, 5) + key_schedule(x) + ctx.c, ctx.a, ctx.b = pass(ctx.c, ctx.a, ctx.b, x, 7) + key_schedule(x) + ctx.b, ctx.c, ctx.a = pass(ctx.b, ctx.c, ctx.a, x, 9) + ctx.a ~= a + ctx.b -= b + ctx.c += c +} + +init_odin :: proc(ctx: ^Tiger_Context) { + ctx.a = 0x0123456789abcdef + ctx.b = 0xfedcba9876543210 + ctx.c = 0xf096a5b4c3b2e187 +} + +update_odin :: proc(ctx: ^Tiger_Context, input: []byte) { + p := make([]byte, len(input)) + copy(p, input) + + length := len(p) + ctx.length += u64(length) + if ctx.nx > 0 { + n := len(p) + if n > 64 - ctx.nx { + n = 64 - ctx.nx + } + copy(ctx.x[ctx.nx:ctx.nx + n], p[:n]) + ctx.nx += n + if ctx.nx == 64 { + compress(ctx, ctx.x[:64 - 1]) + ctx.nx = 0 + } + p = p[n:] + } + for len(p) >= 64 { + compress(ctx, p[:64]) + p = p[64:] + } + if len(p) > 0 { + ctx.nx = copy(ctx.x[:], p) + } +} + +final_odin :: proc(ctx: ^Tiger_Context, hash: []byte) { + length := ctx.length + tmp: [64]byte + if ctx.ver == 1 { + tmp[0] = 0x01 + } else { + tmp[0] = 0x80 + } + + size := length & 0x3f + if size < 56 { + update_odin(ctx, tmp[:56 - size]) + } else { + update_odin(ctx, tmp[:64 + 56 - size]) + } + + length <<= 3 + for i := uint(0); i < 8; i += 1 { + tmp[i] = byte(length >> (8 * i)) + } + update_odin(ctx, tmp[:8]) + + for i := uint(0); i < 8; i += 1 { + tmp[i] = byte(ctx.a >> (8 * i)) + tmp[i + 8] = byte(ctx.b >> (8 * i)) + tmp[i + 16] = byte(ctx.c >> (8 * i)) + } + copy(hash[:], tmp[:len(hash)]) +} \ No newline at end of file diff --git a/core/crypto/blake/blake.odin b/core/crypto/blake/blake.odin new file mode 100644 index 000000000..aecd57ab1 --- /dev/null +++ b/core/crypto/blake/blake.odin @@ -0,0 +1,846 @@ +package blake + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the BLAKE hashing algorithm, as defined in +*/ + +import "core:os" +import "core:io" + +import "../_ctx" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_28 = hash_bytes_odin_28 + ctx.hash_file_28 = hash_file_odin_28 + ctx.hash_stream_28 = hash_stream_odin_28 + ctx.hash_bytes_32 = hash_bytes_odin_32 + ctx.hash_file_32 = hash_file_odin_32 + ctx.hash_stream_32 = hash_stream_odin_32 + ctx.hash_bytes_48 = hash_bytes_odin_48 + ctx.hash_file_48 = hash_file_odin_48 + ctx.hash_stream_48 = hash_stream_odin_48 + ctx.hash_bytes_64 = hash_bytes_odin_64 + ctx.hash_file_64 = hash_file_odin_64 + ctx.hash_stream_64 = hash_stream_odin_64 + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan does nothing, since BLAKE is not available in Botan +@(warning="BLAKE is not provided by the Botan API. Odin implementation will be used") +use_botan :: #force_inline proc() { + use_odin() +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +@(private) +_create_blake256_ctx :: #force_inline proc(is224: bool, size: _ctx.Hash_Size) { + ctx: Blake256_Context + ctx.is224 = is224 + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = size +} + +@(private) +_create_blake512_ctx :: #force_inline proc(is384: bool, size: _ctx.Hash_Size) { + ctx: Blake512_Context + ctx.is384 = is384 + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = size +} + +/* + High level API +*/ + +// hash_string_224 will hash the given input and return the +// computed hash +hash_string_224 :: proc(data: string) -> [28]byte { + return hash_bytes_224(transmute([]byte)(data)) +} + +// hash_bytes_224 will hash the given input and return the +// computed hash +hash_bytes_224 :: proc(data: []byte) -> [28]byte { + _create_blake256_ctx(true, ._28) + return _hash_impl->hash_bytes_28(data) +} + +// hash_stream_224 will read the stream in chunks and compute a +// hash from its contents +hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) { + _create_blake256_ctx(true, ._28) + return _hash_impl->hash_stream_28(s) +} + +// hash_file_224 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_224 :: proc(path: string, load_at_once: bool) -> ([28]byte, bool) { + _create_blake256_ctx(true, ._28) + return _hash_impl->hash_file_28(path, load_at_once) +} + +hash_224 :: proc { + hash_stream_224, + hash_file_224, + hash_bytes_224, + hash_string_224, +} + +// hash_string_256 will hash the given input and return the +// computed hash +hash_string_256 :: proc(data: string) -> [32]byte { + return hash_bytes_256(transmute([]byte)(data)) +} + +// hash_bytes_256 will hash the given input and return the +// computed hash +hash_bytes_256 :: proc(data: []byte) -> [32]byte { + _create_blake256_ctx(false, ._32) + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream_256 will read the stream in chunks and compute a +// hash from its contents +hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_blake256_ctx(false, ._32) + return _hash_impl->hash_stream_32(s) +} + +// hash_file_256 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_256 :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_blake256_ctx(false, ._32) + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash_256 :: proc { + hash_stream_256, + hash_file_256, + hash_bytes_256, + hash_string_256, +} + +// hash_string_384 will hash the given input and return the +// computed hash +hash_string_384 :: proc(data: string) -> [48]byte { + return hash_bytes_384(transmute([]byte)(data)) +} + +// hash_bytes_384 will hash the given input and return the +// computed hash +hash_bytes_384 :: proc(data: []byte) -> [48]byte { + _create_blake512_ctx(true, ._48) + return _hash_impl->hash_bytes_48(data) +} + +// hash_stream_384 will read the stream in chunks and compute a +// hash from its contents +hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) { + _create_blake512_ctx(true, ._48) + return _hash_impl->hash_stream_48(s) +} + +// hash_file_384 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_384 :: proc(path: string, load_at_once: bool) -> ([48]byte, bool) { + _create_blake512_ctx(true, ._48) + return _hash_impl->hash_file_48(path, load_at_once) +} + +hash_384 :: proc { + hash_stream_384, + hash_file_384, + hash_bytes_384, + hash_string_384, +} + +// hash_string_512 will hash the given input and return the +// computed hash +hash_string_512 :: proc(data: string) -> [64]byte { + return hash_bytes_512(transmute([]byte)(data)) +} + +// hash_bytes_512 will hash the given input and return the +// computed hash +hash_bytes_512 :: proc(data: []byte) -> [64]byte { + _create_blake512_ctx(false, ._64) + return _hash_impl->hash_bytes_64(data) +} + +// hash_stream_512 will read the stream in chunks and compute a +// hash from its contents +hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) { + _create_blake512_ctx(false, ._64) + return _hash_impl->hash_stream_64(s) +} + +// hash_file_512 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_512 :: proc(path: string, load_at_once: bool) -> ([64]byte, bool) { + _create_blake512_ctx(false, ._64) + return _hash_impl->hash_file_64(path, load_at_once) +} + +hash_512 :: proc { + hash_stream_512, + hash_file_512, + hash_bytes_512, + hash_string_512, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte { + hash: [28]byte + if c, ok := ctx.internal_ctx.(Blake256_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) { + hash: [28]byte + if c, ok := ctx.internal_ctx.(Blake256_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([28]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_28(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_28(ctx, buf[:]), read_ok + } + } + } + return [28]byte{}, false +} + +hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Blake256_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Blake256_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_32(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_32(ctx, buf[:]), read_ok + } + } + } + return [32]byte{}, false +} + +hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte { + hash: [48]byte + if c, ok := ctx.internal_ctx.(Blake512_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) { + hash: [48]byte + if c, ok := ctx.internal_ctx.(Blake512_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([48]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_48(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_48(ctx, buf[:]), read_ok + } + } + } + return [48]byte{}, false +} + +hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte { + hash: [64]byte + if c, ok := ctx.internal_ctx.(Blake512_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) { + hash: [64]byte + if c, ok := ctx.internal_ctx.(Blake512_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([64]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_64(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_64(ctx, buf[:]), read_ok + } + } + } + return [64]byte{}, false +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + if ctx.hash_size == ._28 || ctx.hash_size == ._32 { + _create_blake256_ctx(ctx.hash_size == ._28, ctx.hash_size) + if c, ok := ctx.internal_ctx.(Blake256_Context); ok { + init_odin(&c) + } + return + } + if ctx.hash_size == ._48 || ctx.hash_size == ._64 { + _create_blake512_ctx(ctx.hash_size == ._48, ctx.hash_size) + if c, ok := ctx.internal_ctx.(Blake512_Context); ok { + init_odin(&c) + } + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + #partial switch ctx.hash_size { + case ._28, ._32: + if c, ok := ctx.internal_ctx.(Blake256_Context); ok { + update_odin(&c, data) + } + case ._48, ._64: + if c, ok := ctx.internal_ctx.(Blake512_Context); ok { + update_odin(&c, data) + } + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + #partial switch ctx.hash_size { + case ._28, ._32: + if c, ok := ctx.internal_ctx.(Blake256_Context); ok { + final_odin(&c, hash) + } + case ._48, ._64: + if c, ok := ctx.internal_ctx.(Blake512_Context); ok { + final_odin(&c, hash) + } + } +} + +/* + BLAKE implementation +*/ + +SIZE_224 :: 28 +SIZE_256 :: 32 +SIZE_384 :: 48 +SIZE_512 :: 64 +BLOCKSIZE_256 :: 64 +BLOCKSIZE_512 :: 128 + +Blake256_Context :: struct { + h: [8]u32, + s: [4]u32, + t: u64, + x: [64]byte, + nx: int, + is224: bool, + nullt: bool, +} + +Blake512_Context :: struct { + h: [8]u64, + s: [4]u64, + t: u64, + x: [128]byte, + nx: int, + is384: bool, + nullt: bool, +} + +SIGMA := [?]int { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, + 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, + 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, + 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13, + 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9, + 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11, + 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, + 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5, + 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0, +} + +U256 := [16]u32 { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, +} + +U512 := [16]u64 { + 0x243f6a8885a308d3, 0x13198a2e03707344, 0xa4093822299f31d0, 0x082efa98ec4e6c89, + 0x452821e638d01377, 0xbe5466cf34e90c6c, 0xc0ac29b7c97c50dd, 0x3f84d5b5b5470917, + 0x9216d5d98979fb1b, 0xd1310ba698dfb5ac, 0x2ffd72dbd01adfb7, 0xb8e1afed6a267e96, + 0xba7c9045f12c7f99, 0x24a19947b3916cf7, 0x0801f2e2858efc16, 0x636920d871574e69, +} + +G256 :: #force_inline proc "contextless" (a, b, c, d: u32, m: [16]u32, i, j: int) -> (u32, u32, u32, u32) { + a, b, c, d := a, b, c, d + a += m[SIGMA[(i % 10) * 16 + (2 * j)]] ~ U256[SIGMA[(i % 10) * 16 + (2 * j + 1)]] + a += b + d ~= a + d = d << (32 - 16) | d >> 16 + c += d + b ~= c + b = b << (32 - 12) | b >> 12 + a += m[SIGMA[(i % 10) * 16 + (2 * j + 1)]] ~ U256[SIGMA[(i % 10) * 16 + (2 * j)]] + a += b + d ~= a + d = d << (32 - 8) | d >> 8 + c += d + b ~= c + b = b << (32 - 7) | b >> 7 + return a, b, c, d +} + +G512 :: #force_inline proc "contextless" (a, b, c, d: u64, m: [16]u64, i, j: int) -> (u64, u64, u64, u64) { + a, b, c, d := a, b, c, d + a += m[SIGMA[(i % 10) * 16 + (2 * j)]] ~ U512[SIGMA[(i % 10) * 16 + (2 * j + 1)]] + a += b + d ~= a + d = d << (64 - 32) | d >> 32 + c += d + b ~= c + b = b << (64 - 25) | b >> 25 + a += m[SIGMA[(i % 10) * 16 + (2 * j + 1)]] ~ U512[SIGMA[(i % 10) * 16 + (2 * j)]] + a += b + d ~= a + d = d << (64 - 16) | d >> 16 + c += d + b ~= c + b = b << (64 - 11) | b >> 11 + return a, b, c, d +} + +block256 :: proc "contextless" (ctx: ^Blake256_Context, p: []byte) { + i, j: int = ---, --- + v, m: [16]u32 = ---, --- + p := p + for len(p) >= BLOCKSIZE_256 { + v[0] = ctx.h[0] + v[1] = ctx.h[1] + v[2] = ctx.h[2] + v[3] = ctx.h[3] + v[4] = ctx.h[4] + v[5] = ctx.h[5] + v[6] = ctx.h[6] + v[7] = ctx.h[7] + v[8] = ctx.s[0] ~ U256[0] + v[9] = ctx.s[1] ~ U256[1] + v[10] = ctx.s[2] ~ U256[2] + v[11] = ctx.s[3] ~ U256[3] + v[12] = U256[4] + v[13] = U256[5] + v[14] = U256[6] + v[15] = U256[7] + + ctx.t += 512 + if !ctx.nullt { + v[12] ~= u32(ctx.t) + v[13] ~= u32(ctx.t) + v[14] ~= u32(ctx.t >> 32) + v[15] ~= u32(ctx.t >> 32) + } + + for i, j = 0, 0; i < 16; i, j = i+1, j+4 { + m[i] = u32(p[j]) << 24 | u32(p[j + 1]) << 16 | u32(p[j + 2]) << 8 | u32(p[j + 3]) + } + + for i = 0; i < 14; i += 1 { + v[0], v[4], v[8], v[12] = G256(v[0], v[4], v[8], v[12], m, i, 0) + v[1], v[5], v[9], v[13] = G256(v[1], v[5], v[9], v[13], m, i, 1) + v[2], v[6], v[10], v[14] = G256(v[2], v[6], v[10], v[14], m, i, 2) + v[3], v[7], v[11], v[15] = G256(v[3], v[7], v[11], v[15], m, i, 3) + v[0], v[5], v[10], v[15] = G256(v[0], v[5], v[10], v[15], m, i, 4) + v[1], v[6], v[11], v[12] = G256(v[1], v[6], v[11], v[12], m, i, 5) + v[2], v[7], v[8], v[13] = G256(v[2], v[7], v[8], v[13], m, i, 6) + v[3], v[4], v[9], v[14] = G256(v[3], v[4], v[9], v[14], m, i, 7) + } + + for i = 0; i < 8; i += 1 { + ctx.h[i] ~= ctx.s[i % 4] ~ v[i] ~ v[i + 8] + } + p = p[BLOCKSIZE_256:] + } +} + +block512 :: proc "contextless" (ctx: ^Blake512_Context, p: []byte) #no_bounds_check { + i, j: int = ---, --- + v, m: [16]u64 = ---, --- + p := p + for len(p) >= BLOCKSIZE_512 { + v[0] = ctx.h[0] + v[1] = ctx.h[1] + v[2] = ctx.h[2] + v[3] = ctx.h[3] + v[4] = ctx.h[4] + v[5] = ctx.h[5] + v[6] = ctx.h[6] + v[7] = ctx.h[7] + v[8] = ctx.s[0] ~ U512[0] + v[9] = ctx.s[1] ~ U512[1] + v[10] = ctx.s[2] ~ U512[2] + v[11] = ctx.s[3] ~ U512[3] + v[12] = U512[4] + v[13] = U512[5] + v[14] = U512[6] + v[15] = U512[7] + + ctx.t += 1024 + if !ctx.nullt { + v[12] ~= ctx.t + v[13] ~= ctx.t + v[14] ~= 0 + v[15] ~= 0 + } + + for i, j = 0, 0; i < 16; i, j = i + 1, j + 8 { + m[i] = u64(p[j]) << 56 | u64(p[j + 1]) << 48 | u64(p[j + 2]) << 40 | u64(p[j + 3]) << 32 | + u64(p[j + 4]) << 24 | u64(p[j + 5]) << 16 | u64(p[j + 6]) << 8 | u64(p[j + 7]) + } + for i = 0; i < 16; i += 1 { + v[0], v[4], v[8], v[12] = G512(v[0], v[4], v[8], v[12], m, i, 0) + v[1], v[5], v[9], v[13] = G512(v[1], v[5], v[9], v[13], m, i, 1) + v[2], v[6], v[10], v[14] = G512(v[2], v[6], v[10], v[14], m, i, 2) + v[3], v[7], v[11], v[15] = G512(v[3], v[7], v[11], v[15], m, i, 3) + v[0], v[5], v[10], v[15] = G512(v[0], v[5], v[10], v[15], m, i, 4) + v[1], v[6], v[11], v[12] = G512(v[1], v[6], v[11], v[12], m, i, 5) + v[2], v[7], v[8], v[13] = G512(v[2], v[7], v[8], v[13], m, i, 6) + v[3], v[4], v[9], v[14] = G512(v[3], v[4], v[9], v[14], m, i, 7) + } + + for i = 0; i < 8; i += 1 { + ctx.h[i] ~= ctx.s[i % 4] ~ v[i] ~ v[i + 8] + } + p = p[BLOCKSIZE_512:] + } +} + +init_odin :: proc(ctx: ^$T) { + when T == Blake256_Context { + if ctx.is224 { + ctx.h[0] = 0xc1059ed8 + ctx.h[1] = 0x367cd507 + ctx.h[2] = 0x3070dd17 + ctx.h[3] = 0xf70e5939 + ctx.h[4] = 0xffc00b31 + ctx.h[5] = 0x68581511 + ctx.h[6] = 0x64f98fa7 + ctx.h[7] = 0xbefa4fa4 + } else { + ctx.h[0] = 0x6a09e667 + ctx.h[1] = 0xbb67ae85 + ctx.h[2] = 0x3c6ef372 + ctx.h[3] = 0xa54ff53a + ctx.h[4] = 0x510e527f + ctx.h[5] = 0x9b05688c + ctx.h[6] = 0x1f83d9ab + ctx.h[7] = 0x5be0cd19 + } + } else when T == Blake512_Context { + if ctx.is384 { + ctx.h[0] = 0xcbbb9d5dc1059ed8 + ctx.h[1] = 0x629a292a367cd507 + ctx.h[2] = 0x9159015a3070dd17 + ctx.h[3] = 0x152fecd8f70e5939 + ctx.h[4] = 0x67332667ffc00b31 + ctx.h[5] = 0x8eb44a8768581511 + ctx.h[6] = 0xdb0c2e0d64f98fa7 + ctx.h[7] = 0x47b5481dbefa4fa4 + } else { + ctx.h[0] = 0x6a09e667f3bcc908 + ctx.h[1] = 0xbb67ae8584caa73b + ctx.h[2] = 0x3c6ef372fe94f82b + ctx.h[3] = 0xa54ff53a5f1d36f1 + ctx.h[4] = 0x510e527fade682d1 + ctx.h[5] = 0x9b05688c2b3e6c1f + ctx.h[6] = 0x1f83d9abfb41bd6b + ctx.h[7] = 0x5be0cd19137e2179 + } + } +} + +update_odin :: proc(ctx: ^$T, data: []byte) { + data := data + when T == Blake256_Context { + if ctx.nx > 0 { + n := copy(ctx.x[ctx.nx:], data) + ctx.nx += n + if ctx.nx == BLOCKSIZE_256 { + block256(ctx, ctx.x[:]) + ctx.nx = 0 + } + data = data[n:] + } + if len(data) >= BLOCKSIZE_256 { + n := len(data) &~ (BLOCKSIZE_256 - 1) + block256(ctx, data[:n]) + data = data[n:] + } + if len(data) > 0 { + ctx.nx = copy(ctx.x[:], data) + } + } else when T == Blake512_Context { + if ctx.nx > 0 { + n := copy(ctx.x[ctx.nx:], data) + ctx.nx += n + if ctx.nx == BLOCKSIZE_512 { + block512(ctx, ctx.x[:]) + ctx.nx = 0 + } + data = data[n:] + } + if len(data) >= BLOCKSIZE_512 { + n := len(data) &~ (BLOCKSIZE_512 - 1) + block512(ctx, data[:n]) + data = data[n:] + } + if len(data) > 0 { + ctx.nx = copy(ctx.x[:], data) + } + } +} + +final_odin :: proc(ctx: ^$T, hash: []byte) { + when T == Blake256_Context { + tmp: [65]byte + } else when T == Blake512_Context { + tmp: [129]byte + } + nx := u64(ctx.nx) + tmp[0] = 0x80 + length := (ctx.t + nx) << 3 + + when T == Blake256_Context { + if nx == 55 { + if ctx.is224 { + write_additional(ctx, {0x80}) + } else { + write_additional(ctx, {0x81}) + } + } else { + if nx < 55 { + if nx == 0 { + ctx.nullt = true + } + write_additional(ctx, tmp[0 : 55 - nx]) + } else { + write_additional(ctx, tmp[0 : 64 - nx]) + write_additional(ctx, tmp[1:56]) + ctx.nullt = true + } + if ctx.is224 { + write_additional(ctx, {0x00}) + } else { + write_additional(ctx, {0x01}) + } + } + + for i : uint = 0; i < 8; i += 1 { + tmp[i] = byte(length >> (56 - 8 * i)) + } + write_additional(ctx, tmp[0:8]) + + h := ctx.h[:] + if ctx.is224 { + h = h[0:7] + } + for s, i in h { + hash[i * 4] = byte(s >> 24) + hash[i * 4 + 1] = byte(s >> 16) + hash[i * 4 + 2] = byte(s >> 8) + hash[i * 4 + 3] = byte(s) + } + } else when T == Blake512_Context { + if nx == 111 { + if ctx.is384 { + write_additional(ctx, {0x80}) + } else { + write_additional(ctx, {0x81}) + } + } else { + if nx < 111 { + if nx == 0 { + ctx.nullt = true + } + write_additional(ctx, tmp[0 : 111 - nx]) + } else { + write_additional(ctx, tmp[0 : 128 - nx]) + write_additional(ctx, tmp[1:112]) + ctx.nullt = true + } + if ctx.is384 { + write_additional(ctx, {0x00}) + } else { + write_additional(ctx, {0x01}) + } + } + + for i : uint = 0; i < 16; i += 1 { + tmp[i] = byte(length >> (120 - 8 * i)) + } + write_additional(ctx, tmp[0:16]) + + h := ctx.h[:] + if ctx.is384 { + h = h[0:6] + } + for s, i in h { + hash[i * 8] = byte(s >> 56) + hash[i * 8 + 1] = byte(s >> 48) + hash[i * 8 + 2] = byte(s >> 40) + hash[i * 8 + 3] = byte(s >> 32) + hash[i * 8 + 4] = byte(s >> 24) + hash[i * 8 + 5] = byte(s >> 16) + hash[i * 8 + 6] = byte(s >> 8) + hash[i * 8 + 7] = byte(s) + } + } +} + +write_additional :: proc(ctx: ^$T, data: []byte) { + ctx.t -= u64(len(data)) << 3 + update_odin(ctx, data) +} diff --git a/core/crypto/blake2b/blake2b.odin b/core/crypto/blake2b/blake2b.odin new file mode 100644 index 000000000..3db3d0e7b --- /dev/null +++ b/core/crypto/blake2b/blake2b.odin @@ -0,0 +1,189 @@ +package blake2b + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Interface for the BLAKE2B hashing algorithm. + BLAKE2B and BLAKE2B share the implementation in the _blake2 package. +*/ + +import "core:os" +import "core:io" + +import "../botan" +import "../_ctx" +import "../_blake2" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_64 = hash_bytes_odin + ctx.hash_file_64 = hash_file_odin + ctx.hash_stream_64 = hash_stream_odin + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan assigns the internal vtable of the hash context to use the Botan bindings +use_botan :: #force_inline proc() { + botan.assign_hash_vtable(_hash_impl, botan.HASH_BLAKE2B) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string will hash the given input and return the +// computed hash +hash_string :: proc(data: string) -> [64]byte { + return hash_bytes(transmute([]byte)(data)) +} + +// hash_bytes will hash the given input and return the +// computed hash +hash_bytes :: proc(data: []byte) -> [64]byte { + _create_blake2_ctx() + return _hash_impl->hash_bytes_64(data) +} + +// hash_stream will read the stream in chunks and compute a +// hash from its contents +hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) { + _create_blake2_ctx() + return _hash_impl->hash_stream_64(s) +} + +// hash_file will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file :: proc(path: string, load_at_once: bool) -> ([64]byte, bool) { + _create_blake2_ctx() + return _hash_impl->hash_file_64(path, load_at_once) +} + +hash :: proc { + hash_stream, + hash_file, + hash_bytes, + hash_string, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte { + hash: [64]byte + if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok { + _blake2.init_odin(&c) + _blake2.update_odin(&c, data) + _blake2.blake2b_final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) { + hash: [64]byte + if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok { + _blake2.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _blake2.update_odin(&c, buf[:read]) + } + } + _blake2.blake2b_final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([64]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin(ctx, buf[:]), read_ok + } + } + } + return [64]byte{}, false +} + +@(private) +_create_blake2_ctx :: #force_inline proc() { + ctx: _blake2.Blake2b_Context + cfg: _blake2.Blake2_Config + cfg.size = _blake2.BLAKE2B_SIZE + ctx.cfg = cfg + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = ._64 +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + _create_blake2_ctx() + if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok { + _blake2.init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok { + _blake2.update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok { + _blake2.blake2b_final_odin(&c, hash) + } +} diff --git a/core/crypto/blake2s/blake2s.odin b/core/crypto/blake2s/blake2s.odin new file mode 100644 index 000000000..41a964472 --- /dev/null +++ b/core/crypto/blake2s/blake2s.odin @@ -0,0 +1,189 @@ +package blake2s + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Interface for the BLAKE2S hashing algorithm. + BLAKE2B and BLAKE2B share the implementation in the _blake2 package. +*/ + +import "core:os" +import "core:io" + +import "../_ctx" +import "../_blake2" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_32 = hash_bytes_odin + ctx.hash_file_32 = hash_file_odin + ctx.hash_stream_32 = hash_stream_odin + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan does nothing, since Blake2s is not available in Botan +@(warning="Blake2s is not provided by the Botan API. Odin implementation will be used") +use_botan :: #force_inline proc() { + use_odin() +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string will hash the given input and return the +// computed hash +hash_string :: proc(data: string) -> [32]byte { + return hash_bytes(transmute([]byte)(data)) +} + +// hash_bytes will hash the given input and return the +// computed hash +hash_bytes :: proc(data: []byte) -> [32]byte { + _create_blake2_ctx() + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream will read the stream in chunks and compute a +// hash from its contents +hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_blake2_ctx() + return _hash_impl->hash_stream_32(s) +} + +// hash_file will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_blake2_ctx() + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash :: proc { + hash_stream, + hash_file, + hash_bytes, + hash_string, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { + hash: [32]byte + if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok { + _blake2.init_odin(&c) + _blake2.update_odin(&c, data) + _blake2.blake2s_final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { + hash: [32]byte + if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok { + _blake2.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _blake2.update_odin(&c, buf[:read]) + } + } + _blake2.blake2s_final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin(ctx, buf[:]), read_ok + } + } + } + return [32]byte{}, false +} + +@(private) +_create_blake2_ctx :: #force_inline proc() { + ctx: _blake2.Blake2s_Context + cfg: _blake2.Blake2_Config + cfg.size = _blake2.BLAKE2S_SIZE + ctx.cfg = cfg + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = ._32 +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + _create_blake2_ctx() + if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok { + _blake2.init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok { + _blake2.update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok { + _blake2.blake2s_final_odin(&c, hash) + } +} diff --git a/core/crypto/botan/botan.lib b/core/crypto/botan/botan.lib new file mode 100644 index 0000000000000000000000000000000000000000..5731855cb19dff1909cc06aa464f5770cf956253 GIT binary patch literal 3298832 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ڜK{(0&n5_*>M*!pqL!m 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 z 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%0DDi(`VCVOfIJXt5aKkyWV<{hE%c-X_5(gv5X= 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

J*l-PlXqwRPyh9!l(t|w$s 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

6u1bzD0B){IP@!(zj=H4 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 @RU)>3*0KUC5hsMg;Jrz- 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�z{jW!XhI<)4#dYm1Al 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

C@MyVe!n30 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 z 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

w@P^AGzTVT-OG-y)K@?znen;90!h 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 literal 0 HcmV?d00001 diff --git a/core/crypto/botan/botan.odin b/core/crypto/botan/botan.odin new file mode 100644 index 000000000..17420a25f --- /dev/null +++ b/core/crypto/botan/botan.odin @@ -0,0 +1,480 @@ +package botan + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog: Initial creation and testing of the bindings. + + Bindings for the Botan crypto library. + Created for version 2.18.1, using the provided FFI header within Botan. + + The "botan_" prefix has been stripped from the identifiers to remove redundancy, + since the package is already named botan. +*/ + +import "core:c" + +FFI_ERROR :: #type c.int +FFI_SUCCESS :: FFI_ERROR(0) +FFI_INVALID_VERIFIER :: FFI_ERROR(1) +FFI_ERROR_INVALID_INPUT :: FFI_ERROR(-1) +FFI_ERROR_BAD_MAC :: FFI_ERROR(-2) +FFI_ERROR_INSUFFICIENT_BUFFER_SPACE :: FFI_ERROR(-10) +FFI_ERROR_EXCEPTION_THROWN :: FFI_ERROR(-20) +FFI_ERROR_OUT_OF_MEMORY :: FFI_ERROR(-21) +FFI_ERROR_BAD_FLAG :: FFI_ERROR(-30) +FFI_ERROR_NULL_POINTER :: FFI_ERROR(-31) +FFI_ERROR_BAD_PARAMETER :: FFI_ERROR(-32) +FFI_ERROR_KEY_NOT_SET :: FFI_ERROR(-33) +FFI_ERROR_INVALID_KEY_LENGTH :: FFI_ERROR(-34) +FFI_ERROR_NOT_IMPLEMENTED :: FFI_ERROR(-40) +FFI_ERROR_INVALID_OBJECT :: FFI_ERROR(-50) +FFI_ERROR_UNKNOWN_ERROR :: FFI_ERROR(-100) + +FFI_HEX_LOWER_CASE :: 1 + +CIPHER_INIT_FLAG_MASK_DIRECTION :: 1 +CIPHER_INIT_FLAG_ENCRYPT :: 0 +CIPHER_INIT_FLAG_DECRYPT :: 1 + +CIPHER_UPDATE_FLAG_FINAL :: 1 << 0 + +CHECK_KEY_EXPENSIVE_TESTS :: 1 + +PRIVKEY_EXPORT_FLAG_DER :: 0 +PRIVKEY_EXPORT_FLAG_PEM :: 1 + +PUBKEY_DER_FORMAT_SIGNATURE :: 1 + +FPE_FLAG_FE1_COMPAT_MODE :: 1 + +x509_cert_key_constraints :: #type c.int +NO_CONSTRAINTS :: x509_cert_key_constraints(0) +DIGITAL_SIGNATURE :: x509_cert_key_constraints(32768) +NON_REPUDIATION :: x509_cert_key_constraints(16384) +KEY_ENCIPHERMENT :: x509_cert_key_constraints(8192) +DATA_ENCIPHERMENT :: x509_cert_key_constraints(4096) +KEY_AGREEMENT :: x509_cert_key_constraints(2048) +KEY_CERT_SIGN :: x509_cert_key_constraints(1024) +CRL_SIGN :: x509_cert_key_constraints(512) +ENCIPHER_ONLY :: x509_cert_key_constraints(256) +DECIPHER_ONLY :: x509_cert_key_constraints(128) + +HASH_SHA1 :: "SHA1" +HASH_SHA_224 :: "SHA-224" +HASH_SHA_256 :: "SHA-256" +HASH_SHA_384 :: "SHA-384" +HASH_SHA_512 :: "SHA-512" +HASH_SHA3_224 :: "SHA-3(224)" +HASH_SHA3_256 :: "SHA-3(256)" +HASH_SHA3_384 :: "SHA-3(384)" +HASH_SHA3_512 :: "SHA-3(512)" +HASH_SHAKE_128 :: "SHAKE-128" +HASH_SHAKE_256 :: "SHAKE-256" +HASH_KECCAK_224 :: "KECCAK(224)" +HASH_KECCAK_256 :: "KECCAK(256)" +HASH_KECCAK_384 :: "KECCAK(384)" +HASH_KECCAK_512 :: "KECCAK(512)" +HASH_RIPEMD_160 :: "RIPEMD-160" +HASH_WHIRLPOOL :: "Whirlpool" +HASH_BLAKE2B :: "BLAKE2b" +HASH_MD4 :: "MD4" +HASH_MD5 :: "MD5" +HASH_TIGER_128 :: "Tiger(16,3)" +HASH_TIGER_160 :: "Tiger(20,3)" +HASH_TIGER_192 :: "Tiger(24,3)" +HASH_GOST :: "GOST-34.11" +HASH_STREEBOG_256 :: "Streebog-256" +HASH_STREEBOG_512 :: "Streebog-512" +HASH_SM3 :: "SM3" +HASH_SKEIN_512_256 :: "Skein-512(256)" +HASH_SKEIN_512_512 :: "Skein-512(512)" +HASH_SKEIN_512_1024 :: "Skein-512(1024)" + +// Not real values from Botan, only used for context setup within the crypto lib +HASH_SHA2 :: "SHA2" +HASH_SHA3 :: "SHA3" +HASH_SHAKE :: "SHAKE" +HASH_KECCAK :: "KECCAK" +HASH_STREEBOG :: "STREEBOG" +HASH_TIGER :: "TIGER" +HASH_SKEIN_512 :: "SKEIN_512" + +MAC_HMAC_SHA1 :: "HMAC(SHA1)" +MAC_HMAC_SHA_224 :: "HMAC(SHA-224)" +MAC_HMAC_SHA_256 :: "HMAC(SHA-256)" +MAC_HMAC_SHA_384 :: "HMAC(SHA-384)" +MAC_HMAC_SHA_512 :: "HMAC(SHA-512)" +MAC_HMAC_MD5 :: "HMAC(MD5)" + +hash_struct :: struct{} +hash_t :: ^hash_struct +rng_struct :: struct{} +rng_t :: ^rng_struct +mac_struct :: struct{} +mac_t :: ^mac_struct +cipher_struct :: struct{} +cipher_t :: ^cipher_struct +block_cipher_struct :: struct{} +block_cipher_t :: ^block_cipher_struct +mp_struct :: struct{} +mp_t :: ^mp_struct +privkey_struct :: struct{} +privkey_t :: ^privkey_struct +pubkey_struct :: struct{} +pubkey_t :: ^pubkey_struct +pk_op_encrypt_struct :: struct{} +pk_op_encrypt_t :: ^pk_op_encrypt_struct +pk_op_decrypt_struct :: struct{} +pk_op_decrypt_t :: ^pk_op_decrypt_struct +pk_op_sign_struct :: struct{} +pk_op_sign_t :: ^pk_op_sign_struct +pk_op_verify_struct :: struct{} +pk_op_verify_t :: ^pk_op_verify_struct +pk_op_ka_struct :: struct{} +pk_op_ka_t :: ^pk_op_ka_struct +x509_cert_struct :: struct{} +x509_cert_t :: ^x509_cert_struct +x509_crl_struct :: struct{} +x509_crl_t :: ^x509_crl_struct +hotp_struct :: struct{} +hotp_t :: ^hotp_struct +totp_struct :: struct{} +totp_t :: ^totp_struct +fpe_struct :: struct{} +fpe_t :: ^fpe_struct + +when ODIN_OS == "windows" { + foreign import botan_lib "botan.lib" +} else when ODIN_OS == "linux" { + foreign import botan_lib "system:botan-2" +} else when ODIN_OS == "darwin" { + foreign import botan_lib "system:botan-2" +} + +@(default_calling_convention="c") +@(link_prefix="botan_") +foreign botan_lib { + error_description :: proc(err: c.int) -> cstring --- + ffi_api_version :: proc() -> c.int --- + ffi_supports_api :: proc(api_version: c.int) -> c.int --- + version_string :: proc() -> cstring --- + version_major :: proc() -> c.int --- + version_minor :: proc() -> c.int --- + version_patch :: proc() -> c.int --- + version_datestamp :: proc() -> c.int --- + + constant_time_compare :: proc(x, y: ^c.char, length: c.size_t) -> c.int --- + same_mem :: proc(x, y: ^c.char, length: c.size_t) -> c.int --- + scrub_mem :: proc(mem: rawptr, bytes: c.size_t) -> c.int --- + + hex_encode :: proc(x: ^c.char, length: c.size_t, out: ^c.char, flags: c.uint) -> c.int --- + hex_decode :: proc(hex_str: cstring, in_len: c.size_t, out: ^c.char, out_len: c.size_t) -> c.int --- + + base64_encode :: proc(x: ^c.char, length: c.size_t, out: ^c.char, out_len: c.size_t) -> c.int --- + base64_decode :: proc(base64_str: cstring, in_len: c.size_t, out: ^c.char, out_len: c.size_t) -> c.int --- + + rng_init :: proc(rng: ^rng_t, rng_type: cstring) -> c.int --- + rng_init_custom :: proc(rng_out: ^rng_t, rng_name: cstring, ctx: rawptr, + get_cb: proc(ctx: rawptr, out: ^c.char, out_len: c.size_t) -> ^c.int, + add_entropy_cb: proc(ctx: rawptr, input: ^c.char, length: c.size_t) -> ^c.int, + destroy_cb: proc(ctx: rawptr) -> rawptr) -> c.int --- + rng_get :: proc(rng: rng_t, out: ^c.char, out_len: c.size_t) -> c.int --- + rng_reseed :: proc(rng: rng_t, bits: c.size_t) -> c.int --- + rng_reseed_from_rng :: proc(rng, source_rng: rng_t, bits: c.size_t) -> c.int --- + rng_add_entropy :: proc(rng: rng_t, entropy: ^c.char, entropy_len: c.size_t) -> c.int --- + rng_destroy :: proc(rng: rng_t) -> c.int --- + + hash_init :: proc(hash: ^hash_t, hash_name: cstring, flags: c.uint) -> c.int --- + hash_copy_state :: proc(dest: ^hash_t, source: hash_t) -> c.int --- + hash_output_length :: proc(hash: hash_t, output_length: ^c.size_t) -> c.int --- + hash_block_size :: proc(hash: hash_t, block_size: ^c.size_t) -> c.int --- + hash_update :: proc(hash: hash_t, input: ^c.char, input_len: c.size_t) -> c.int --- + hash_final :: proc(hash: hash_t, out: ^c.char) -> c.int --- + hash_clear :: proc(hash: hash_t) -> c.int --- + hash_destroy :: proc(hash: hash_t) -> c.int --- + hash_name :: proc(hash: hash_t, name: ^c.char, name_len: ^c.size_t) -> c.int --- + + mac_init :: proc(mac: ^mac_t, hash_name: cstring, flags: c.uint) -> c.int --- + mac_output_length :: proc(mac: mac_t, output_length: ^c.size_t) -> c.int --- + mac_set_key :: proc(mac: mac_t, key: ^c.char, key_len: c.size_t) -> c.int --- + mac_update :: proc(mac: mac_t, buf: ^c.char, length: c.size_t) -> c.int --- + mac_final :: proc(mac: mac_t, out: ^c.char) -> c.int --- + mac_clear :: proc(mac: mac_t) -> c.int --- + mac_name :: proc(mac: mac_t, name: ^c.char, name_len: ^c.size_t) -> c.int --- + mac_get_keyspec :: proc(mac: mac_t, out_minimum_keylength, out_maximum_keylength, out_keylength_modulo: ^c.size_t) -> c.int --- + mac_destroy :: proc(mac: mac_t) -> c.int --- + + cipher_init :: proc(cipher: ^cipher_t, name: cstring, flags: c.uint) -> c.int --- + cipher_name :: proc(cipher: cipher_t, name: ^c.char, name_len: ^c.size_t) -> c.int --- + cipher_output_length :: proc(cipher: cipher_t, output_length: ^c.size_t) -> c.int --- + cipher_valid_nonce_length :: proc(cipher: cipher_t, nl: c.size_t) -> c.int --- + cipher_get_tag_length :: proc(cipher: cipher_t, tag_size: ^c.size_t) -> c.int --- + cipher_get_default_nonce_length :: proc(cipher: cipher_t, nl: ^c.size_t) -> c.int --- + cipher_get_update_granularity :: proc(cipher: cipher_t, ug: ^c.size_t) -> c.int --- + cipher_query_keylen :: proc(cipher: cipher_t, out_minimum_keylength, out_maximum_keylength: ^c.size_t) -> c.int --- + cipher_get_keyspec :: proc(cipher: cipher_t, min_keylen, max_keylen, mod_keylen: ^c.size_t) -> c.int --- + cipher_set_key :: proc(cipher: cipher_t, key: ^c.char, key_len: c.size_t) -> c.int --- + cipher_reset :: proc(cipher: cipher_t) -> c.int --- + cipher_set_associated_data :: proc(cipher: cipher_t, ad: ^c.char, ad_len: c.size_t) -> c.int --- + cipher_start :: proc(cipher: cipher_t, nonce: ^c.char, nonce_len: c.size_t) -> c.int --- + cipher_update :: proc(cipher: cipher_t, flags: c.uint, output: ^c.char, output_size: c.size_t, output_written: ^c.size_t, + input_bytes: ^c.char, input_size: c.size_t, input_consumed: ^c.size_t) -> c.int --- + cipher_clear :: proc(hash: cipher_t) -> c.int --- + cipher_destroy :: proc(cipher: cipher_t) -> c.int --- + + @(deprecated="Use botan.pwdhash") + pbkdf :: proc(pbkdf_algo: cstring, out: ^c.char, out_len: c.size_t, passphrase: cstring, salt: ^c.char, + salt_len, iterations: c.size_t) -> c.int --- + @(deprecated="Use botan.pwdhash_timed") + pbkdf_timed :: proc(pbkdf_algo: cstring, out: ^c.char, out_len: c.size_t, passphrase: cstring, salt: ^c.char, + salt_len, milliseconds_to_run: c.size_t, out_iterations_used: ^c.size_t) -> c.int --- + pwdhash :: proc(algo: cstring, param1, param2, param3: c.size_t, out: ^c.char, out_len: c.size_t, passphrase: cstring, + passphrase_len: c.size_t, salt: ^c.char, salt_len: c.size_t) -> c.int --- + pwdhash_timed :: proc(algo: cstring, msec: c.uint, param1, param2, param3: c.size_t, out: ^c.char, out_len: c.size_t, + passphrase: cstring, passphrase_len: c.size_t, salt: ^c.char, salt_len: c.size_t) -> c.int --- + @(deprecated="Use botan.pwdhash") + scrypt :: proc(out: ^c.char, out_len: c.size_t, passphrase: cstring, salt: ^c.char, salt_len, N, r, p: c.size_t) -> c.int --- + kdf :: proc(kdf_algo: cstring, out: ^c.char, out_len: c.size_t, secret: ^c.char, secret_lent: c.size_t, salt: ^c.char, + salt_len: c.size_t, label: ^c.char, label_len: c.size_t) -> c.int --- + + block_cipher_init :: proc(bc: ^block_cipher_t, name: cstring) -> c.int --- + block_cipher_destroy :: proc(bc: block_cipher_t) -> c.int --- + block_cipher_clear :: proc(bc: block_cipher_t) -> c.int --- + block_cipher_set_key :: proc(bc: block_cipher_t, key: ^c.char, key_len: c.size_t) -> c.int --- + block_cipher_block_size :: proc(bc: block_cipher_t) -> c.int --- + block_cipher_encrypt_blocks :: proc(bc: block_cipher_t, input, out: ^c.char, blocks: c.size_t) -> c.int --- + block_cipher_decrypt_blocks :: proc(bc: block_cipher_t, input, out: ^c.char, blocks: c.size_t) -> c.int --- + block_cipher_name :: proc(bc: block_cipher_t, name: ^c.char, name_len: ^c.size_t) -> c.int --- + block_cipher_get_keyspec :: proc(bc: block_cipher_t, out_minimum_keylength, out_maximum_keylength, out_keylength_modulo: ^c.size_t) -> c.int --- + + mp_init :: proc(mp: ^mp_t) -> c.int --- + mp_destroy :: proc(mp: mp_t) -> c.int --- + mp_to_hex :: proc(mp: mp_t, out: ^c.char) -> c.int --- + mp_to_str :: proc(mp: mp_t, base: c.char, out: ^c.char, out_len: ^c.size_t) -> c.int --- + mp_clear :: proc(mp: mp_t) -> c.int --- + mp_set_from_int :: proc(mp: mp_t, initial_value: c.int) -> c.int --- + mp_set_from_mp :: proc(dest, source: mp_t) -> c.int --- + mp_set_from_str :: proc(dest: mp_t, str: cstring) -> c.int --- + mp_set_from_radix_str :: proc(mp: mp_t, str: cstring, radix: c.size_t) -> c.int --- + mp_num_bits :: proc(n: mp_t, bits: ^c.size_t) -> c.int --- + mp_num_bytes :: proc(n: mp_t, bytes: ^c.size_t) -> c.int --- + mp_to_bin :: proc(mp: mp_t, vec: ^c.char) -> c.int --- + mp_from_bin :: proc(mp: mp_t, vec: ^c.char, vec_len: c.size_t) -> c.int --- + mp_to_uint32 :: proc(mp: mp_t, val: ^c.uint) -> c.int --- + mp_is_positive :: proc(mp: mp_t) -> c.int --- + mp_is_negative :: proc(mp: mp_t) -> c.int --- + mp_flip_sign :: proc(mp: mp_t) -> c.int --- + mp_is_zero :: proc(mp: mp_t) -> c.int --- + @(deprecated="Use botan.mp_get_bit(0)") + mp_is_odd :: proc(mp: mp_t) -> c.int --- + @(deprecated="Use botan.mp_get_bit(0)") + mp_is_even :: proc(mp: mp_t) -> c.int --- + mp_add_u32 :: proc(result, x: mp_t, y: c.uint) -> c.int --- + mp_sub_u32 :: proc(result, x: mp_t, y: c.uint) -> c.int --- + mp_add :: proc(result, x, y: mp_t) -> c.int --- + mp_sub :: proc(result, x, y: mp_t) -> c.int --- + mp_mul :: proc(result, x, y: mp_t) -> c.int --- + mp_div :: proc(quotient, remainder, x, y: mp_t) -> c.int --- + mp_mod_mul :: proc(result, x, y, mod: mp_t) -> c.int --- + mp_equal :: proc(x, y: mp_t) -> c.int --- + mp_cmp :: proc(result: ^c.int, x, y: mp_t) -> c.int --- + mp_swap :: proc(x, y: mp_t) -> c.int --- + mp_powmod :: proc(out, base, exponent, modulus: mp_t) -> c.int --- + mp_lshift :: proc(out, input: mp_t, shift: c.size_t) -> c.int --- + mp_rshift :: proc(out, input: mp_t, shift: c.size_t) -> c.int --- + mp_mod_inverse :: proc(out, input, modulus: mp_t) -> c.int --- + mp_rand_bits :: proc(rand_out: mp_t, rng: rng_t, bits: c.size_t) -> c.int --- + mp_rand_range :: proc(rand_out: mp_t, rng: rng_t, lower_bound, upper_bound: mp_t) -> c.int --- + mp_gcd :: proc(out, x, y: mp_t) -> c.int --- + mp_is_prime :: proc(n: mp_t, rng: rng_t, test_prob: c.size_t) -> c.int --- + mp_get_bit :: proc(n: mp_t, bit: c.size_t) -> c.int --- + mp_set_bit :: proc(n: mp_t, bit: c.size_t) -> c.int --- + mp_clear_bit :: proc(n: mp_t, bit: c.size_t) -> c.int --- + + bcrypt_generate :: proc(out: ^c.char, out_len: ^c.size_t, password: cstring, rng: rng_t, work_factor: c.size_t, flags: c.uint) -> c.int --- + bcrypt_is_valid :: proc(pass, hash: cstring) -> c.int --- + + privkey_create :: proc(key: ^privkey_t, algo_name, algo_params: cstring, rng: rng_t) -> c.int --- + @(deprecated="Use botan.privkey_create") + privkey_check_key :: proc(key: privkey_t, rng: rng_t, flags: c.uint) -> c.int --- + @(deprecated="Use botan.privkey_create") + privkey_create_rsa :: proc(key: ^privkey_t, rng: rng_t, bits: c.size_t) -> c.int --- + @(deprecated="Use botan.privkey_create") + privkey_create_ecdsa :: proc(key: ^privkey_t, rng: rng_t, params: cstring) -> c.int --- + @(deprecated="Use botan.privkey_create") + privkey_create_ecdh :: proc(key: ^privkey_t, rng: rng_t, params: cstring) -> c.int --- + @(deprecated="Use botan.privkey_create") + privkey_create_mceliece :: proc(key: ^privkey_t, rng: rng_t, n, t: c.size_t) -> c.int --- + @(deprecated="Use botan.privkey_create") + privkey_create_dh :: proc(key: ^privkey_t, rng: rng_t, param: cstring) -> c.int --- + privkey_create_dsa :: proc(key: ^privkey_t, rng: rng_t, pbits, qbits: c.size_t) -> c.int --- + privkey_create_elgamal :: proc(key: ^privkey_t, rng: rng_t, pbits, qbits: c.size_t) -> c.int --- + privkey_load :: proc(key: ^privkey_t, rng: rng_t, bits: ^c.char, length: c.size_t, password: cstring) -> c.int --- + privkey_destroy :: proc(key: privkey_t) -> c.int --- + privkey_export :: proc(key: privkey_t, out: ^c.char, out_len: ^c.size_t, flags: c.uint) -> c.int --- + privkey_algo_name :: proc(key: privkey_t, out: ^c.char, out_len: ^c.size_t) -> c.int --- + @(deprecated="Use botan.privkey_export_encrypted_pbkdf_{msec,iter}") + privkey_export_encrypted :: proc(key: privkey_t, out: ^c.char, out_len: ^c.size_t, rng: rng_t, passphrase, encryption_algo: cstring, flags: c.uint) -> c.int --- + privkey_export_encrypted_pbkdf_msec :: proc(key: privkey_t, out: ^c.char, out_len: ^c.size_t, rng: rng_t, passphrase: cstring, pbkdf_msec_runtime: c.uint, + pbkdf_iterations_out: ^c.size_t, cipher_algo, pbkdf_algo: cstring, flags: c.uint) -> c.int --- + privkey_export_encrypted_pbkdf_iter :: proc(key: privkey_t, out: ^c.char, out_len: ^c.size_t, rng: rng_t, passphrase: cstring, pbkdf_iterations: c.size_t, + cipher_algo, pbkdf_algo: cstring, flags: c.uint) -> c.int --- + pubkey_load :: proc(key: ^pubkey_t, bits: ^c.char, length: c.size_t) -> c.int --- + privkey_export_pubkey :: proc(out: ^pubkey_t, input: privkey_t) -> c.int --- + pubkey_export :: proc(key: pubkey_t, out: ^c.char, out_len: ^c.size_t, flags: c.uint) -> c.int --- + pubkey_algo_name :: proc(key: pubkey_t, out: ^c.char, out_len: ^c.size_t) -> c.int --- + pubkey_check_key :: proc(key: pubkey_t, rng: rng_t, flags: c.uint) -> c.int --- + pubkey_estimated_strength :: proc(key: pubkey_t, estimate: ^c.size_t) -> c.int --- + pubkey_fingerprint :: proc(key: pubkey_t, hash: cstring, out: ^c.char, out_len: ^c.size_t) -> c.int --- + pubkey_destroy :: proc(key: pubkey_t) -> c.int --- + pubkey_get_field :: proc(output: mp_t, key: pubkey_t, field_name: cstring) -> c.int --- + privkey_get_field :: proc(output: mp_t, key: privkey_t, field_name: cstring) -> c.int --- + + privkey_load_rsa :: proc(key: ^privkey_t, p, q, e: mp_t) -> c.int --- + privkey_load_rsa_pkcs1 :: proc(key: ^privkey_t, bits: ^c.char, length: c.size_t) -> c.int --- + @(deprecated="Use botan.privkey_get_field") + privkey_rsa_get_p :: proc(p: mp_t, rsa_key: privkey_t) -> c.int --- + @(deprecated="Use botan.privkey_get_field") + privkey_rsa_get_q :: proc(q: mp_t, rsa_key: privkey_t) -> c.int --- + @(deprecated="Use botan.privkey_get_field") + privkey_rsa_get_d :: proc(d: mp_t, rsa_key: privkey_t) -> c.int --- + @(deprecated="Use botan.privkey_get_field") + privkey_rsa_get_n :: proc(n: mp_t, rsa_key: privkey_t) -> c.int --- + @(deprecated="Use botan.privkey_get_field") + privkey_rsa_get_e :: proc(e: mp_t, rsa_key: privkey_t) -> c.int --- + privkey_rsa_get_privkey :: proc(rsa_key: privkey_t, out: ^c.char, out_len: ^c.size_t, flags: c.uint) -> c.int --- + pubkey_load_rsa :: proc(key: ^pubkey_t, n, e: mp_t) -> c.int --- + @(deprecated="Use botan.pubkey_get_field") + pubkey_rsa_get_e :: proc(e: mp_t, rsa_key: pubkey_t) -> c.int --- + @(deprecated="Use botan.pubkey_get_field") + pubkey_rsa_get_n :: proc(n: mp_t, rsa_key: pubkey_t) -> c.int --- + + privkey_load_dsa :: proc(key: ^privkey_t, p, q, g, x: mp_t) -> c.int --- + pubkey_load_dsa :: proc(key: ^pubkey_t, p, q, g, y: mp_t) -> c.int --- + @(deprecated="Use botan.pubkey_get_field") + privkey_dsa_get_x :: proc(n: mp_t, key: privkey_t) -> c.int --- + @(deprecated="Use botan.pubkey_get_field") + pubkey_dsa_get_p :: proc(p: mp_t, key: pubkey_t) -> c.int --- + @(deprecated="Use botan.pubkey_get_field") + pubkey_dsa_get_q :: proc(q: mp_t, key: pubkey_t) -> c.int --- + @(deprecated="Use botan.pubkey_get_field") + pubkey_dsa_get_g :: proc(d: mp_t, key: pubkey_t) -> c.int --- + @(deprecated="Use botan.pubkey_get_field") + pubkey_dsa_get_y :: proc(y: mp_t, key: pubkey_t) -> c.int --- + + privkey_load_dh :: proc(key: ^privkey_t, p, g, y: mp_t) -> c.int --- + pubkey_load_dh :: proc(key: ^pubkey_t, p, g, x: mp_t) -> c.int --- + + privkey_load_elgamal :: proc(key: ^privkey_t, p, g, y: mp_t) -> c.int --- + pubkey_load_elgamal :: proc(key: ^pubkey_t, p, g, x: mp_t) -> c.int --- + + privkey_load_ed25519 :: proc(key: ^privkey_t, privkey: [32]c.char) -> c.int --- + pubkey_load_ed25519 :: proc(key: ^pubkey_t, pubkey: [32]c.char) -> c.int --- + privkey_ed25519_get_privkey :: proc(key: ^privkey_t, output: [64]c.char) -> c.int --- + pubkey_ed25519_get_pubkey :: proc(key: ^pubkey_t, pubkey: [32]c.char) -> c.int --- + + privkey_load_x25519 :: proc(key: ^privkey_t, privkey: [32]c.char) -> c.int --- + pubkey_load_x25519 :: proc(key: ^pubkey_t, pubkey: [32]c.char) -> c.int --- + privkey_x25519_get_privkey :: proc(key: ^privkey_t, output: [32]c.char) -> c.int --- + pubkey_x25519_get_pubkey :: proc(key: ^pubkey_t, pubkey: [32]c.char) -> c.int --- + + privkey_load_ecdsa :: proc(key: ^privkey_t, scalar: mp_t, curve_name: cstring) -> c.int --- + pubkey_load_ecdsa :: proc(key: ^pubkey_t, public_x, public_y: mp_t, curve_name: cstring) -> c.int --- + pubkey_load_ecdh :: proc(key: ^pubkey_t, public_x, public_y: mp_t, curve_name: cstring) -> c.int --- + privkey_load_ecdh :: proc(key: ^privkey_t, scalar: mp_t, curve_name: cstring) -> c.int --- + pubkey_load_sm2 :: proc(key: ^pubkey_t, public_x, public_y: mp_t, curve_name: cstring) -> c.int --- + privkey_load_sm2 :: proc(key: ^privkey_t, scalar: mp_t, curve_name: cstring) -> c.int --- + @(deprecated="Use botan.pubkey_load_sm2") + pubkey_load_sm2_enc :: proc(key: ^pubkey_t, public_x, public_y: mp_t, curve_name: cstring) -> c.int --- + @(deprecated="Use botan.privkey_load_sm2") + privkey_load_sm2_enc :: proc(key: ^privkey_t, scalar: mp_t, curve_name: cstring) -> c.int --- + pubkey_sm2_compute_za :: proc(out: ^c.char, out_len: ^c.size_t, ident, hash_algo: cstring, key: pubkey_t) -> c.int --- + + pk_op_encrypt_create :: proc(op: ^pk_op_encrypt_t, key: pubkey_t, padding: cstring, flags: c.uint) -> c.int --- + pk_op_encrypt_destroy :: proc(op: pk_op_encrypt_t) -> c.int --- + pk_op_encrypt_output_length :: proc(op: pk_op_encrypt_t, ptext_len: c.size_t, ctext_len: ^c.size_t) -> c.int --- + pk_op_encrypt :: proc(op: pk_op_encrypt_t, rng: rng_t, out: ^c.char, out_len: ^c.size_t, plaintext: cstring, plaintext_len: c.size_t) -> c.int --- + + pk_op_decrypt_create :: proc(op: ^pk_op_decrypt_t, key: privkey_t, padding: cstring, flags: c.uint) -> c.int --- + pk_op_decrypt_destroy :: proc(op: pk_op_decrypt_t) -> c.int --- + pk_op_decrypt_output_length :: proc(op: pk_op_decrypt_t, ptext_len: c.size_t, ctext_len: ^c.size_t) -> c.int --- + pk_op_decrypt :: proc(op: pk_op_decrypt_t, rng: rng_t, out: ^c.char, out_len: ^c.size_t, ciphertext: cstring, ciphertext_len: c.size_t) -> c.int --- + + pk_op_sign_create :: proc(op: ^pk_op_sign_t, key: privkey_t, hash_and_padding: cstring, flags: c.uint) -> c.int --- + pk_op_sign_destroy :: proc(op: pk_op_sign_t) -> c.int --- + pk_op_sign_output_length :: proc(op: pk_op_sign_t, olen: ^c.size_t) -> c.int --- + pk_op_sign_update :: proc(op: pk_op_sign_t, input: ^c.char, input_len: c.size_t) -> c.int --- + pk_op_sign_finish :: proc(op: pk_op_sign_t, rng: rng_t, sig: ^c.char, sig_len: ^c.size_t) -> c.int --- + + pk_op_verify_create :: proc(op: ^pk_op_verify_t, hash_and_padding: cstring, flags: c.uint) -> c.int --- + pk_op_verify_destroy :: proc(op: pk_op_verify_t) -> c.int --- + pk_op_verify_update :: proc(op: pk_op_verify_t, input: ^c.char, input_len: c.size_t) -> c.int --- + pk_op_verify_finish :: proc(op: pk_op_verify_t, sig: ^c.char, sig_len: c.size_t) -> c.int --- + + pk_op_key_agreement_create :: proc(op: ^pk_op_ka_t, kdf: cstring, flags: c.uint) -> c.int --- + pk_op_key_agreement_destroy :: proc(op: pk_op_ka_t) -> c.int --- + pk_op_key_agreement_export_public :: proc(key: privkey_t, out: ^c.char, out_len: ^c.size_t) -> c.int --- + pk_op_key_agreement_size :: proc(op: pk_op_ka_t, out_len: ^c.size_t) -> c.int --- + pk_op_key_agreement :: proc(op: pk_op_ka_t, out: ^c.char, out_len: ^c.size_t, other_key: ^c.char, other_key_len: c.size_t, salt: ^c.char, + salt_len: c.size_t) -> c.int --- + + pkcs_hash_id :: proc(hash_name: cstring, pkcs_id: ^c.char, pkcs_id_len: ^c.size_t) -> c.int --- + + @(deprecated="Poorly specified, avoid in new code") + mceies_encrypt :: proc(mce_key: pubkey_t, rng: rng_t, aead: cstring, pt: ^c.char, pt_len: c.size_t, ad: ^c.char, ad_len: c.size_t, + ct: ^c.char, ct_len: ^c.size_t) -> c.int --- + @(deprecated="Poorly specified, avoid in new code") + mceies_decrypt :: proc(mce_key: privkey_t, aead: cstring, ct: ^c.char, ct_len: c.size_t, ad: ^c.char, ad_len: c.size_t, pt: ^c.char, + pt_len: ^c.size_t) -> c.int --- + + x509_cert_load :: proc(cert_obj: ^x509_cert_t, cert: ^c.char, cert_len: c.size_t) -> c.int --- + x509_cert_load_file :: proc(cert_obj: ^x509_cert_t, filename: cstring) -> c.int --- + x509_cert_destroy :: proc(cert: x509_cert_t) -> c.int --- + x509_cert_dup :: proc(new_cert: ^x509_cert_t, cert: x509_cert_t) -> c.int --- + x509_cert_get_time_starts :: proc(cert: x509_cert_t, out: ^c.char, out_len: ^c.size_t) -> c.int --- + x509_cert_get_time_expires :: proc(cert: x509_cert_t, out: ^c.char, out_len: ^c.size_t) -> c.int --- + x509_cert_not_before :: proc(cert: x509_cert_t, time_since_epoch: ^c.ulonglong) -> c.int --- + x509_cert_not_after :: proc(cert: x509_cert_t, time_since_epoch: ^c.ulonglong) -> c.int --- + x509_cert_get_fingerprint :: proc(cert: x509_cert_t, hash: cstring, out: ^c.char, out_len: ^c.size_t) -> c.int --- + x509_cert_get_serial_number :: proc(cert: x509_cert_t, out: ^c.char, out_len: ^c.size_t) -> c.int --- + x509_cert_get_authority_key_id :: proc(cert: x509_cert_t, out: ^c.char, out_len: ^c.size_t) -> c.int --- + x509_cert_get_subject_key_id :: proc(cert: x509_cert_t, out: ^c.char, out_len: ^c.size_t) -> c.int --- + x509_cert_get_public_key_bits :: proc(cert: x509_cert_t, out: ^c.char, out_len: ^c.size_t) -> c.int --- + x509_cert_get_public_key :: proc(cert: x509_cert_t, key: ^pubkey_t) -> c.int --- + x509_cert_get_issuer_dn :: proc(cert: x509_cert_t, key: ^c.char, index: c.size_t, out: ^c.char, out_len: ^c.size_t) -> c.int --- + x509_cert_get_subject_dn :: proc(cert: x509_cert_t, key: ^c.char, index: c.size_t, out: ^c.char, out_len: ^c.size_t) -> c.int --- + x509_cert_to_string :: proc(cert: x509_cert_t, out: ^c.char, out_len: ^c.size_t) -> c.int --- + x509_cert_allowed_usage :: proc(cert: x509_cert_t, key_usage: c.uint) -> c.int --- + x509_cert_hostname_match :: proc(cert: x509_cert_t, hostname: cstring) -> c.int --- + x509_cert_verify :: proc(validation_result: ^c.int, cert: x509_cert_t, intermediates: ^x509_cert_t, intermediates_len: c.size_t, trusted: ^x509_cert_t, + trusted_len: c.size_t, trusted_path: cstring, required_strength: c.size_t, hostname: cstring, reference_time: c.ulonglong) -> c.int --- + x509_cert_validation_status :: proc(code: c.int) -> cstring --- + x509_crl_load_file :: proc(crl_obj: ^x509_crl_t, crl_path: cstring) -> c.int --- + x509_crl_load :: proc(crl_obj: ^x509_crl_t, crl_bits: ^c.char, crl_bits_len: c.size_t) -> c.int --- + x509_crl_destroy :: proc(crl: x509_crl_t) -> c.int --- + x509_is_revoked :: proc(crl: x509_crl_t, cert: x509_cert_t) -> c.int --- + x509_cert_verify_with_crl :: proc(validation_result: ^c.int, cert: x509_cert_t, intermediates: ^x509_cert_t, intermediates_len: c.size_t, trusted: ^x509_cert_t, + trusted_len: c.size_t, crls: ^x509_crl_t, crls_len: c.size_t, trusted_path: cstring, required_strength: c.size_t, + hostname: cstring, reference_time: c.ulonglong) -> c.int --- + + key_wrap3394 :: proc(key: ^c.char, key_len: c.size_t, kek: ^c.char, kek_len: c.size_t, wrapped_key: ^c.char, wrapped_key_len: ^c.size_t) -> c.int --- + key_unwrap3394 :: proc(wrapped_key: ^c.char, wrapped_key_len: c.size_t, kek: ^c.char, kek_len: c.size_t, key: ^c.char, key_len: ^c.size_t) -> c.int --- + + hotp_init :: proc(hotp: ^hotp_t, key: ^c.char, key_len: c.size_t, hash_algo: cstring, digits: c.size_t) -> c.int --- + hotp_destroy :: proc(hotp: hotp_t) -> c.int --- + hotp_generate :: proc(hotp: hotp_t, hotp_code: ^c.uint, hotp_counter: c.ulonglong) -> c.int --- + hotp_check :: proc(hotp: hotp_t, next_hotp_counter: ^c.ulonglong, hotp_code: c.uint, hotp_counter: c.ulonglong, resync_range: c.size_t) -> c.int --- + + totp_init :: proc(totp: ^totp_t, key: ^c.char, key_len: c.size_t, hash_algo: cstring, digits, time_step: c.size_t) -> c.int --- + totp_destroy :: proc(totp: totp_t) -> c.int --- + totp_generate :: proc(totp: totp_t, totp_code: ^c.uint, timestamp: c.ulonglong) -> c.int --- + totp_check :: proc(totp: totp_t, totp_code: ^c.uint, timestamp: c.ulonglong, acceptable_clock_drift: c.size_t) -> c.int --- + + fpe_fe1_init :: proc(fpe: ^fpe_t, n: mp_t, key: ^c.char, key_len, rounds: c.size_t, flags: c.uint) -> c.int --- + fpe_destroy :: proc(fpe: fpe_t) -> c.int --- + fpe_encrypt :: proc(fpe: fpe_t, x: mp_t, tweak: ^c.char, tweak_len: c.size_t) -> c.int --- + fpe_decrypt :: proc(fpe: fpe_t, x: mp_t, tweak: ^c.char, tweak_len: c.size_t) -> c.int --- +} \ No newline at end of file diff --git a/core/crypto/botan/hash.odin b/core/crypto/botan/hash.odin new file mode 100644 index 000000000..8f12f871c --- /dev/null +++ b/core/crypto/botan/hash.odin @@ -0,0 +1,471 @@ +package botan + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog: Initial creation and testing of the bindings. + + Implementation of the context for the Botan side. +*/ + +import "core:os" +import "core:io" +import "core:fmt" +import "core:strings" + +import "../_ctx" + +hash_bytes_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte { + hash: [16]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._16, 16), 0) + hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data))) + hash_final(c, &hash[0]) + hash_destroy(c) + return hash +} + +hash_bytes_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte { + hash: [20]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._20, 20), 0) + hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data))) + hash_final(c, &hash[0]) + hash_destroy(c) + return hash +} + +hash_bytes_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [24]byte { + hash: [24]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._24, 24), 0) + hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data))) + hash_final(c, &hash[0]) + hash_destroy(c) + return hash +} + +hash_bytes_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte { + hash: [28]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._28, 28), 0) + hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data))) + hash_final(c, &hash[0]) + hash_destroy(c) + return hash +} + +hash_bytes_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { + hash: [32]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._32, 32), 0) + hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data))) + hash_final(c, &hash[0]) + hash_destroy(c) + return hash +} + +hash_bytes_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte { + hash: [48]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._48, 48), 0) + hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data))) + hash_final(c, &hash[0]) + hash_destroy(c) + return hash +} + +hash_bytes_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte { + hash: [64]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._64, 64), 0) + hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data))) + hash_final(c, &hash[0]) + hash_destroy(c) + return hash +} + +hash_bytes_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [128]byte { + hash: [128]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._128, 128), 0) + hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data))) + hash_final(c, &hash[0]) + hash_destroy(c) + return hash +} + +hash_bytes_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte { + hash := make([]byte, bit_size, allocator) + c: hash_t + hash_init(&c, _check_ctx(ctx, nil, bit_size), 0) + hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data))) + hash_final(c, &hash[0]) + hash_destroy(c) + return hash[:] +} + +hash_file_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([16]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + return hash_stream_16(ctx, os.stream_from_handle(hd)) + } else { + return [16]byte{}, false + } +} + +hash_file_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([20]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + return hash_stream_20(ctx, os.stream_from_handle(hd)) + } else { + return [20]byte{}, false + } +} + +hash_file_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([24]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + return hash_stream_24(ctx, os.stream_from_handle(hd)) + } else { + return [24]byte{}, false + } +} + +hash_file_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([28]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + return hash_stream_28(ctx, os.stream_from_handle(hd)) + } else { + return [28]byte{}, false + } +} + +hash_file_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + return hash_stream_32(ctx, os.stream_from_handle(hd)) + } else { + return [32]byte{}, false + } +} + +hash_file_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([48]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + return hash_stream_48(ctx, os.stream_from_handle(hd)) + } else { + return [48]byte{}, false + } +} + +hash_file_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([64]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + return hash_stream_64(ctx, os.stream_from_handle(hd)) + } else { + return [64]byte{}, false + } +} + +hash_file_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([128]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + return hash_stream_128(ctx, os.stream_from_handle(hd)) + } else { + return [128]byte{}, false + } +} + +hash_file_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, bit_size: int, load_at_once: bool, allocator := context.allocator) -> ([]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + return hash_stream_slice(ctx, os.stream_from_handle(hd), bit_size, allocator) + } else { + return nil, false + } +} + +hash_stream_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([16]byte, bool) { + hash: [16]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._16, 16), 0) + buf := make([]byte, 512) + defer delete(buf) + i := 1 + for i > 0 { + i, _ = s->impl_read(buf) + if i > 0 { + hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i)) + } + } + hash_final(c, &hash[0]) + hash_destroy(c) + return hash, true +} + +hash_stream_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([20]byte, bool) { + hash: [20]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._20, 20), 0) + buf := make([]byte, 512) + defer delete(buf) + i := 1 + for i > 0 { + i, _ = s->impl_read(buf) + if i > 0 { + hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i)) + } + } + hash_final(c, &hash[0]) + hash_destroy(c) + return hash, true +} + +hash_stream_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([24]byte, bool) { + hash: [24]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._24, 24), 0) + buf := make([]byte, 512) + defer delete(buf) + i := 1 + for i > 0 { + i, _ = s->impl_read(buf) + if i > 0 { + hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i)) + } + } + hash_final(c, &hash[0]) + hash_destroy(c) + return hash, true +} + +hash_stream_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([28]byte, bool) { + hash: [28]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._28, 28), 0) + buf := make([]byte, 512) + defer delete(buf) + i := 1 + for i > 0 { + i, _ = s->impl_read(buf) + if i > 0 { + hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i)) + } + } + hash_final(c, &hash[0]) + hash_destroy(c) + return hash, true +} + +hash_stream_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([32]byte, bool) { + hash: [32]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._32, 32), 0) + buf := make([]byte, 512) + defer delete(buf) + i := 1 + for i > 0 { + i, _ = s->impl_read(buf) + if i > 0 { + hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i)) + } + } + hash_final(c, &hash[0]) + hash_destroy(c) + return hash, true +} + +hash_stream_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([48]byte, bool) { + hash: [48]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._48, 48), 0) + buf := make([]byte, 512) + defer delete(buf) + i := 1 + for i > 0 { + i, _ = s->impl_read(buf) + if i > 0 { + hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i)) + } + } + hash_final(c, &hash[0]) + hash_destroy(c) + return hash, true +} + +hash_stream_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([64]byte, bool) { + hash: [64]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._64, 64), 0) + buf := make([]byte, 512) + defer delete(buf) + i := 1 + for i > 0 { + i, _ = s->impl_read(buf) + if i > 0 { + hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i)) + } + } + hash_final(c, &hash[0]) + hash_destroy(c) + return hash, true +} + +hash_stream_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([128]byte, bool) { + hash: [128]byte + c: hash_t + hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._128, 128), 0) + buf := make([]byte, 512) + defer delete(buf) + i := 1 + for i > 0 { + i, _ = s->impl_read(buf) + if i > 0 { + hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i)) + } + } + hash_final(c, &hash[0]) + hash_destroy(c) + return hash, true +} + +hash_stream_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) { + hash := make([]byte, bit_size, allocator) + c: hash_t + hash_init(&c, _check_ctx(ctx, nil, bit_size), 0) + buf := make([]byte, 512) + defer delete(buf) + i := 1 + for i > 0 { + i, _ = s->impl_read(buf) + if i > 0 { + hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i)) + } + } + hash_final(c, &hash[0]) + hash_destroy(c) + return hash[:], true +} + +init :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + c: hash_t + hash_init(&c, ctx.botan_hash_algo, 0) + ctx.external_ctx = c +} + +update :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.external_ctx.(hash_t); ok { + hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data))) + } +} + +final :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.external_ctx.(hash_t); ok { + hash_final(c, &hash[0]) + hash_destroy(c) + } +} + +assign_hash_vtable :: proc(ctx: ^_ctx.Hash_Context, hash_algo: cstring) { + ctx.init = init + ctx.update = update + ctx.final = final + ctx.botan_hash_algo = hash_algo + + switch hash_algo { + case HASH_MD4, HASH_MD5: + ctx.hash_bytes_16 = hash_bytes_16 + ctx.hash_file_16 = hash_file_16 + ctx.hash_stream_16 = hash_stream_16 + + case HASH_SHA1, HASH_RIPEMD_160: + ctx.hash_bytes_20 = hash_bytes_20 + ctx.hash_file_20 = hash_file_20 + ctx.hash_stream_20 = hash_stream_20 + + case HASH_SHA2, HASH_SHA3: + ctx.hash_bytes_28 = hash_bytes_28 + ctx.hash_file_28 = hash_file_28 + ctx.hash_stream_28 = hash_stream_28 + ctx.hash_bytes_32 = hash_bytes_32 + ctx.hash_file_32 = hash_file_32 + ctx.hash_stream_32 = hash_stream_32 + ctx.hash_bytes_48 = hash_bytes_48 + ctx.hash_file_48 = hash_file_48 + ctx.hash_stream_48 = hash_stream_48 + ctx.hash_bytes_64 = hash_bytes_64 + ctx.hash_file_64 = hash_file_64 + ctx.hash_stream_64 = hash_stream_64 + + case HASH_GOST, HASH_WHIRLPOOL, HASH_SM3: + ctx.hash_bytes_32 = hash_bytes_32 + ctx.hash_file_32 = hash_file_32 + ctx.hash_stream_32 = hash_stream_32 + + case HASH_STREEBOG: + ctx.hash_bytes_32 = hash_bytes_32 + ctx.hash_file_32 = hash_file_32 + ctx.hash_stream_32 = hash_stream_32 + ctx.hash_bytes_64 = hash_bytes_64 + ctx.hash_file_64 = hash_file_64 + ctx.hash_stream_64 = hash_stream_64 + + case HASH_BLAKE2B: + ctx.hash_bytes_64 = hash_bytes_64 + ctx.hash_file_64 = hash_file_64 + ctx.hash_stream_64 = hash_stream_64 + + case HASH_TIGER: + ctx.hash_bytes_16 = hash_bytes_16 + ctx.hash_file_16 = hash_file_16 + ctx.hash_stream_16 = hash_stream_16 + ctx.hash_bytes_20 = hash_bytes_20 + ctx.hash_file_20 = hash_file_20 + ctx.hash_stream_20 = hash_stream_20 + ctx.hash_bytes_24 = hash_bytes_24 + ctx.hash_file_24 = hash_file_24 + ctx.hash_stream_24 = hash_stream_24 + + case HASH_SKEIN_512: + ctx.hash_bytes_slice = hash_bytes_slice + ctx.hash_file_slice = hash_file_slice + ctx.hash_stream_slice = hash_stream_slice + } +} + +_check_ctx :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash_size: _ctx.Hash_Size, hash_size_val: int) -> cstring { + ctx.hash_size = hash_size + ctx.hash_size_val = hash_size_val + switch ctx.botan_hash_algo { + case HASH_SHA2: + #partial switch hash_size { + case ._28: return HASH_SHA_224 + case ._32: return HASH_SHA_256 + case ._48: return HASH_SHA_384 + case ._64: return HASH_SHA_512 + } + case HASH_SHA3: + #partial switch hash_size { + case ._28: return HASH_SHA3_224 + case ._32: return HASH_SHA3_256 + case ._48: return HASH_SHA3_384 + case ._64: return HASH_SHA3_512 + } + case HASH_KECCAK: + #partial switch hash_size { + case ._28: return HASH_KECCAK_224 + case ._32: return HASH_KECCAK_256 + case ._48: return HASH_KECCAK_384 + case ._64: return HASH_KECCAK_512 + } + case HASH_STREEBOG: + #partial switch hash_size { + case ._32: return HASH_STREEBOG_256 + case ._64: return HASH_STREEBOG_512 + } + case HASH_TIGER: + #partial switch hash_size { + case ._16: return HASH_TIGER_128 + case ._20: return HASH_TIGER_160 + case ._24: return HASH_TIGER_192 + } + case HASH_SKEIN_512: + return strings.unsafe_string_to_cstring(fmt.tprintf("Skein-512(%d)", hash_size_val * 8)) + case: return ctx.botan_hash_algo + } + return nil +} \ No newline at end of file diff --git a/core/crypto/gost/gost.odin b/core/crypto/gost/gost.odin new file mode 100644 index 000000000..210c7f862 --- /dev/null +++ b/core/crypto/gost/gost.odin @@ -0,0 +1,460 @@ +package gost + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the GOST hashing algorithm, as defined in RFC 5831 +*/ + +import "core:mem" +import "core:os" +import "core:io" + +import "../botan" +import "../_ctx" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + _assign_hash_vtable(ctx) + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_32 = hash_bytes_odin + ctx.hash_file_32 = hash_file_odin + ctx.hash_stream_32 = hash_stream_odin + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan does nothing, since MD2 is not available in Botan +use_botan :: #force_inline proc() { + botan.assign_hash_vtable(_hash_impl, botan.HASH_GOST) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string will hash the given input and return the +// computed hash +hash_string :: proc(data: string) -> [32]byte { + return hash_bytes(transmute([]byte)(data)) +} + +// hash_bytes will hash the given input and return the +// computed hash +hash_bytes :: proc(data: []byte) -> [32]byte { + _create_gost_ctx() + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream will read the stream in chunks and compute a +// hash from its contents +hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_gost_ctx() + return _hash_impl->hash_stream_32(s) +} + +// hash_file will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_gost_ctx() + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash :: proc { + hash_stream, + hash_file, + hash_bytes, + hash_string, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Gost_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Gost_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin(ctx, buf[:]), read_ok + } + } + } + return [32]byte{}, false +} + +@(private) +_create_gost_ctx :: #force_inline proc() { + ctx: Gost_Context + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = ._32 +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + _create_gost_ctx() + if c, ok := ctx.internal_ctx.(Gost_Context); ok { + init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(Gost_Context); ok { + update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(Gost_Context); ok { + final_odin(&c, hash) + } +} + +/* + GOST implementation +*/ + +Gost_Context :: struct { + sum: [8]u32, + hash: [8]u32, + len: [8]u32, + partial: [32]byte, + partial_bytes: byte, +} + +SBOX_1 : [256]u32 +SBOX_2 : [256]u32 +SBOX_3 : [256]u32 +SBOX_4 : [256]u32 + +GOST_ENCRYPT_ROUND :: #force_inline proc "contextless"(l, r, t, k1, k2: u32) -> (u32, u32, u32) { + l, r, t := l, r, t + t = (k1) + r + l ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24] + t = (k2) + l + r ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24] + return l, r, t +} + +GOST_ENCRYPT :: #force_inline proc "contextless"(a, b, c: u32, key: []u32) -> (l, r, t: u32) { + l, r, t = GOST_ENCRYPT_ROUND(a, b, c, key[0], key[1]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[0], key[1]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[0], key[1]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[7], key[6]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[5], key[4]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[3], key[2]) + l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[1], key[0]) + t = r + r = l + l = t + return +} + +gost_bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) { + a, c: u32 + m: [8]u32 + + for i, j := 0, 0; i < 8; i += 1 { + a = u32(buf[j]) | u32(buf[j + 1]) << 8 | u32(buf[j + 2]) << 16 | u32(buf[j + 3]) << 24 + j += 4 + m[i] = a + c = a + c + ctx.sum[i] + ctx.sum[i] = c + c = c < a ? 1 : 0 + } + + gost_compress(ctx.hash[:], m[:]) + ctx.len[0] += bits + if ctx.len[0] < bits { + ctx.len[1] += 1 + } +} + +gost_compress :: proc(h, m: []u32) { + key, u, v, w, s: [8]u32 + + copy(u[:], h) + copy(v[:], m) + + for i := 0; i < 8; i += 2 { + w[0] = u[0] ~ v[0] + w[1] = u[1] ~ v[1] + w[2] = u[2] ~ v[2] + w[3] = u[3] ~ v[3] + w[4] = u[4] ~ v[4] + w[5] = u[5] ~ v[5] + w[6] = u[6] ~ v[6] + w[7] = u[7] ~ v[7] + + key[0] = (w[0] & 0x000000ff) | (w[2] & 0x000000ff) << 8 | (w[4] & 0x000000ff) << 16 | (w[6] & 0x000000ff) << 24 + key[1] = (w[0] & 0x0000ff00) >> 8 | (w[2] & 0x0000ff00) | (w[4] & 0x0000ff00) << 8 | (w[6] & 0x0000ff00) << 16 + key[2] = (w[0] & 0x00ff0000) >> 16 | (w[2] & 0x00ff0000) >> 8 | (w[4] & 0x00ff0000) | (w[6] & 0x00ff0000) << 8 + key[3] = (w[0] & 0xff000000) >> 24 | (w[2] & 0xff000000) >> 16 | (w[4] & 0xff000000) >> 8 | (w[6] & 0xff000000) + key[4] = (w[1] & 0x000000ff) | (w[3] & 0x000000ff) << 8 | (w[5] & 0x000000ff) << 16 | (w[7] & 0x000000ff) << 24 + key[5] = (w[1] & 0x0000ff00) >> 8 | (w[3] & 0x0000ff00) | (w[5] & 0x0000ff00) << 8 | (w[7] & 0x0000ff00) << 16 + key[6] = (w[1] & 0x00ff0000) >> 16 | (w[3] & 0x00ff0000) >> 8 | (w[5] & 0x00ff0000) | (w[7] & 0x00ff0000) << 8 + key[7] = (w[1] & 0xff000000) >> 24 | (w[3] & 0xff000000) >> 16 | (w[5] & 0xff000000) >> 8 | (w[7] & 0xff000000) + + r := h[i] + l := h[i + 1] + t: u32 + l, r, t = GOST_ENCRYPT(l, r, 0, key[:]) + + s[i] = r + s[i + 1] = l + + if i == 6 { + break + } + + l = u[0] ~ u[2] + r = u[1] ~ u[3] + u[0] = u[2] + u[1] = u[3] + u[2] = u[4] + u[3] = u[5] + u[4] = u[6] + u[5] = u[7] + u[6] = l + u[7] = r + + if i == 2 { + u[0] ~= 0xff00ff00 + u[1] ~= 0xff00ff00 + u[2] ~= 0x00ff00ff + u[3] ~= 0x00ff00ff + u[4] ~= 0x00ffff00 + u[5] ~= 0xff0000ff + u[6] ~= 0x000000ff + u[7] ~= 0xff00ffff + } + + l = v[0] + r = v[2] + v[0] = v[4] + v[2] = v[6] + v[4] = l ~ r + v[6] = v[0] ~ r + l = v[1] + r = v[3] + v[1] = v[5] + v[3] = v[7] + v[5] = l ~ r + v[7] = v[1] ~ r + } + + u[0] = m[0] ~ s[6] + u[1] = m[1] ~ s[7] + u[2] = m[2] ~ (s[0] << 16) ~ (s[0] >> 16) ~ (s[0] & 0xffff) ~ + (s[1] & 0xffff) ~ (s[1] >> 16) ~ (s[2] << 16) ~ s[6] ~ (s[6] << 16) ~ + (s[7] & 0xffff0000) ~ (s[7] >> 16) + u[3] = m[3] ~ (s[0] & 0xffff) ~ (s[0] << 16) ~ (s[1] & 0xffff) ~ + (s[1] << 16) ~ (s[1] >> 16) ~ (s[2] << 16) ~ (s[2] >> 16) ~ + (s[3] << 16) ~ s[6] ~ (s[6] << 16) ~ (s[6] >> 16) ~ (s[7] & 0xffff) ~ + (s[7] << 16) ~ (s[7] >> 16) + u[4] = m[4] ~ + (s[0] & 0xffff0000) ~ (s[0] << 16) ~ (s[0] >> 16) ~ + (s[1] & 0xffff0000) ~ (s[1] >> 16) ~ (s[2] << 16) ~ (s[2] >> 16) ~ + (s[3] << 16) ~ (s[3] >> 16) ~ (s[4] << 16) ~ (s[6] << 16) ~ + (s[6] >> 16) ~(s[7] & 0xffff) ~ (s[7] << 16) ~ (s[7] >> 16) + u[5] = m[5] ~ (s[0] << 16) ~ (s[0] >> 16) ~ (s[0] & 0xffff0000) ~ + (s[1] & 0xffff) ~ s[2] ~ (s[2] >> 16) ~ (s[3] << 16) ~ (s[3] >> 16) ~ + (s[4] << 16) ~ (s[4] >> 16) ~ (s[5] << 16) ~ (s[6] << 16) ~ + (s[6] >> 16) ~ (s[7] & 0xffff0000) ~ (s[7] << 16) ~ (s[7] >> 16) + u[6] = m[6] ~ s[0] ~ (s[1] >> 16) ~ (s[2] << 16) ~ s[3] ~ (s[3] >> 16) ~ + (s[4] << 16) ~ (s[4] >> 16) ~ (s[5] << 16) ~ (s[5] >> 16) ~ s[6] ~ + (s[6] << 16) ~ (s[6] >> 16) ~ (s[7] << 16) + u[7] = m[7] ~ (s[0] & 0xffff0000) ~ (s[0] << 16) ~ (s[1] & 0xffff) ~ + (s[1] << 16) ~ (s[2] >> 16) ~ (s[3] << 16) ~ s[4] ~ (s[4] >> 16) ~ + (s[5] << 16) ~ (s[5] >> 16) ~ (s[6] >> 16) ~ (s[7] & 0xffff) ~ + (s[7] << 16) ~ (s[7] >> 16) + + v[0] = h[0] ~ (u[1] << 16) ~ (u[0] >> 16) + v[1] = h[1] ~ (u[2] << 16) ~ (u[1] >> 16) + v[2] = h[2] ~ (u[3] << 16) ~ (u[2] >> 16) + v[3] = h[3] ~ (u[4] << 16) ~ (u[3] >> 16) + v[4] = h[4] ~ (u[5] << 16) ~ (u[4] >> 16) + v[5] = h[5] ~ (u[6] << 16) ~ (u[5] >> 16) + v[6] = h[6] ~ (u[7] << 16) ~ (u[6] >> 16) + v[7] = h[7] ~ (u[0] & 0xffff0000) ~ (u[0] << 16) ~ (u[7] >> 16) ~ (u[1] & 0xffff0000) ~ (u[1] << 16) ~ (u[6] << 16) ~ (u[7] & 0xffff0000) + + h[0] = (v[0] & 0xffff0000) ~ (v[0] << 16) ~ (v[0] >> 16) ~ (v[1] >> 16) ~ + (v[1] & 0xffff0000) ~ (v[2] << 16) ~ (v[3] >> 16) ~ (v[4] << 16) ~ + (v[5] >> 16) ~ v[5] ~ (v[6] >> 16) ~ (v[7] << 16) ~ (v[7] >> 16) ~ + (v[7] & 0xffff) + h[1] = (v[0] << 16) ~ (v[0] >> 16) ~ (v[0] & 0xffff0000) ~ (v[1] & 0xffff) ~ + v[2] ~ (v[2] >> 16) ~ (v[3] << 16) ~ (v[4] >> 16) ~ (v[5] << 16) ~ + (v[6] << 16) ~ v[6] ~ (v[7] & 0xffff0000) ~ (v[7] >> 16) + h[2] = (v[0] & 0xffff) ~ (v[0] << 16) ~ (v[1] << 16) ~ (v[1] >> 16) ~ + (v[1] & 0xffff0000) ~ (v[2] << 16) ~ (v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ + (v[5] >> 16) ~ v[6] ~ (v[6] >> 16) ~ (v[7] & 0xffff) ~ (v[7] << 16) ~ + (v[7] >> 16) + h[3] = (v[0] << 16) ~ (v[0] >> 16) ~ (v[0] & 0xffff0000) ~ + (v[1] & 0xffff0000) ~ (v[1] >> 16) ~ (v[2] << 16) ~ (v[2] >> 16) ~ v[2] ~ + (v[3] << 16) ~ (v[4] >> 16) ~ v[4] ~ (v[5] << 16) ~ (v[6] << 16) ~ + (v[7] & 0xffff) ~ (v[7] >> 16) + h[4] = (v[0] >> 16) ~ (v[1] << 16) ~ v[1] ~ (v[2] >> 16) ~ v[2] ~ + (v[3] << 16) ~ (v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ (v[5] >> 16) ~ + v[5] ~ (v[6] << 16) ~ (v[6] >> 16) ~ (v[7] << 16) + h[5] = (v[0] << 16) ~ (v[0] & 0xffff0000) ~ (v[1] << 16) ~ (v[1] >> 16) ~ + (v[1] & 0xffff0000) ~ (v[2] << 16) ~ v[2] ~ (v[3] >> 16) ~ v[3] ~ + (v[4] << 16) ~ (v[4] >> 16) ~ v[4] ~ (v[5] << 16) ~ (v[6] << 16) ~ + (v[6] >> 16) ~ v[6] ~ (v[7] << 16) ~ (v[7] >> 16) ~ (v[7] & 0xffff0000) + h[6] = v[0] ~ v[2] ~ (v[2] >> 16) ~ v[3] ~ (v[3] << 16) ~ v[4] ~ + (v[4] >> 16) ~ (v[5] << 16) ~ (v[5] >> 16) ~ v[5] ~ (v[6] << 16) ~ + (v[6] >> 16) ~ v[6] ~ (v[7] << 16) ~ v[7] + h[7] = v[0] ~ (v[0] >> 16) ~ (v[1] << 16) ~ (v[1] >> 16) ~ (v[2] << 16) ~ + (v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ v[4] ~ (v[5] >> 16) ~ v[5] ~ + (v[6] << 16) ~ (v[6] >> 16) ~ (v[7] << 16) ~ v[7] +} + +init_odin :: proc(ctx: ^Gost_Context) { + sbox: [8][16]u32 = { + { 10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15 }, + { 5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8 }, + { 7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13 }, + { 4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3 }, + { 7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5 }, + { 7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3 }, + { 13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11 }, + { 1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12 }, + } + + i := 0 + for a := 0; a < 16; a += 1 { + ax := sbox[1][a] << 15 + bx := sbox[3][a] << 23 + cx := sbox[5][a] + cx = (cx >> 1) | (cx << 31) + dx := sbox[7][a] << 7 + for b := 0; b < 16; b, i = b + 1, i + 1 { + SBOX_1[i] = ax | (sbox[0][b] << 11) + SBOX_2[i] = bx | (sbox[2][b] << 19) + SBOX_3[i] = cx | (sbox[4][b] << 27) + SBOX_4[i] = dx | (sbox[6][b] << 3) + } + } +} + +update_odin :: proc(ctx: ^Gost_Context, data: []byte) { + length := byte(len(data)) + j: byte + + i := ctx.partial_bytes + for i < 32 && j < length { + ctx.partial[i] = data[j] + i, j = i + 1, j + 1 + } + + if i < 32 { + ctx.partial_bytes = i + return + } + gost_bytes(ctx, ctx.partial[:], 256) + + for (j + 32) < length { + gost_bytes(ctx, data[j:], 256) + j += 32 + } + + i = 0 + for j < length { + ctx.partial[i] = data[j] + i, j = i + 1, j + 1 + } + ctx.partial_bytes = i +} + +final_odin :: proc(ctx: ^Gost_Context, hash: []byte) { + if ctx.partial_bytes > 0 { + mem.set(&ctx.partial[ctx.partial_bytes], 0, 32 - int(ctx.partial_bytes)) + gost_bytes(ctx, ctx.partial[:], u32(ctx.partial_bytes) << 3) + } + + gost_compress(ctx.hash[:], ctx.len[:]) + gost_compress(ctx.hash[:], ctx.sum[:]) + + for i, j := 0, 0; i < 8; i, j = i + 1, j + 4 { + hash[j] = byte(ctx.hash[i]) + hash[j + 1] = byte(ctx.hash[i] >> 8) + hash[j + 2] = byte(ctx.hash[i] >> 16) + hash[j + 3] = byte(ctx.hash[i] >> 24) + } +} \ No newline at end of file diff --git a/core/crypto/groestl/groestl.odin b/core/crypto/groestl/groestl.odin new file mode 100644 index 000000000..8f9f4547c --- /dev/null +++ b/core/crypto/groestl/groestl.odin @@ -0,0 +1,742 @@ +package groestl + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the GROESTL hashing algorithm, as defined in +*/ + +import "core:os" +import "core:io" + +import "../_ctx" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_28 = hash_bytes_odin_28 + ctx.hash_file_28 = hash_file_odin_28 + ctx.hash_stream_28 = hash_stream_odin_28 + ctx.hash_bytes_32 = hash_bytes_odin_32 + ctx.hash_file_32 = hash_file_odin_32 + ctx.hash_stream_32 = hash_stream_odin_32 + ctx.hash_bytes_48 = hash_bytes_odin_48 + ctx.hash_file_48 = hash_file_odin_48 + ctx.hash_stream_48 = hash_stream_odin_48 + ctx.hash_bytes_64 = hash_bytes_odin_64 + ctx.hash_file_64 = hash_file_odin_64 + ctx.hash_stream_64 = hash_stream_odin_64 + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan does nothing, since GROESTL is not available in Botan +@(warning="GROESTL is not provided by the Botan API. Odin implementation will be used") +use_botan :: #force_inline proc() { + use_odin() +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +@(private) +_create_groestl_ctx :: #force_inline proc(size: _ctx.Hash_Size) { + ctx: Groestl_Context + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = size + #partial switch size { + case ._28: ctx.hashbitlen = 224 + case ._32: ctx.hashbitlen = 256 + case ._48: ctx.hashbitlen = 384 + case ._64: ctx.hashbitlen = 512 + } +} + +/* + High level API +*/ + +// hash_string_224 will hash the given input and return the +// computed hash +hash_string_224 :: proc(data: string) -> [28]byte { + return hash_bytes_224(transmute([]byte)(data)) +} + +// hash_bytes_224 will hash the given input and return the +// computed hash +hash_bytes_224 :: proc(data: []byte) -> [28]byte { + _create_groestl_ctx(._28) + return _hash_impl->hash_bytes_28(data) +} + +// hash_stream_224 will read the stream in chunks and compute a +// hash from its contents +hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) { + _create_groestl_ctx(._28) + return _hash_impl->hash_stream_28(s) +} + +// hash_file_224 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_224 :: proc(path: string, load_at_once: bool) -> ([28]byte, bool) { + _create_groestl_ctx(._28) + return _hash_impl->hash_file_28(path, load_at_once) +} + +hash_224 :: proc { + hash_stream_224, + hash_file_224, + hash_bytes_224, + hash_string_224, +} + +// hash_string_256 will hash the given input and return the +// computed hash +hash_string_256 :: proc(data: string) -> [32]byte { + return hash_bytes_256(transmute([]byte)(data)) +} + +// hash_bytes_256 will hash the given input and return the +// computed hash +hash_bytes_256 :: proc(data: []byte) -> [32]byte { + _create_groestl_ctx(._32) + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream_256 will read the stream in chunks and compute a +// hash from its contents +hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_groestl_ctx(._32) + return _hash_impl->hash_stream_32(s) +} + +// hash_file_256 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_256 :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_groestl_ctx(._32) + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash_256 :: proc { + hash_stream_256, + hash_file_256, + hash_bytes_256, + hash_string_256, +} + +// hash_string_384 will hash the given input and return the +// computed hash +hash_string_384 :: proc(data: string) -> [48]byte { + return hash_bytes_384(transmute([]byte)(data)) +} + +// hash_bytes_384 will hash the given input and return the +// computed hash +hash_bytes_384 :: proc(data: []byte) -> [48]byte { + _create_groestl_ctx(._48) + return _hash_impl->hash_bytes_48(data) +} + +// hash_stream_384 will read the stream in chunks and compute a +// hash from its contents +hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) { + _create_groestl_ctx(._48) + return _hash_impl->hash_stream_48(s) +} + +// hash_file_384 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_384 :: proc(path: string, load_at_once: bool) -> ([48]byte, bool) { + _create_groestl_ctx(._48) + return _hash_impl->hash_file_48(path, load_at_once) +} + +hash_384 :: proc { + hash_stream_384, + hash_file_384, + hash_bytes_384, + hash_string_384, +} + +// hash_string_512 will hash the given input and return the +// computed hash +hash_string_512 :: proc(data: string) -> [64]byte { + return hash_bytes_512(transmute([]byte)(data)) +} + +// hash_bytes_512 will hash the given input and return the +// computed hash +hash_bytes_512 :: proc(data: []byte) -> [64]byte { + _create_groestl_ctx(._64) + return _hash_impl->hash_bytes_64(data) +} + +// hash_stream_512 will read the stream in chunks and compute a +// hash from its contents +hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) { + _create_groestl_ctx(._64) + return _hash_impl->hash_stream_64(s) +} + +// hash_file_512 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_512 :: proc(path: string, load_at_once: bool) -> ([64]byte, bool) { + _create_groestl_ctx(._64) + return _hash_impl->hash_file_64(path, load_at_once) +} + +hash_512 :: proc { + hash_stream_512, + hash_file_512, + hash_bytes_512, + hash_string_512, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte { + hash: [28]byte + if c, ok := ctx.internal_ctx.(Groestl_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) { + hash: [28]byte + if c, ok := ctx.internal_ctx.(Groestl_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([28]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_28(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_28(ctx, buf[:]), read_ok + } + } + } + return [28]byte{}, false +} + +hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Groestl_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Groestl_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_32(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_32(ctx, buf[:]), read_ok + } + } + } + return [32]byte{}, false +} + +hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte { + hash: [48]byte + if c, ok := ctx.internal_ctx.(Groestl_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) { + hash: [48]byte + if c, ok := ctx.internal_ctx.(Groestl_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([48]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_48(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_48(ctx, buf[:]), read_ok + } + } + } + return [48]byte{}, false +} + +hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte { + hash: [64]byte + if c, ok := ctx.internal_ctx.(Groestl_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) { + hash: [64]byte + if c, ok := ctx.internal_ctx.(Groestl_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([64]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_64(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_64(ctx, buf[:]), read_ok + } + } + } + return [64]byte{}, false +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + _create_groestl_ctx(ctx.hash_size) + if c, ok := ctx.internal_ctx.(Groestl_Context); ok { + init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(Groestl_Context); ok { + update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(Groestl_Context); ok { + final_odin(&c, hash) + } +} + +/* + GROESTL implementation +*/ + +SBOX := [256]byte { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, + 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, + 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, + 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, + 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, + 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, + 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, + 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, + 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, + 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, + 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, +} + +SHIFT := [2][2][8]int { + {{0, 1, 2, 3, 4, 5, 6, 7}, {1, 3, 5, 7, 0, 2, 4, 6}}, + {{0, 1, 2, 3, 4, 5, 6, 11}, {1, 3, 5, 11, 0, 2, 4, 6}}, +} + +Groestl_Context :: struct { + chaining: [8][16]byte, + block_counter: u64, + hashbitlen: int, + buffer: [128]byte, + buf_ptr: int, + bits_in_last_byte: int, + columns: int, + rounds: int, + statesize: int, +} + +Groestl_Variant :: enum { + P512 = 0, + Q512 = 1, + P1024 = 2, + Q1024 = 3, +} + +MUL2 :: #force_inline proc "contextless"(b: byte) -> byte { + return (b >> 7) != 0 ? (b << 1) ~ 0x1b : (b << 1) +} + +MUL3 :: #force_inline proc "contextless"(b: byte) -> byte { + return MUL2(b) ~ b +} + +MUL4 :: #force_inline proc "contextless"(b: byte) -> byte { + return MUL2(MUL2(b)) +} + +MUL5 :: #force_inline proc "contextless"(b: byte) -> byte { + return MUL4(b) ~ b +} + +MUL6 :: #force_inline proc "contextless"(b: byte) -> byte { + return MUL4(b) ~ MUL2(b) +} + +MUL7 :: #force_inline proc "contextless"(b: byte) -> byte { + return MUL4(b) ~ MUL2(b) ~ b +} + +sub_bytes :: #force_inline proc (x: [][16]byte, columns: int) { + for i := 0; i < 8; i += 1 { + for j := 0; j < columns; j += 1 { + x[i][j] = SBOX[x[i][j]] + } + } +} + +shift_bytes :: #force_inline proc (x: [][16]byte, columns: int, v: Groestl_Variant) { + temp: [16]byte + R := &SHIFT[int(v) / 2][int(v) & 1] + + for i := 0; i < 8; i += 1 { + for j := 0; j < columns; j += 1 { + temp[j] = x[i][(j + R[i]) % columns] + } + for j := 0; j < columns; j += 1 { + x[i][j] = temp[j] + } + } +} + +mix_bytes :: #force_inline proc (x: [][16]byte, columns: int) { + temp: [8]byte + + for i := 0; i < columns; i += 1 { + for j := 0; j < 8; j += 1 { + temp[j] = MUL2(x[(j + 0) % 8][i]) ~ + MUL2(x[(j + 1) % 8][i]) ~ + MUL3(x[(j + 2) % 8][i]) ~ + MUL4(x[(j + 3) % 8][i]) ~ + MUL5(x[(j + 4) % 8][i]) ~ + MUL3(x[(j + 5) % 8][i]) ~ + MUL5(x[(j + 6) % 8][i]) ~ + MUL7(x[(j + 7) % 8][i]) + } + for j := 0; j < 8; j += 1 { + x[j][i] = temp[j] + } + } +} + +p :: #force_inline proc (ctx: ^Groestl_Context, x: [][16]byte) { + v := ctx.columns == 8 ? Groestl_Variant.P512 : Groestl_Variant.P1024 + for i := 0; i < ctx.rounds; i += 1 { + add_roundconstant(x, ctx.columns, byte(i), v) + sub_bytes(x, ctx.columns) + shift_bytes(x, ctx.columns, v) + mix_bytes(x, ctx.columns) + } +} + +q :: #force_inline proc (ctx: ^Groestl_Context, x: [][16]byte) { + v := ctx.columns == 8 ? Groestl_Variant.Q512 : Groestl_Variant.Q1024 + for i := 0; i < ctx.rounds; i += 1 { + add_roundconstant(x, ctx.columns, byte(i), v) + sub_bytes(x, ctx.columns) + shift_bytes(x, ctx.columns, v) + mix_bytes(x, ctx.columns) + } +} + +transform :: proc(ctx: ^Groestl_Context, input: []byte, msglen: u32) { + tmp1, tmp2: [8][16]byte + input, msglen := input, msglen + + for msglen >= u32(ctx.statesize) { + for i := 0; i < 8; i += 1 { + for j := 0; j < ctx.columns; j += 1 { + tmp1[i][j] = ctx.chaining[i][j] ~ input[j * 8 + i] + tmp2[i][j] = input[j * 8 + i] + } + } + + p(ctx, tmp1[:]) + q(ctx, tmp2[:]) + + for i := 0; i < 8; i += 1 { + for j := 0; j < ctx.columns; j += 1 { + ctx.chaining[i][j] ~= tmp1[i][j] ~ tmp2[i][j] + } + } + + ctx.block_counter += 1 + msglen -= u32(ctx.statesize) + input = input[ctx.statesize:] + } +} + +output_transformation :: proc(ctx: ^Groestl_Context) { + temp: [8][16]byte + + for i := 0; i < 8; i += 1 { + for j := 0; j < ctx.columns; j += 1 { + temp[i][j] = ctx.chaining[i][j] + } + } + + p(ctx, temp[:]) + + for i := 0; i < 8; i += 1 { + for j := 0; j < ctx.columns; j += 1 { + ctx.chaining[i][j] ~= temp[i][j] + } + } +} + +add_roundconstant :: proc(x: [][16]byte, columns: int, round: byte, v: Groestl_Variant) { + switch (i32(v) & 1) { + case 0: + for i := 0; i < columns; i += 1 { + x[0][i] ~= byte(i << 4) ~ round + } + case 1: + for i := 0; i < columns; i += 1 { + for j := 0; j < 7; j += 1 { + x[j][i] ~= 0xff + } + } + for i := 0; i < columns; i += 1 { + x[7][i] ~= byte(i << 4) ~ 0xff ~ round + } + } +} + +init_odin :: proc(ctx: ^Groestl_Context) { + if ctx.hashbitlen <= 256 { + ctx.rounds = 10 + ctx.columns = 8 + ctx.statesize = 64 + } else { + ctx.rounds = 14 + ctx.columns = 16 + ctx.statesize = 128 + } + for i := 8 - size_of(i32); i < 8; i += 1 { + ctx.chaining[i][ctx.columns - 1] = byte(ctx.hashbitlen >> (8 * (7 - uint(i)))) + } +} + +update_odin :: proc(ctx: ^Groestl_Context, data: []byte) { + databitlen := len(data) * 8 + msglen := databitlen / 8 + rem := databitlen % 8 + + i: int + assert(ctx.bits_in_last_byte == 0) + + if ctx.buf_ptr != 0 { + for i = 0; ctx.buf_ptr < ctx.statesize && i < msglen; i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1 { + ctx.buffer[ctx.buf_ptr] = data[i] + } + + if ctx.buf_ptr < ctx.statesize { + if rem != 0 { + ctx.bits_in_last_byte = rem + ctx.buffer[ctx.buf_ptr] = data[i] + ctx.buf_ptr += 1 + } + return + } + + ctx.buf_ptr = 0 + transform(ctx, ctx.buffer[:], u32(ctx.statesize)) + } + + transform(ctx, data[i:], u32(msglen - i)) + i += ((msglen - i) / ctx.statesize) * ctx.statesize + for i < msglen { + ctx.buffer[ctx.buf_ptr] = data[i] + i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1 + } + + if rem != 0 { + ctx.bits_in_last_byte = rem + ctx.buffer[ctx.buf_ptr] = data[i] + ctx.buf_ptr += 1 + } +} + +final_odin :: proc(ctx: ^Groestl_Context, hash: []byte) { + hashbytelen := ctx.hashbitlen / 8 + + if ctx.bits_in_last_byte != 0 { + ctx.buffer[ctx.buf_ptr - 1] &= ((1 << uint(ctx.bits_in_last_byte)) - 1) << (8 - uint(ctx.bits_in_last_byte)) + ctx.buffer[ctx.buf_ptr - 1] ~= 0x1 << (7 - uint(ctx.bits_in_last_byte)) + } else { + ctx.buffer[ctx.buf_ptr] = 0x80 + ctx.buf_ptr += 1 + } + + if ctx.buf_ptr > ctx.statesize - 8 { + for ctx.buf_ptr < ctx.statesize { + ctx.buffer[ctx.buf_ptr] = 0 + ctx.buf_ptr += 1 + } + transform(ctx, ctx.buffer[:], u32(ctx.statesize)) + ctx.buf_ptr = 0 + } + + for ctx.buf_ptr < ctx.statesize - 8 { + ctx.buffer[ctx.buf_ptr] = 0 + ctx.buf_ptr += 1 + } + + ctx.block_counter += 1 + ctx.buf_ptr = ctx.statesize + + for ctx.buf_ptr > ctx.statesize - 8 { + ctx.buf_ptr -= 1 + ctx.buffer[ctx.buf_ptr] = byte(ctx.block_counter) + ctx.block_counter >>= 8 + } + + transform(ctx, ctx.buffer[:], u32(ctx.statesize)) + output_transformation(ctx) + + for i, j := ctx.statesize - hashbytelen , 0; i < ctx.statesize; i, j = i + 1, j + 1 { + hash[j] = ctx.chaining[i % 8][i / 8] + } +} \ No newline at end of file diff --git a/core/crypto/haval/haval.odin b/core/crypto/haval/haval.odin new file mode 100644 index 000000000..943a54a23 --- /dev/null +++ b/core/crypto/haval/haval.odin @@ -0,0 +1,1372 @@ +package haval + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation for the HAVAL hashing algorithm as defined in +*/ + +import "core:mem" +import "core:os" +import "core:io" + +import "../util" +import "../_ctx" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_16 = hash_bytes_odin_16 + ctx.hash_file_16 = hash_file_odin_16 + ctx.hash_stream_16 = hash_stream_odin_16 + ctx.hash_bytes_20 = hash_bytes_odin_20 + ctx.hash_file_20 = hash_file_odin_20 + ctx.hash_stream_20 = hash_stream_odin_20 + ctx.hash_bytes_24 = hash_bytes_odin_24 + ctx.hash_file_24 = hash_file_odin_24 + ctx.hash_stream_24 = hash_stream_odin_24 + ctx.hash_bytes_28 = hash_bytes_odin_28 + ctx.hash_file_28 = hash_file_odin_28 + ctx.hash_stream_28 = hash_stream_odin_28 + ctx.hash_bytes_32 = hash_bytes_odin_32 + ctx.hash_file_32 = hash_file_odin_32 + ctx.hash_stream_32 = hash_stream_odin_32 + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan does nothing, since HAVAL is not available in Botan +@(warning="HAVAL is not provided by the Botan API. Odin implementation will be used") +use_botan :: #force_inline proc() { + use_odin() +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +@(private) +_create_haval_ctx :: #force_inline proc(size: _ctx.Hash_Size, rounds: u32) { + ctx: Haval_Context + ctx.rounds = rounds + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = size + #partial switch size { + case ._16: ctx.hashbitlen = 128 + case ._20: ctx.hashbitlen = 160 + case ._24: ctx.hashbitlen = 192 + case ._28: ctx.hashbitlen = 224 + case ._32: ctx.hashbitlen = 256 + } +} + +/* + High level API +*/ + +// hash_string_128_3 will hash the given input and return the +// computed hash +hash_string_128_3 :: proc(data: string) -> [16]byte { + return hash_bytes_128_3(transmute([]byte)(data)) +} + +// hash_bytes_128_3 will hash the given input and return the +// computed hash +hash_bytes_128_3 :: proc(data: []byte) -> [16]byte { + _create_haval_ctx(._16, 3) + return _hash_impl->hash_bytes_16(data) +} + +// hash_stream_128_3 will read the stream in chunks and compute a +// hash from its contents +hash_stream_128_3 :: proc(s: io.Stream) -> ([16]byte, bool) { + _create_haval_ctx(._16, 3) + return _hash_impl->hash_stream_16(s) +} + +// hash_file_128_3 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_128_3 :: proc(path: string, load_at_once: bool) -> ([16]byte, bool) { + _create_haval_ctx(._16, 3) + return _hash_impl->hash_file_16(path, load_at_once) +} + +hash_128_3 :: proc { + hash_stream_128_3, + hash_file_128_3, + hash_bytes_128_3, + hash_string_128_3, +} + +// hash_string_128_4 will hash the given input and return the +// computed hash +hash_string_128_4 :: proc(data: string) -> [16]byte { + return hash_bytes_128_4(transmute([]byte)(data)) +} + +// hash_bytes_128_4 will hash the given input and return the +// computed hash +hash_bytes_128_4 :: proc(data: []byte) -> [16]byte { + _create_haval_ctx(._16, 4) + return _hash_impl->hash_bytes_16(data) +} + +// hash_stream_128_4 will read the stream in chunks and compute a +// hash from its contents +hash_stream_128_4 :: proc(s: io.Stream) -> ([16]byte, bool) { + _create_haval_ctx(._16, 4) + return _hash_impl->hash_stream_16(s) +} + +// hash_file_128_4 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_128_4 :: proc(path: string, load_at_once: bool) -> ([16]byte, bool) { + _create_haval_ctx(._16, 4) + return _hash_impl->hash_file_16(path, load_at_once) +} + +hash_128_4 :: proc { + hash_stream_128_4, + hash_file_128_4, + hash_bytes_128_4, + hash_string_128_4, +} + +// hash_string_128_5 will hash the given input and return the +// computed hash +hash_string_128_5 :: proc(data: string) -> [16]byte { + return hash_bytes_128_5(transmute([]byte)(data)) +} + +// hash_bytes_128_5 will hash the given input and return the +// computed hash +hash_bytes_128_5 :: proc(data: []byte) -> [16]byte { + _create_haval_ctx(._16, 5) + return _hash_impl->hash_bytes_16(data) +} + +// hash_stream_128_5 will read the stream in chunks and compute a +// hash from its contents +hash_stream_128_5 :: proc(s: io.Stream) -> ([16]byte, bool) { + _create_haval_ctx(._16, 5) + return _hash_impl->hash_stream_16(s) +} + +// hash_file_128_5 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_128_5 :: proc(path: string, load_at_once: bool) -> ([16]byte, bool) { + _create_haval_ctx(._16, 5) + return _hash_impl->hash_file_16(path, load_at_once) +} + +hash_128_5 :: proc { + hash_stream_128_5, + hash_file_128_5, + hash_bytes_128_5, + hash_string_128_5, +} + +// hash_string_160_3 will hash the given input and return the +// computed hash +hash_string_160_3 :: proc(data: string) -> [20]byte { + return hash_bytes_160_3(transmute([]byte)(data)) +} + +// hash_bytes_160_3 will hash the given input and return the +// computed hash +hash_bytes_160_3 :: proc(data: []byte) -> [20]byte { + _create_haval_ctx(._20, 3) + return _hash_impl->hash_bytes_20(data) +} + +// hash_stream_160_3 will read the stream in chunks and compute a +// hash from its contents +hash_stream_160_3 :: proc(s: io.Stream) -> ([20]byte, bool) { + _create_haval_ctx(._20, 3) + return _hash_impl->hash_stream_20(s) +} + +// hash_file_160_3 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_160_3 :: proc(path: string, load_at_once: bool) -> ([20]byte, bool) { + _create_haval_ctx(._20, 3) + return _hash_impl->hash_file_20(path, load_at_once) +} + +hash_160_3 :: proc { + hash_stream_160_3, + hash_file_160_3, + hash_bytes_160_3, + hash_string_160_3, +} + +// hash_string_160_4 will hash the given input and return the +// computed hash +hash_string_160_4 :: proc(data: string) -> [20]byte { + return hash_bytes_160_4(transmute([]byte)(data)) +} + +// hash_bytes_160_4 will hash the given input and return the +// computed hash +hash_bytes_160_4 :: proc(data: []byte) -> [20]byte { + _create_haval_ctx(._20, 4) + return _hash_impl->hash_bytes_20(data) +} + +// hash_stream_160_4 will read the stream in chunks and compute a +// hash from its contents +hash_stream_160_4 :: proc(s: io.Stream) -> ([20]byte, bool) { + _create_haval_ctx(._20, 4) + return _hash_impl->hash_stream_20(s) +} + +// hash_file_160_4 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_160_4 :: proc(path: string, load_at_once: bool) -> ([20]byte, bool) { + _create_haval_ctx(._20, 4) + return _hash_impl->hash_file_20(path, load_at_once) +} + +hash_160_4 :: proc { + hash_stream_160_4, + hash_file_160_4, + hash_bytes_160_4, + hash_string_160_4, +} + +// hash_string_160_5 will hash the given input and return the +// computed hash +hash_string_160_5 :: proc(data: string) -> [20]byte { + return hash_bytes_160_5(transmute([]byte)(data)) +} + +// hash_bytes_160_5 will hash the given input and return the +// computed hash +hash_bytes_160_5 :: proc(data: []byte) -> [20]byte { + _create_haval_ctx(._20, 5) + return _hash_impl->hash_bytes_20(data) +} + +// hash_stream_160_5 will read the stream in chunks and compute a +// hash from its contents +hash_stream_160_5 :: proc(s: io.Stream) -> ([20]byte, bool) { + _create_haval_ctx(._20, 5) + return _hash_impl->hash_stream_20(s) +} + +// hash_file_160_5 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_160_5 :: proc(path: string, load_at_once: bool) -> ([20]byte, bool) { + _create_haval_ctx(._20, 5) + return _hash_impl->hash_file_20(path, load_at_once) +} + +hash_160_5 :: proc { + hash_stream_160_5, + hash_file_160_5, + hash_bytes_160_5, + hash_string_160_5, +} + +// hash_string_192_3 will hash the given input and return the +// computed hash +hash_string_192_3 :: proc(data: string) -> [24]byte { + return hash_bytes_192_3(transmute([]byte)(data)) +} + +// hash_bytes_192_3 will hash the given input and return the +// computed hash +hash_bytes_192_3 :: proc(data: []byte) -> [24]byte { + _create_haval_ctx(._24, 3) + return _hash_impl->hash_bytes_24(data) +} + +// hash_stream_192_3 will read the stream in chunks and compute a +// hash from its contents +hash_stream_192_3 :: proc(s: io.Stream) -> ([24]byte, bool) { + _create_haval_ctx(._24, 3) + return _hash_impl->hash_stream_24(s) +} + +// hash_file_192_3 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_192_3 :: proc(path: string, load_at_once: bool) -> ([24]byte, bool) { + _create_haval_ctx(._24, 3) + return _hash_impl->hash_file_24(path, load_at_once) +} + +hash_192_3 :: proc { + hash_stream_192_3, + hash_file_192_3, + hash_bytes_192_3, + hash_string_192_3, +} + +// hash_string_192_4 will hash the given input and return the +// computed hash +hash_string_192_4 :: proc(data: string) -> [24]byte { + return hash_bytes_192_4(transmute([]byte)(data)) +} + +// hash_bytes_192_4 will hash the given input and return the +// computed hash +hash_bytes_192_4 :: proc(data: []byte) -> [24]byte { + _create_haval_ctx(._24, 4) + return _hash_impl->hash_bytes_24(data) +} + +// hash_stream_192_4 will read the stream in chunks and compute a +// hash from its contents +hash_stream_192_4 :: proc(s: io.Stream) -> ([24]byte, bool) { + _create_haval_ctx(._24, 4) + return _hash_impl->hash_stream_24(s) +} + +// hash_file_192_4 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_192_4 :: proc(path: string, load_at_once: bool) -> ([24]byte, bool) { + _create_haval_ctx(._24, 4) + return _hash_impl->hash_file_24(path, load_at_once) +} + +hash_192_4 :: proc { + hash_stream_192_4, + hash_file_192_4, + hash_bytes_192_4, + hash_string_192_4, +} + +// hash_string_192_5 will hash the given input and return the +// computed hash +hash_string_192_5 :: proc(data: string) -> [24]byte { + return hash_bytes_192_5(transmute([]byte)(data)) +} + +// hash_bytes_224_5 will hash the given input and return the +// computed hash +hash_bytes_192_5 :: proc(data: []byte) -> [24]byte { + _create_haval_ctx(._24, 5) + return _hash_impl->hash_bytes_24(data) +} + +// hash_stream_192_5 will read the stream in chunks and compute a +// hash from its contents +hash_stream_192_5 :: proc(s: io.Stream) -> ([24]byte, bool) { + _create_haval_ctx(._24, 5) + return _hash_impl->hash_stream_24(s) +} + +// hash_file_192_5 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_192_5 :: proc(path: string, load_at_once: bool) -> ([24]byte, bool) { + _create_haval_ctx(._24, 5) + return _hash_impl->hash_file_24(path, load_at_once) +} + +hash_192_5 :: proc { + hash_stream_192_5, + hash_file_192_5, + hash_bytes_192_5, + hash_string_192_5, +} + +// hash_string_224_3 will hash the given input and return the +// computed hash +hash_string_224_3 :: proc(data: string) -> [28]byte { + return hash_bytes_224_3(transmute([]byte)(data)) +} + +// hash_bytes_224_3 will hash the given input and return the +// computed hash +hash_bytes_224_3 :: proc(data: []byte) -> [28]byte { + _create_haval_ctx(._28, 3) + return _hash_impl->hash_bytes_28(data) +} + +// hash_stream_224_3 will read the stream in chunks and compute a +// hash from its contents +hash_stream_224_3 :: proc(s: io.Stream) -> ([28]byte, bool) { + _create_haval_ctx(._28, 3) + return _hash_impl->hash_stream_28(s) +} + +// hash_file_224_3 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_224_3 :: proc(path: string, load_at_once: bool) -> ([28]byte, bool) { + _create_haval_ctx(._28, 3) + return _hash_impl->hash_file_28(path, load_at_once) +} + +hash_224_3 :: proc { + hash_stream_224_3, + hash_file_224_3, + hash_bytes_224_3, + hash_string_224_3, +} + +// hash_string_224_4 will hash the given input and return the +// computed hash +hash_string_224_4 :: proc(data: string) -> [28]byte { + return hash_bytes_224_4(transmute([]byte)(data)) +} + +// hash_bytes_224_4 will hash the given input and return the +// computed hash +hash_bytes_224_4 :: proc(data: []byte) -> [28]byte { + _create_haval_ctx(._28, 4) + return _hash_impl->hash_bytes_28(data) +} + +// hash_stream_224_4 will read the stream in chunks and compute a +// hash from its contents +hash_stream_224_4 :: proc(s: io.Stream) -> ([28]byte, bool) { + _create_haval_ctx(._28, 4) + return _hash_impl->hash_stream_28(s) +} + +// hash_file_224_4 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_224_4 :: proc(path: string, load_at_once: bool) -> ([28]byte, bool) { + _create_haval_ctx(._28, 4) + return _hash_impl->hash_file_28(path, load_at_once) +} + +hash_224_4 :: proc { + hash_stream_224_4, + hash_file_224_4, + hash_bytes_224_4, + hash_string_224_4, +} + +// hash_string_224_5 will hash the given input and return the +// computed hash +hash_string_224_5 :: proc(data: string) -> [28]byte { + return hash_bytes_224_5(transmute([]byte)(data)) +} + +// hash_bytes_224_5 will hash the given input and return the +// computed hash +hash_bytes_224_5 :: proc(data: []byte) -> [28]byte { + _create_haval_ctx(._28, 5) + return _hash_impl->hash_bytes_28(data) +} + +// hash_stream_224_5 will read the stream in chunks and compute a +// hash from its contents +hash_stream_224_5 :: proc(s: io.Stream) -> ([28]byte, bool) { + _create_haval_ctx(._28, 5) + return _hash_impl->hash_stream_28(s) +} + +// hash_file_224_5 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_224_5 :: proc(path: string, load_at_once: bool) -> ([28]byte, bool) { + _create_haval_ctx(._28, 5) + return _hash_impl->hash_file_28(path, load_at_once) +} + +hash_224_5 :: proc { + hash_stream_224_5, + hash_file_224_5, + hash_bytes_224_5, + hash_string_224_5, +} + +// hash_string_256_3 will hash the given input and return the +// computed hash +hash_string_256_3 :: proc(data: string) -> [32]byte { + return hash_bytes_256_3(transmute([]byte)(data)) +} + +// hash_bytes_256_3 will hash the given input and return the +// computed hash +hash_bytes_256_3 :: proc(data: []byte) -> [32]byte { + _create_haval_ctx(._32, 3) + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream_256_3 will read the stream in chunks and compute a +// hash from its contents +hash_stream_256_3 :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_haval_ctx(._32, 3) + return _hash_impl->hash_stream_32(s) +} + +// hash_file_256_3 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_256_3 :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_haval_ctx(._32, 3) + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash_256_3 :: proc { + hash_stream_256_3, + hash_file_256_3, + hash_bytes_256_3, + hash_string_256_3, +} + +// hash_string_256_4 will hash the given input and return the +// computed hash +hash_string_256_4 :: proc(data: string) -> [32]byte { + return hash_bytes_256_4(transmute([]byte)(data)) +} + +// hash_bytes_256_4 will hash the given input and return the +// computed hash +hash_bytes_256_4 :: proc(data: []byte) -> [32]byte { + _create_haval_ctx(._32, 4) + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream_256_4 will read the stream in chunks and compute a +// hash from its contents +hash_stream_256_4 :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_haval_ctx(._32, 4) + return _hash_impl->hash_stream_32(s) +} + +// hash_file_256_4 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_256_4 :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_haval_ctx(._32, 4) + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash_256_4 :: proc { + hash_stream_256_4, + hash_file_256_4, + hash_bytes_256_4, + hash_string_256_4, +} + +// hash_string_256_5 will hash the given input and return the +// computed hash +hash_string_256_5 :: proc(data: string) -> [32]byte { + return hash_bytes_256_5(transmute([]byte)(data)) +} + +// hash_bytes_256_5 will hash the given input and return the +// computed hash +hash_bytes_256_5 :: proc(data: []byte) -> [32]byte { + _create_haval_ctx(._32, 5) + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream_256_5 will read the stream in chunks and compute a +// hash from its contents +hash_stream_256_5 :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_haval_ctx(._32, 5) + return _hash_impl->hash_stream_32(s) +} + +// hash_file_256_5 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_256_5 :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_haval_ctx(._32, 5) + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash_256_5 :: proc { + hash_stream_256_5, + hash_file_256_5, + hash_bytes_256_5, + hash_string_256_5, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte { + hash: [16]byte + if c, ok := ctx.internal_ctx.(Haval_Context); ok { + init_odin(&c) + c.str_len = u32(len(data)) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) { + hash: [16]byte + if c, ok := ctx.internal_ctx.(Haval_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + c.str_len = u32(len(buf[:read])) + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([16]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_16(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_16(ctx, buf[:]), read_ok + } + } + } + return [16]byte{}, false +} + +hash_bytes_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte { + hash: [20]byte + if c, ok := ctx.internal_ctx.(Haval_Context); ok { + init_odin(&c) + c.str_len = u32(len(data)) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) { + hash: [20]byte + if c, ok := ctx.internal_ctx.(Haval_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + c.str_len = u32(len(buf[:read])) + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([20]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_20(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_20(ctx, buf[:]), read_ok + } + } + } + return [20]byte{}, false +} + +hash_bytes_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [24]byte { + hash: [24]byte + if c, ok := ctx.internal_ctx.(Haval_Context); ok { + init_odin(&c) + c.str_len = u32(len(data)) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([24]byte, bool) { + hash: [24]byte + if c, ok := ctx.internal_ctx.(Haval_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + c.str_len = u32(len(buf[:read])) + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([24]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_24(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_24(ctx, buf[:]), read_ok + } + } + } + return [24]byte{}, false +} + +hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte { + hash: [28]byte + if c, ok := ctx.internal_ctx.(Haval_Context); ok { + init_odin(&c) + c.str_len = u32(len(data)) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) { + hash: [28]byte + if c, ok := ctx.internal_ctx.(Haval_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + c.str_len = u32(len(buf[:read])) + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([28]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_28(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_28(ctx, buf[:]), read_ok + } + } + } + return [28]byte{}, false +} + +hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Haval_Context); ok { + init_odin(&c) + c.str_len = u32(len(data)) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Haval_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + c.str_len = u32(len(buf[:read])) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_32(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_32(ctx, buf[:]), read_ok + } + } + } + return [32]byte{}, false +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + if c, ok := ctx.internal_ctx.(Haval_Context); ok { + init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(Haval_Context); ok { + c.str_len = u32(len(data)) + update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(Haval_Context); ok { + final_odin(&c, hash) + } +} + +/* + HAVAL implementation +*/ + +HAVAL_VERSION :: 1 + +Haval_Context :: struct { + count: [2]u32, + fingerprint: [8]u32, + block: [32]u32, + remainder: [128]byte, + rounds: u32, + hashbitlen: u32, + str_len: u32, +} + +PADDING := [128]byte { + 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +} + +F_1 :: #force_inline proc(x6, x5, x4, x3, x2, x1, x0: u32) -> u32 { + return ((x1) & ((x0) ~ (x4)) ~ (x2) & (x5) ~ (x3) & (x6) ~ (x0)) +} + +F_2 :: #force_inline proc(x6, x5, x4, x3, x2, x1, x0: u32) -> u32 { + return ((x2) & ((x1) & ~(x3) ~ (x4) & (x5) ~ (x6) ~ (x0)) ~ (x4) & ((x1) ~ (x5)) ~ (x3) & (x5) ~ (x0)) +} + +F_3 :: #force_inline proc(x6, x5, x4, x3, x2, x1, x0: u32) -> u32 { + return ((x3) & ((x1) & (x2) ~ (x6) ~ (x0)) ~ (x1) & (x4) ~ (x2) & (x5) ~ (x0)) +} + +F_4 :: #force_inline proc(x6, x5, x4, x3, x2, x1, x0: u32) -> u32 { + return ((x4) & ((x5) & ~(x2) ~ (x3) & ~(x6) ~ (x1) ~ (x6) ~ (x0)) ~ (x3) & ((x1) & (x2) ~ (x5) ~ (x6)) ~ (x2) & (x6) ~ (x0)) +} + +F_5 :: #force_inline proc(x6, x5, x4, x3, x2, x1, x0: u32) -> u32 { + return ((x0) & ((x1) & (x2) & (x3) ~ ~(x5)) ~ (x1) & (x4) ~ (x2) & (x5) ~ (x3) & (x6)) +} + +FPHI_1 :: #force_inline proc(x6, x5, x4, x3, x2, x1, x0, rounds: u32) -> u32 { + switch rounds { + case 3: return F_1(x1, x0, x3, x5, x6, x2, x4) + case 4: return F_1(x2, x6, x1, x4, x5, x3, x0) + case 5: return F_1(x3, x4, x1, x0, x5, x2, x6) + case: assert(rounds < 3 || rounds > 5, "Rounds count not supported!") + } + return 0 +} + +FPHI_2 :: #force_inline proc(x6, x5, x4, x3, x2, x1, x0, rounds: u32) -> u32 { + switch rounds { + case 3: return F_2(x4, x2, x1, x0, x5, x3, x6) + case 4: return F_2(x3, x5, x2, x0, x1, x6, x4) + case 5: return F_2(x6, x2, x1, x0, x3, x4, x5) + case: assert(rounds < 3 || rounds > 5, "Rounds count not supported!") + } + return 0 +} + +FPHI_3 :: #force_inline proc(x6, x5, x4, x3, x2, x1, x0, rounds: u32) -> u32 { + switch rounds { + case 3: return F_3(x6, x1, x2, x3, x4, x5, x0) + case 4: return F_3(x1, x4, x3, x6, x0, x2, x5) + case 5: return F_3(x2, x6, x0, x4, x3, x1, x5) + case: assert(rounds < 3 || rounds > 5, "Rounds count not supported!") + } + return 0 +} + +FPHI_4 :: #force_inline proc(x6, x5, x4, x3, x2, x1, x0, rounds: u32) -> u32 { + switch rounds { + case 4: return F_4(x6, x4, x0, x5, x2, x1, x3) + case 5: return F_4(x1, x5, x3, x2, x0, x4, x6) + case: assert(rounds < 4 || rounds > 5, "Rounds count not supported!") + } + return 0 +} + +FPHI_5 :: #force_inline proc(x6, x5, x4, x3, x2, x1, x0, rounds: u32) -> u32 { + switch rounds { + case 5: return F_5(x2, x5, x0, x6, x4, x3, x1) + case: assert(rounds != 5, "Rounds count not supported!") + } + return 0 +} + +FF_1 :: #force_inline proc(x7, x6, x5, x4, x3, x2, x1, x0, w, rounds: u32) -> u32 { + tmp := FPHI_1(x6, x5, x4, x3, x2, x1, x0, rounds) + x8 := util.ROTR32(tmp, 7) + util.ROTR32(x7, 11) + w + return x8 +} + +FF_2 :: #force_inline proc(x7, x6, x5, x4, x3, x2, x1, x0, w, c, rounds: u32) -> u32 { + tmp := FPHI_2(x6, x5, x4, x3, x2, x1, x0, rounds) + x8 := util.ROTR32(tmp, 7) + util.ROTR32(x7, 11) + w + c + return x8 +} + +FF_3 :: #force_inline proc(x7, x6, x5, x4, x3, x2, x1, x0, w, c, rounds: u32) -> u32 { + tmp := FPHI_3(x6, x5, x4, x3, x2, x1, x0, rounds) + x8 := util.ROTR32(tmp, 7) + util.ROTR32(x7, 11) + w + c + return x8 +} + +FF_4 :: #force_inline proc(x7, x6, x5, x4, x3, x2, x1, x0, w, c, rounds: u32) -> u32 { + tmp := FPHI_4(x6, x5, x4, x3, x2, x1, x0, rounds) + x8 := util.ROTR32(tmp, 7) + util.ROTR32(x7, 11) + w + c + return x8 +} + +FF_5 :: #force_inline proc(x7, x6, x5, x4, x3, x2, x1, x0, w, c, rounds: u32) -> u32 { + tmp := FPHI_5(x6, x5, x4, x3, x2, x1, x0, rounds) + x8 := util.ROTR32(tmp, 7) + util.ROTR32(x7, 11) + w + c + return x8 +} + +HAVAL_CH2UINT :: #force_inline proc (str: []byte, word: []u32) { + for _, i in word[:32] { + word[i] = u32(str[i*4+0]) << 0 | u32(str[i*4+1]) << 8 | u32(str[i*4+2]) << 16 | u32(str[i*4+3]) << 24 + } +} + +HAVAL_UINT2CH :: #force_inline proc(word: []u32, str: []byte, wlen: u32) { + for _, i in word[:wlen] { + str[i*4+0] = byte(word[i] >> 0) & 0xff + str[i*4+1] = byte(word[i] >> 8) & 0xff + str[i*4+2] = byte(word[i] >> 16) & 0xff + str[i*4+3] = byte(word[i] >> 24) & 0xff + } +} + +haval_block :: proc(ctx: ^Haval_Context, rounds: u32) { + t0, t1, t2, t3 := ctx.fingerprint[0], ctx.fingerprint[1], ctx.fingerprint[2], ctx.fingerprint[3] + t4, t5, t6, t7 := ctx.fingerprint[4], ctx.fingerprint[5], ctx.fingerprint[6], ctx.fingerprint[7] + w := ctx.block + + t7 = FF_1(t7, t6, t5, t4, t3, t2, t1, t0, w[ 0], rounds) + t6 = FF_1(t6, t5, t4, t3, t2, t1, t0, t7, w[ 1], rounds) + t5 = FF_1(t5, t4, t3, t2, t1, t0, t7, t6, w[ 2], rounds) + t4 = FF_1(t4, t3, t2, t1, t0, t7, t6, t5, w[ 3], rounds) + t3 = FF_1(t3, t2, t1, t0, t7, t6, t5, t4, w[ 4], rounds) + t2 = FF_1(t2, t1, t0, t7, t6, t5, t4, t3, w[ 5], rounds) + t1 = FF_1(t1, t0, t7, t6, t5, t4, t3, t2, w[ 6], rounds) + t0 = FF_1(t0, t7, t6, t5, t4, t3, t2, t1, w[ 7], rounds) + + t7 = FF_1(t7, t6, t5, t4, t3, t2, t1, t0, w[ 8], rounds) + t6 = FF_1(t6, t5, t4, t3, t2, t1, t0, t7, w[ 9], rounds) + t5 = FF_1(t5, t4, t3, t2, t1, t0, t7, t6, w[10], rounds) + t4 = FF_1(t4, t3, t2, t1, t0, t7, t6, t5, w[11], rounds) + t3 = FF_1(t3, t2, t1, t0, t7, t6, t5, t4, w[12], rounds) + t2 = FF_1(t2, t1, t0, t7, t6, t5, t4, t3, w[13], rounds) + t1 = FF_1(t1, t0, t7, t6, t5, t4, t3, t2, w[14], rounds) + t0 = FF_1(t0, t7, t6, t5, t4, t3, t2, t1, w[15], rounds) + + t7 = FF_1(t7, t6, t5, t4, t3, t2, t1, t0, w[16], rounds) + t6 = FF_1(t6, t5, t4, t3, t2, t1, t0, t7, w[17], rounds) + t5 = FF_1(t5, t4, t3, t2, t1, t0, t7, t6, w[18], rounds) + t4 = FF_1(t4, t3, t2, t1, t0, t7, t6, t5, w[19], rounds) + t3 = FF_1(t3, t2, t1, t0, t7, t6, t5, t4, w[20], rounds) + t2 = FF_1(t2, t1, t0, t7, t6, t5, t4, t3, w[21], rounds) + t1 = FF_1(t1, t0, t7, t6, t5, t4, t3, t2, w[22], rounds) + t0 = FF_1(t0, t7, t6, t5, t4, t3, t2, t1, w[23], rounds) + + t7 = FF_1(t7, t6, t5, t4, t3, t2, t1, t0, w[24], rounds) + t6 = FF_1(t6, t5, t4, t3, t2, t1, t0, t7, w[25], rounds) + t5 = FF_1(t5, t4, t3, t2, t1, t0, t7, t6, w[26], rounds) + t4 = FF_1(t4, t3, t2, t1, t0, t7, t6, t5, w[27], rounds) + t3 = FF_1(t3, t2, t1, t0, t7, t6, t5, t4, w[28], rounds) + t2 = FF_1(t2, t1, t0, t7, t6, t5, t4, t3, w[29], rounds) + t1 = FF_1(t1, t0, t7, t6, t5, t4, t3, t2, w[30], rounds) + t0 = FF_1(t0, t7, t6, t5, t4, t3, t2, t1, w[31], rounds) + + t7 = FF_2(t7, t6, t5, t4, t3, t2, t1, t0, w[ 5], 0x452821e6, rounds) + t6 = FF_2(t6, t5, t4, t3, t2, t1, t0, t7, w[14], 0x38d01377, rounds) + t5 = FF_2(t5, t4, t3, t2, t1, t0, t7, t6, w[26], 0xbe5466cf, rounds) + t4 = FF_2(t4, t3, t2, t1, t0, t7, t6, t5, w[18], 0x34e90c6c, rounds) + t3 = FF_2(t3, t2, t1, t0, t7, t6, t5, t4, w[11], 0xc0ac29b7, rounds) + t2 = FF_2(t2, t1, t0, t7, t6, t5, t4, t3, w[28], 0xc97c50dd, rounds) + t1 = FF_2(t1, t0, t7, t6, t5, t4, t3, t2, w[ 7], 0x3f84d5b5, rounds) + t0 = FF_2(t0, t7, t6, t5, t4, t3, t2, t1, w[16], 0xb5470917, rounds) + + t7 = FF_2(t7, t6, t5, t4, t3, t2, t1, t0, w[ 0], 0x9216d5d9, rounds) + t6 = FF_2(t6, t5, t4, t3, t2, t1, t0, t7, w[23], 0x8979fb1b, rounds) + t5 = FF_2(t5, t4, t3, t2, t1, t0, t7, t6, w[20], 0xd1310ba6, rounds) + t4 = FF_2(t4, t3, t2, t1, t0, t7, t6, t5, w[22], 0x98dfb5ac, rounds) + t3 = FF_2(t3, t2, t1, t0, t7, t6, t5, t4, w[ 1], 0x2ffd72db, rounds) + t2 = FF_2(t2, t1, t0, t7, t6, t5, t4, t3, w[10], 0xd01adfb7, rounds) + t1 = FF_2(t1, t0, t7, t6, t5, t4, t3, t2, w[ 4], 0xb8e1afed, rounds) + t0 = FF_2(t0, t7, t6, t5, t4, t3, t2, t1, w[ 8], 0x6a267e96, rounds) + + t7 = FF_2(t7, t6, t5, t4, t3, t2, t1, t0, w[30], 0xba7c9045, rounds) + t6 = FF_2(t6, t5, t4, t3, t2, t1, t0, t7, w[ 3], 0xf12c7f99, rounds) + t5 = FF_2(t5, t4, t3, t2, t1, t0, t7, t6, w[21], 0x24a19947, rounds) + t4 = FF_2(t4, t3, t2, t1, t0, t7, t6, t5, w[ 9], 0xb3916cf7, rounds) + t3 = FF_2(t3, t2, t1, t0, t7, t6, t5, t4, w[17], 0x0801f2e2, rounds) + t2 = FF_2(t2, t1, t0, t7, t6, t5, t4, t3, w[24], 0x858efc16, rounds) + t1 = FF_2(t1, t0, t7, t6, t5, t4, t3, t2, w[29], 0x636920d8, rounds) + t0 = FF_2(t0, t7, t6, t5, t4, t3, t2, t1, w[ 6], 0x71574e69, rounds) + + t7 = FF_2(t7, t6, t5, t4, t3, t2, t1, t0, w[19], 0xa458fea3, rounds) + t6 = FF_2(t6, t5, t4, t3, t2, t1, t0, t7, w[12], 0xf4933d7e, rounds) + t5 = FF_2(t5, t4, t3, t2, t1, t0, t7, t6, w[15], 0x0d95748f, rounds) + t4 = FF_2(t4, t3, t2, t1, t0, t7, t6, t5, w[13], 0x728eb658, rounds) + t3 = FF_2(t3, t2, t1, t0, t7, t6, t5, t4, w[ 2], 0x718bcd58, rounds) + t2 = FF_2(t2, t1, t0, t7, t6, t5, t4, t3, w[25], 0x82154aee, rounds) + t1 = FF_2(t1, t0, t7, t6, t5, t4, t3, t2, w[31], 0x7b54a41d, rounds) + t0 = FF_2(t0, t7, t6, t5, t4, t3, t2, t1, w[27], 0xc25a59b5, rounds) + + t7 = FF_3(t7, t6, t5, t4, t3, t2, t1, t0, w[19], 0x9c30d539, rounds) + t6 = FF_3(t6, t5, t4, t3, t2, t1, t0, t7, w[ 9], 0x2af26013, rounds) + t5 = FF_3(t5, t4, t3, t2, t1, t0, t7, t6, w[ 4], 0xc5d1b023, rounds) + t4 = FF_3(t4, t3, t2, t1, t0, t7, t6, t5, w[20], 0x286085f0, rounds) + t3 = FF_3(t3, t2, t1, t0, t7, t6, t5, t4, w[28], 0xca417918, rounds) + t2 = FF_3(t2, t1, t0, t7, t6, t5, t4, t3, w[17], 0xb8db38ef, rounds) + t1 = FF_3(t1, t0, t7, t6, t5, t4, t3, t2, w[ 8], 0x8e79dcb0, rounds) + t0 = FF_3(t0, t7, t6, t5, t4, t3, t2, t1, w[22], 0x603a180e, rounds) + + t7 = FF_3(t7, t6, t5, t4, t3, t2, t1, t0, w[29], 0x6c9e0e8b, rounds) + t6 = FF_3(t6, t5, t4, t3, t2, t1, t0, t7, w[14], 0xb01e8a3e, rounds) + t5 = FF_3(t5, t4, t3, t2, t1, t0, t7, t6, w[25], 0xd71577c1, rounds) + t4 = FF_3(t4, t3, t2, t1, t0, t7, t6, t5, w[12], 0xbd314b27, rounds) + t3 = FF_3(t3, t2, t1, t0, t7, t6, t5, t4, w[24], 0x78af2fda, rounds) + t2 = FF_3(t2, t1, t0, t7, t6, t5, t4, t3, w[30], 0x55605c60, rounds) + t1 = FF_3(t1, t0, t7, t6, t5, t4, t3, t2, w[16], 0xe65525f3, rounds) + t0 = FF_3(t0, t7, t6, t5, t4, t3, t2, t1, w[26], 0xaa55ab94, rounds) + + t7 = FF_3(t7, t6, t5, t4, t3, t2, t1, t0, w[31], 0x57489862, rounds) + t6 = FF_3(t6, t5, t4, t3, t2, t1, t0, t7, w[15], 0x63e81440, rounds) + t5 = FF_3(t5, t4, t3, t2, t1, t0, t7, t6, w[ 7], 0x55ca396a, rounds) + t4 = FF_3(t4, t3, t2, t1, t0, t7, t6, t5, w[ 3], 0x2aab10b6, rounds) + t3 = FF_3(t3, t2, t1, t0, t7, t6, t5, t4, w[ 1], 0xb4cc5c34, rounds) + t2 = FF_3(t2, t1, t0, t7, t6, t5, t4, t3, w[ 0], 0x1141e8ce, rounds) + t1 = FF_3(t1, t0, t7, t6, t5, t4, t3, t2, w[18], 0xa15486af, rounds) + t0 = FF_3(t0, t7, t6, t5, t4, t3, t2, t1, w[27], 0x7c72e993, rounds) + + t7 = FF_3(t7, t6, t5, t4, t3, t2, t1, t0, w[13], 0xb3ee1411, rounds) + t6 = FF_3(t6, t5, t4, t3, t2, t1, t0, t7, w[ 6], 0x636fbc2a, rounds) + t5 = FF_3(t5, t4, t3, t2, t1, t0, t7, t6, w[21], 0x2ba9c55d, rounds) + t4 = FF_3(t4, t3, t2, t1, t0, t7, t6, t5, w[10], 0x741831f6, rounds) + t3 = FF_3(t3, t2, t1, t0, t7, t6, t5, t4, w[23], 0xce5c3e16, rounds) + t2 = FF_3(t2, t1, t0, t7, t6, t5, t4, t3, w[11], 0x9b87931e, rounds) + t1 = FF_3(t1, t0, t7, t6, t5, t4, t3, t2, w[ 5], 0xafd6ba33, rounds) + t0 = FF_3(t0, t7, t6, t5, t4, t3, t2, t1, w[ 2], 0x6c24cf5c, rounds) + + if rounds >= 4 { + t7 = FF_4(t7, t6, t5, t4, t3, t2, t1, t0, w[24], 0x7a325381, rounds) + t6 = FF_4(t6, t5, t4, t3, t2, t1, t0, t7, w[ 4], 0x28958677, rounds) + t5 = FF_4(t5, t4, t3, t2, t1, t0, t7, t6, w[ 0], 0x3b8f4898, rounds) + t4 = FF_4(t4, t3, t2, t1, t0, t7, t6, t5, w[14], 0x6b4bb9af, rounds) + t3 = FF_4(t3, t2, t1, t0, t7, t6, t5, t4, w[ 2], 0xc4bfe81b, rounds) + t2 = FF_4(t2, t1, t0, t7, t6, t5, t4, t3, w[ 7], 0x66282193, rounds) + t1 = FF_4(t1, t0, t7, t6, t5, t4, t3, t2, w[28], 0x61d809cc, rounds) + t0 = FF_4(t0, t7, t6, t5, t4, t3, t2, t1, w[23], 0xfb21a991, rounds) + + t7 = FF_4(t7, t6, t5, t4, t3, t2, t1, t0, w[26], 0x487cac60, rounds) + t6 = FF_4(t6, t5, t4, t3, t2, t1, t0, t7, w[ 6], 0x5dec8032, rounds) + t5 = FF_4(t5, t4, t3, t2, t1, t0, t7, t6, w[30], 0xef845d5d, rounds) + t4 = FF_4(t4, t3, t2, t1, t0, t7, t6, t5, w[20], 0xe98575b1, rounds) + t3 = FF_4(t3, t2, t1, t0, t7, t6, t5, t4, w[18], 0xdc262302, rounds) + t2 = FF_4(t2, t1, t0, t7, t6, t5, t4, t3, w[25], 0xeb651b88, rounds) + t1 = FF_4(t1, t0, t7, t6, t5, t4, t3, t2, w[19], 0x23893e81, rounds) + t0 = FF_4(t0, t7, t6, t5, t4, t3, t2, t1, w[ 3], 0xd396acc5, rounds) + + t7 = FF_4(t7, t6, t5, t4, t3, t2, t1, t0, w[22], 0x0f6d6ff3, rounds) + t6 = FF_4(t6, t5, t4, t3, t2, t1, t0, t7, w[11], 0x83f44239, rounds) + t5 = FF_4(t5, t4, t3, t2, t1, t0, t7, t6, w[31], 0x2e0b4482, rounds) + t4 = FF_4(t4, t3, t2, t1, t0, t7, t6, t5, w[21], 0xa4842004, rounds) + t3 = FF_4(t3, t2, t1, t0, t7, t6, t5, t4, w[ 8], 0x69c8f04a, rounds) + t2 = FF_4(t2, t1, t0, t7, t6, t5, t4, t3, w[27], 0x9e1f9b5e, rounds) + t1 = FF_4(t1, t0, t7, t6, t5, t4, t3, t2, w[12], 0x21c66842, rounds) + t0 = FF_4(t0, t7, t6, t5, t4, t3, t2, t1, w[ 9], 0xf6e96c9a, rounds) + + t7 = FF_4(t7, t6, t5, t4, t3, t2, t1, t0, w[ 1], 0x670c9c61, rounds) + t6 = FF_4(t6, t5, t4, t3, t2, t1, t0, t7, w[29], 0xabd388f0, rounds) + t5 = FF_4(t5, t4, t3, t2, t1, t0, t7, t6, w[ 5], 0x6a51a0d2, rounds) + t4 = FF_4(t4, t3, t2, t1, t0, t7, t6, t5, w[15], 0xd8542f68, rounds) + t3 = FF_4(t3, t2, t1, t0, t7, t6, t5, t4, w[17], 0x960fa728, rounds) + t2 = FF_4(t2, t1, t0, t7, t6, t5, t4, t3, w[10], 0xab5133a3, rounds) + t1 = FF_4(t1, t0, t7, t6, t5, t4, t3, t2, w[16], 0x6eef0b6c, rounds) + t0 = FF_4(t0, t7, t6, t5, t4, t3, t2, t1, w[13], 0x137a3be4, rounds) + } + + if rounds == 5 { + t7 = FF_5(t7, t6, t5, t4, t3, t2, t1, t0, w[27], 0xba3bf050, rounds) + t6 = FF_5(t6, t5, t4, t3, t2, t1, t0, t7, w[ 3], 0x7efb2a98, rounds) + t5 = FF_5(t5, t4, t3, t2, t1, t0, t7, t6, w[21], 0xa1f1651d, rounds) + t4 = FF_5(t4, t3, t2, t1, t0, t7, t6, t5, w[26], 0x39af0176, rounds) + t3 = FF_5(t3, t2, t1, t0, t7, t6, t5, t4, w[17], 0x66ca593e, rounds) + t2 = FF_5(t2, t1, t0, t7, t6, t5, t4, t3, w[11], 0x82430e88, rounds) + t1 = FF_5(t1, t0, t7, t6, t5, t4, t3, t2, w[20], 0x8cee8619, rounds) + t0 = FF_5(t0, t7, t6, t5, t4, t3, t2, t1, w[29], 0x456f9fb4, rounds) + + t7 = FF_5(t7, t6, t5, t4, t3, t2, t1, t0, w[19], 0x7d84a5c3, rounds) + t6 = FF_5(t6, t5, t4, t3, t2, t1, t0, t7, w[ 0], 0x3b8b5ebe, rounds) + t5 = FF_5(t5, t4, t3, t2, t1, t0, t7, t6, w[12], 0xe06f75d8, rounds) + t4 = FF_5(t4, t3, t2, t1, t0, t7, t6, t5, w[ 7], 0x85c12073, rounds) + t3 = FF_5(t3, t2, t1, t0, t7, t6, t5, t4, w[13], 0x401a449f, rounds) + t2 = FF_5(t2, t1, t0, t7, t6, t5, t4, t3, w[ 8], 0x56c16aa6, rounds) + t1 = FF_5(t1, t0, t7, t6, t5, t4, t3, t2, w[31], 0x4ed3aa62, rounds) + t0 = FF_5(t0, t7, t6, t5, t4, t3, t2, t1, w[10], 0x363f7706, rounds) + + t7 = FF_5(t7, t6, t5, t4, t3, t2, t1, t0, w[ 5], 0x1bfedf72, rounds) + t6 = FF_5(t6, t5, t4, t3, t2, t1, t0, t7, w[ 9], 0x429b023d, rounds) + t5 = FF_5(t5, t4, t3, t2, t1, t0, t7, t6, w[14], 0x37d0d724, rounds) + t4 = FF_5(t4, t3, t2, t1, t0, t7, t6, t5, w[30], 0xd00a1248, rounds) + t3 = FF_5(t3, t2, t1, t0, t7, t6, t5, t4, w[18], 0xdb0fead3, rounds) + t2 = FF_5(t2, t1, t0, t7, t6, t5, t4, t3, w[ 6], 0x49f1c09b, rounds) + t1 = FF_5(t1, t0, t7, t6, t5, t4, t3, t2, w[28], 0x075372c9, rounds) + t0 = FF_5(t0, t7, t6, t5, t4, t3, t2, t1, w[24], 0x80991b7b, rounds) + + t7 = FF_5(t7, t6, t5, t4, t3, t2, t1, t0, w[ 2], 0x25d479d8, rounds) + t6 = FF_5(t6, t5, t4, t3, t2, t1, t0, t7, w[23], 0xf6e8def7, rounds) + t5 = FF_5(t5, t4, t3, t2, t1, t0, t7, t6, w[16], 0xe3fe501a, rounds) + t4 = FF_5(t4, t3, t2, t1, t0, t7, t6, t5, w[22], 0xb6794c3b, rounds) + t3 = FF_5(t3, t2, t1, t0, t7, t6, t5, t4, w[ 4], 0x976ce0bd, rounds) + t2 = FF_5(t2, t1, t0, t7, t6, t5, t4, t3, w[ 1], 0x04c006ba, rounds) + t1 = FF_5(t1, t0, t7, t6, t5, t4, t3, t2, w[25], 0xc1a94fb6, rounds) + t0 = FF_5(t0, t7, t6, t5, t4, t3, t2, t1, w[15], 0x409f60c4, rounds) + } + + ctx.fingerprint[0] += t0 + ctx.fingerprint[1] += t1 + ctx.fingerprint[2] += t2 + ctx.fingerprint[3] += t3 + ctx.fingerprint[4] += t4 + ctx.fingerprint[5] += t5 + ctx.fingerprint[6] += t6 + ctx.fingerprint[7] += t7 +} + +haval_tailor :: proc(ctx: ^Haval_Context, size: u32) { + temp: u32 + switch size { + case 128: + temp = (ctx.fingerprint[7] & 0x000000ff) | + (ctx.fingerprint[6] & 0xff000000) | + (ctx.fingerprint[5] & 0x00ff0000) | + (ctx.fingerprint[4] & 0x0000ff00) + ctx.fingerprint[0] += util.ROTR32(temp, 8) + + temp = (ctx.fingerprint[7] & 0x0000ff00) | + (ctx.fingerprint[6] & 0x000000ff) | + (ctx.fingerprint[5] & 0xff000000) | + (ctx.fingerprint[4] & 0x00ff0000) + ctx.fingerprint[1] += util.ROTR32(temp, 16) + + temp = (ctx.fingerprint[7] & 0x00ff0000) | + (ctx.fingerprint[6] & 0x0000ff00) | + (ctx.fingerprint[5] & 0x000000ff) | + (ctx.fingerprint[4] & 0xff000000) + ctx.fingerprint[2] += util.ROTR32(temp, 24) + + temp = (ctx.fingerprint[7] & 0xff000000) | + (ctx.fingerprint[6] & 0x00ff0000) | + (ctx.fingerprint[5] & 0x0000ff00) | + (ctx.fingerprint[4] & 0x000000ff) + ctx.fingerprint[3] += temp + case 160: + temp = (ctx.fingerprint[7] & u32(0x3f)) | + (ctx.fingerprint[6] & u32(0x7f << 25)) | + (ctx.fingerprint[5] & u32(0x3f << 19)) + ctx.fingerprint[0] += util.ROTR32(temp, 19) + + temp = (ctx.fingerprint[7] & u32(0x3f << 6)) | + (ctx.fingerprint[6] & u32(0x3f)) | + (ctx.fingerprint[5] & u32(0x7f << 25)) + ctx.fingerprint[1] += util.ROTR32(temp, 25) + + temp = (ctx.fingerprint[7] & u32(0x7f << 12)) | + (ctx.fingerprint[6] & u32(0x3f << 6)) | + (ctx.fingerprint[5] & u32(0x3f)) + ctx.fingerprint[2] += temp + + temp = (ctx.fingerprint[7] & u32(0x3f << 19)) | + (ctx.fingerprint[6] & u32(0x7f << 12)) | + (ctx.fingerprint[5] & u32(0x3f << 6)) + ctx.fingerprint[3] += temp >> 6 + + temp = (ctx.fingerprint[7] & u32(0x7f << 25)) | + (ctx.fingerprint[6] & u32(0x3f << 19)) | + (ctx.fingerprint[5] & u32(0x7f << 12)) + ctx.fingerprint[4] += temp >> 12 + case 192: + temp = (ctx.fingerprint[7] & u32(0x1f)) | + (ctx.fingerprint[6] & u32(0x3f << 26)) + ctx.fingerprint[0] += util.ROTR32(temp, 26) + + temp = (ctx.fingerprint[7] & u32(0x1f << 5)) | + (ctx.fingerprint[6] & u32(0x1f)) + ctx.fingerprint[1] += temp + + temp = (ctx.fingerprint[7] & u32(0x3f << 10)) | + (ctx.fingerprint[6] & u32(0x1f << 5)) + ctx.fingerprint[2] += temp >> 5 + + temp = (ctx.fingerprint[7] & u32(0x1f << 16)) | + (ctx.fingerprint[6] & u32(0x3f << 10)) + ctx.fingerprint[3] += temp >> 10 + + temp = (ctx.fingerprint[7] & u32(0x1f << 21)) | + (ctx.fingerprint[6] & u32(0x1f << 16)) + ctx.fingerprint[4] += temp >> 16 + + temp = (ctx.fingerprint[7] & u32(0x3f << 26)) | + (ctx.fingerprint[6] & u32(0x1f << 21)) + ctx.fingerprint[5] += temp >> 21 + case 224: + ctx.fingerprint[0] += (ctx.fingerprint[7] >> 27) & 0x1f + ctx.fingerprint[1] += (ctx.fingerprint[7] >> 22) & 0x1f + ctx.fingerprint[2] += (ctx.fingerprint[7] >> 18) & 0x0f + ctx.fingerprint[3] += (ctx.fingerprint[7] >> 13) & 0x1f + ctx.fingerprint[4] += (ctx.fingerprint[7] >> 9) & 0x0f + ctx.fingerprint[5] += (ctx.fingerprint[7] >> 4) & 0x1f + ctx.fingerprint[6] += ctx.fingerprint[7] & 0x0f + } +} + +init_odin :: proc(ctx: ^Haval_Context) { + ctx.fingerprint[0] = 0x243f6a88 + ctx.fingerprint[1] = 0x85a308d3 + ctx.fingerprint[2] = 0x13198a2e + ctx.fingerprint[3] = 0x03707344 + ctx.fingerprint[4] = 0xa4093822 + ctx.fingerprint[5] = 0x299f31d0 + ctx.fingerprint[6] = 0x082efa98 + ctx.fingerprint[7] = 0xec4e6c89 +} + +update_odin :: proc(ctx: ^Haval_Context, data: []byte) { + i: u32 + rmd_len := u32((ctx.count[0] >> 3) & 0x7f) + fill_len := 128 - rmd_len + str_len := ctx.str_len + + ctx.count[0] += str_len << 3 + if ctx.count[0] < (str_len << 3) { + ctx.count[1] += 1 + } + ctx.count[1] += str_len >> 29 + + when ODIN_ENDIAN == "little" { + if rmd_len + str_len >= 128 { + copy(util.slice_to_bytes(ctx.block[:])[rmd_len:], data[:fill_len]) + haval_block(ctx, ctx.rounds) + for i = fill_len; i + 127 < str_len; i += 128 { + copy(util.slice_to_bytes(ctx.block[:]), data[i:128]) + haval_block(ctx, ctx.rounds) + } + rmd_len = 0 + } else { + i = 0 + } + copy(util.slice_to_bytes(ctx.block[:])[rmd_len:], data[i:]) + } else { + if rmd_len + str_len >= 128 { + copy(ctx.remainder[rmd_len:], data[:fill_len]) + HAVAL_CH2UINT(ctx.remainder[:], ctx.block[:]) + haval_block(ctx, ctx.rounds) + for i = fill_len; i + 127 < str_len; i += 128 { + copy(ctx.remainder[:], data[i:128]) + HAVAL_CH2UINT(ctx.remainder[:], ctx.block[:]) + haval_block(ctx, ctx.rounds) + } + rmd_len = 0 + } else { + i = 0 + } + copy(ctx.remainder[rmd_len:], data[i:]) + } +} + +final_odin :: proc(ctx: ^Haval_Context, hash: []byte) { + pad_len: u32 + tail: [10]byte + + tail[0] = byte(ctx.hashbitlen & 0x3) << 6 | byte(ctx.rounds & 0x7) << 3 | (HAVAL_VERSION & 0x7) + tail[1] = byte(ctx.hashbitlen >> 2) & 0xff + + HAVAL_UINT2CH(ctx.count[:], util.slice_to_bytes(tail[2:]), 2) + rmd_len := (ctx.count[0] >> 3) & 0x7f + if rmd_len < 118 { + pad_len = 118 - rmd_len + } else { + pad_len = 246 - rmd_len + } + + ctx.str_len = pad_len + update_odin(ctx, PADDING[:]) + ctx.str_len = 10 + update_odin(ctx, tail[:]) + haval_tailor(ctx, ctx.hashbitlen) + HAVAL_UINT2CH(ctx.fingerprint[:], hash, ctx.hashbitlen >> 5) + + mem.set(ctx, 0, size_of(ctx)) +} diff --git a/core/crypto/jh/jh.odin b/core/crypto/jh/jh.odin new file mode 100644 index 000000000..725411d42 --- /dev/null +++ b/core/crypto/jh/jh.odin @@ -0,0 +1,673 @@ +package jh + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the JH hashing algorithm, as defined in +*/ + +import "core:os" +import "core:io" + +import "../_ctx" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_28 = hash_bytes_odin_28 + ctx.hash_file_28 = hash_file_odin_28 + ctx.hash_stream_28 = hash_stream_odin_28 + ctx.hash_bytes_32 = hash_bytes_odin_32 + ctx.hash_file_32 = hash_file_odin_32 + ctx.hash_stream_32 = hash_stream_odin_32 + ctx.hash_bytes_48 = hash_bytes_odin_48 + ctx.hash_file_48 = hash_file_odin_48 + ctx.hash_stream_48 = hash_stream_odin_48 + ctx.hash_bytes_64 = hash_bytes_odin_64 + ctx.hash_file_64 = hash_file_odin_64 + ctx.hash_stream_64 = hash_stream_odin_64 + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan does nothing, since JH is not available in Botan +@(warning="JH is not provided by the Botan API. Odin implementation will be used") +use_botan :: #force_inline proc() { + use_odin() +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +@(private) +_create_jh_ctx :: #force_inline proc(size: _ctx.Hash_Size) { + ctx: Jh_Context + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = size + #partial switch size { + case ._28: ctx.hashbitlen = 224 + case ._32: ctx.hashbitlen = 256 + case ._48: ctx.hashbitlen = 384 + case ._64: ctx.hashbitlen = 512 + } +} + +/* + High level API +*/ + +// hash_string_224 will hash the given input and return the +// computed hash +hash_string_224 :: proc(data: string) -> [28]byte { + return hash_bytes_224(transmute([]byte)(data)) +} + +// hash_bytes_224 will hash the given input and return the +// computed hash +hash_bytes_224 :: proc(data: []byte) -> [28]byte { + _create_jh_ctx(._28) + return _hash_impl->hash_bytes_28(data) +} + +// hash_stream_224 will read the stream in chunks and compute a +// hash from its contents +hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) { + _create_jh_ctx(._28) + return _hash_impl->hash_stream_28(s) +} + +// hash_file_224 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_224 :: proc(path: string, load_at_once: bool) -> ([28]byte, bool) { + _create_jh_ctx(._28) + return _hash_impl->hash_file_28(path, load_at_once) +} + +hash_224 :: proc { + hash_stream_224, + hash_file_224, + hash_bytes_224, + hash_string_224, +} + +// hash_string_256 will hash the given input and return the +// computed hash +hash_string_256 :: proc(data: string) -> [32]byte { + return hash_bytes_256(transmute([]byte)(data)) +} + +// hash_bytes_256 will hash the given input and return the +// computed hash +hash_bytes_256 :: proc(data: []byte) -> [32]byte { + _create_jh_ctx(._32) + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream_256 will read the stream in chunks and compute a +// hash from its contents +hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_jh_ctx(._32) + return _hash_impl->hash_stream_32(s) +} + +// hash_file_256 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_256 :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_jh_ctx(._32) + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash_256 :: proc { + hash_stream_256, + hash_file_256, + hash_bytes_256, + hash_string_256, +} + +// hash_string_384 will hash the given input and return the +// computed hash +hash_string_384 :: proc(data: string) -> [48]byte { + return hash_bytes_384(transmute([]byte)(data)) +} + +// hash_bytes_384 will hash the given input and return the +// computed hash +hash_bytes_384 :: proc(data: []byte) -> [48]byte { + _create_jh_ctx(._48) + return _hash_impl->hash_bytes_48(data) +} + +// hash_stream_384 will read the stream in chunks and compute a +// hash from its contents +hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) { + _create_jh_ctx(._48) + return _hash_impl->hash_stream_48(s) +} + +// hash_file_384 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_384 :: proc(path: string, load_at_once: bool) -> ([48]byte, bool) { + _create_jh_ctx(._48) + return _hash_impl->hash_file_48(path, load_at_once) +} + +hash_384 :: proc { + hash_stream_384, + hash_file_384, + hash_bytes_384, + hash_string_384, +} + +// hash_string_512 will hash the given input and return the +// computed hash +hash_string_512 :: proc(data: string) -> [64]byte { + return hash_bytes_512(transmute([]byte)(data)) +} + +// hash_bytes_512 will hash the given input and return the +// computed hash +hash_bytes_512 :: proc(data: []byte) -> [64]byte { + _create_jh_ctx(._64) + return _hash_impl->hash_bytes_64(data) +} + +// hash_stream_512 will read the stream in chunks and compute a +// hash from its contents +hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) { + _create_jh_ctx(._64) + return _hash_impl->hash_stream_64(s) +} + +// hash_file_512 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_512 :: proc(path: string, load_at_once: bool) -> ([64]byte, bool) { + _create_jh_ctx(._64) + return _hash_impl->hash_file_64(path, load_at_once) +} + +hash_512 :: proc { + hash_stream_512, + hash_file_512, + hash_bytes_512, + hash_string_512, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte { + hash: [28]byte + if c, ok := ctx.internal_ctx.(Jh_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) { + hash: [28]byte + if c, ok := ctx.internal_ctx.(Jh_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([28]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_28(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_28(ctx, buf[:]), read_ok + } + } + } + return [28]byte{}, false +} + +hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Jh_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Jh_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_32(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_32(ctx, buf[:]), read_ok + } + } + } + return [32]byte{}, false +} + +hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte { + hash: [48]byte + if c, ok := ctx.internal_ctx.(Jh_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) { + hash: [48]byte + if c, ok := ctx.internal_ctx.(Jh_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([48]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_48(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_48(ctx, buf[:]), read_ok + } + } + } + return [48]byte{}, false +} + +hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte { + hash: [64]byte + if c, ok := ctx.internal_ctx.(Jh_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) { + hash: [64]byte + if c, ok := ctx.internal_ctx.(Jh_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([64]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_64(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_64(ctx, buf[:]), read_ok + } + } + } + return [64]byte{}, false +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + _create_jh_ctx(ctx.hash_size) + if c, ok := ctx.internal_ctx.(Jh_Context); ok { + init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(Jh_Context); ok { + update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(Jh_Context); ok { + final_odin(&c, hash) + } +} + +/* + JH implementation +*/ + +JH_ROUNDCONSTANT_ZERO := [64]byte { + 0x6, 0xa, 0x0, 0x9, 0xe, 0x6, 0x6, 0x7, + 0xf, 0x3, 0xb, 0xc, 0xc, 0x9, 0x0, 0x8, + 0xb, 0x2, 0xf, 0xb, 0x1, 0x3, 0x6, 0x6, + 0xe, 0xa, 0x9, 0x5, 0x7, 0xd, 0x3, 0xe, + 0x3, 0xa, 0xd, 0xe, 0xc, 0x1, 0x7, 0x5, + 0x1, 0x2, 0x7, 0x7, 0x5, 0x0, 0x9, 0x9, + 0xd, 0xa, 0x2, 0xf, 0x5, 0x9, 0x0, 0xb, + 0x0, 0x6, 0x6, 0x7, 0x3, 0x2, 0x2, 0xa, +} + +JH_S := [2][16]byte { + {9, 0, 4, 11, 13, 12, 3, 15, 1, 10, 2, 6, 7, 5, 8, 14}, + {3, 12, 6, 13, 5, 7, 1, 9, 15, 2, 0, 4, 11, 10, 14, 8}, +} + +Jh_Context :: struct { + hashbitlen: int, + databitlen: u64, + buffer_size: u64, + H: [128]byte, + A: [256]byte, + roundconstant: [64]byte, + buffer: [64]byte, +} + +JH_E8_finaldegroup :: proc(ctx: ^Jh_Context) { + t0,t1,t2,t3: byte + tem: [256]byte + for i := 0; i < 128; i += 1 { + tem[i] = ctx.A[i << 1] + tem[i + 128] = ctx.A[(i << 1) + 1] + } + for i := 0; i < 128; i += 1 { + ctx.H[i] = 0 + } + for i := 0; i < 256; i += 1 { + t0 = (tem[i] >> 3) & 1 + t1 = (tem[i] >> 2) & 1 + t2 = (tem[i] >> 1) & 1 + t3 = (tem[i] >> 0) & 1 + + ctx.H[uint(i) >> 3] |= t0 << (7 - (uint(i) & 7)) + ctx.H[(uint(i) + 256) >> 3] |= t1 << (7 - (uint(i) & 7)) + ctx.H[(uint(i) + 512) >> 3] |= t2 << (7 - (uint(i) & 7)) + ctx.H[(uint(i) + 768) >> 3] |= t3 << (7 - (uint(i) & 7)) + } +} + +jh_update_roundconstant :: proc(ctx: ^Jh_Context) { + tem: [64]byte + t: byte + for i := 0; i < 64; i += 1 { + tem[i] = JH_S[0][ctx.roundconstant[i]] + } + for i := 0; i < 64; i += 2 { + tem[i + 1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf + tem[i] ~= ((tem[i + 1] << 1) ~ (tem[i + 1] >> 3) ~ ((tem[i + 1] >> 2) & 2)) & 0xf + } + for i := 0; i < 64; i += 4 { + t = tem[i + 2] + tem[i + 2] = tem[i + 3] + tem[i + 3] = t + } + for i := 0; i < 32; i += 1 { + ctx.roundconstant[i] = tem[i << 1] + ctx.roundconstant[i + 32] = tem[(i << 1) + 1] + } + for i := 32; i < 64; i += 2 { + t = ctx.roundconstant[i] + ctx.roundconstant[i] = ctx.roundconstant[i + 1] + ctx.roundconstant[i + 1] = t + } +} + +JH_R8 :: proc(ctx: ^Jh_Context) { + t: byte + tem, roundconstant_expanded: [256]byte + for i := u32(0); i < 256; i += 1 { + roundconstant_expanded[i] = (ctx.roundconstant[i >> 2] >> (3 - (i & 3)) ) & 1 + } + for i := 0; i < 256; i += 1 { + tem[i] = JH_S[roundconstant_expanded[i]][ctx.A[i]] + } + for i := 0; i < 256; i += 2 { + tem[i+1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf + tem[i] ~= ((tem[i + 1] << 1) ~ (tem[i + 1] >> 3) ~ ((tem[i + 1] >> 2) & 2)) & 0xf + } + for i := 0; i < 256; i += 4 { + t = tem[i + 2] + tem[i+2] = tem[i + 3] + tem[i+3] = t + } + for i := 0; i < 128; i += 1 { + ctx.A[i] = tem[i << 1] + ctx.A[i + 128] = tem[(i << 1) + 1] + } + for i := 128; i < 256; i += 2 { + t = ctx.A[i] + ctx.A[i] = ctx.A[i + 1] + ctx.A[i + 1] = t + } +} + +JH_E8_initialgroup :: proc(ctx: ^Jh_Context) { + t0, t1, t2, t3: byte + tem: [256]byte + for i := u32(0); i < 256; i += 1 { + t0 = (ctx.H[i >> 3] >> (7 - (i & 7))) & 1 + t1 = (ctx.H[(i + 256) >> 3] >> (7 - (i & 7))) & 1 + t2 = (ctx.H[(i + 512) >> 3] >> (7 - (i & 7))) & 1 + t3 = (ctx.H[(i + 768) >> 3] >> (7 - (i & 7))) & 1 + tem[i] = (t0 << 3) | (t1 << 2) | (t2 << 1) | (t3 << 0) + } + for i := 0; i < 128; i += 1 { + ctx.A[i << 1] = tem[i] + ctx.A[(i << 1) + 1] = tem[i + 128] + } +} + +JH_E8 :: proc(ctx: ^Jh_Context) { + for i := 0; i < 64; i += 1 { + ctx.roundconstant[i] = JH_ROUNDCONSTANT_ZERO[i] + } + JH_E8_initialgroup(ctx) + for i := 0; i < 42; i += 1 { + JH_R8(ctx) + jh_update_roundconstant(ctx) + } + JH_E8_finaldegroup(ctx) +} + +JH_F8 :: proc(ctx: ^Jh_Context) { + for i := 0; i < 64; i += 1 { + ctx.H[i] ~= ctx.buffer[i] + } + JH_E8(ctx) + for i := 0; i < 64; i += 1 { + ctx.H[i + 64] ~= ctx.buffer[i] + } +} + +init_odin :: proc(ctx: ^Jh_Context) { + ctx.H[1] = byte(ctx.hashbitlen) & 0xff + ctx.H[0] = byte(ctx.hashbitlen >> 8) & 0xff + JH_F8(ctx) +} + +update_odin :: proc(ctx: ^Jh_Context, data: []byte) { + databitlen := u64(len(data)) * 8 + ctx.databitlen += databitlen + i := u64(0) + + if (ctx.buffer_size > 0) && ((ctx.buffer_size + databitlen) < 512) { + if (databitlen & 7) == 0 { + copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)]) + } else { + copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3) + 1]) + } + ctx.buffer_size += databitlen + databitlen = 0 + } + + if (ctx.buffer_size > 0 ) && ((ctx.buffer_size + databitlen) >= 512) { + copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)]) + i = 64 - (ctx.buffer_size >> 3) + databitlen = databitlen - (512 - ctx.buffer_size) + JH_F8(ctx) + ctx.buffer_size = 0 + } + + for databitlen >= 512 { + copy(ctx.buffer[:], data[i:i + 64]) + JH_F8(ctx) + i += 64 + databitlen -= 512 + } + + if databitlen > 0 { + if (databitlen & 7) == 0 { + copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3)]) + } else { + copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3) + 1]) + } + ctx.buffer_size = databitlen + } +} + +final_odin :: proc(ctx: ^Jh_Context, hash: []byte) { + if ctx.databitlen & 0x1ff == 0 { + for i := 0; i < 64; i += 1 { + ctx.buffer[i] = 0 + } + ctx.buffer[0] = 0x80 + ctx.buffer[63] = byte(ctx.databitlen) & 0xff + ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff + ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff + ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff + ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff + ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff + ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff + ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff + JH_F8(ctx) + } else { + if ctx.buffer_size & 7 == 0 { + for i := (ctx.databitlen & 0x1ff) >> 3; i < 64; i += 1 { + ctx.buffer[i] = 0 + } + } else { + for i := ((ctx.databitlen & 0x1ff) >> 3) + 1; i < 64; i += 1 { + ctx.buffer[i] = 0 + } + } + ctx.buffer[(ctx.databitlen & 0x1ff) >> 3] |= 1 << (7 - (ctx.databitlen & 7)) + JH_F8(ctx) + for i := 0; i < 64; i += 1 { + ctx.buffer[i] = 0 + } + ctx.buffer[63] = byte(ctx.databitlen) & 0xff + ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff + ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff + ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff + ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff + ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff + ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff + ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff + JH_F8(ctx) + } + switch ctx.hashbitlen { + case 224: copy(hash[:], ctx.H[100:128]) + case 256: copy(hash[:], ctx.H[96:128]) + case 384: copy(hash[:], ctx.H[80:128]) + case 512: copy(hash[:], ctx.H[64:128]) + } +} diff --git a/core/crypto/keccak/keccak.odin b/core/crypto/keccak/keccak.odin new file mode 100644 index 000000000..a4e5a227a --- /dev/null +++ b/core/crypto/keccak/keccak.odin @@ -0,0 +1,441 @@ +package keccak + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Interface for the Keccak hashing algorithm. + This is done because the padding in the SHA3 standard was changed by the NIST, resulting in a different output. +*/ + +import "core:os" +import "core:io" + +import "../botan" +import "../_ctx" +import "../_sha3" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_28 = hash_bytes_odin_28 + ctx.hash_file_28 = hash_file_odin_28 + ctx.hash_stream_28 = hash_stream_odin_28 + ctx.hash_bytes_32 = hash_bytes_odin_32 + ctx.hash_file_32 = hash_file_odin_32 + ctx.hash_stream_32 = hash_stream_odin_32 + ctx.hash_bytes_48 = hash_bytes_odin_48 + ctx.hash_file_48 = hash_file_odin_48 + ctx.hash_stream_48 = hash_stream_odin_48 + ctx.hash_bytes_64 = hash_bytes_odin_64 + ctx.hash_file_64 = hash_file_odin_64 + ctx.hash_stream_64 = hash_stream_odin_64 + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan assigns the internal vtable of the hash context to use the Botan bindings +use_botan :: #force_inline proc() { + botan.assign_hash_vtable(_hash_impl, botan.HASH_KECCAK) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string_224 will hash the given input and return the +// computed hash +hash_string_224 :: proc(data: string) -> [28]byte { + return hash_bytes_224(transmute([]byte)(data)) +} + +// hash_bytes_224 will hash the given input and return the +// computed hash +hash_bytes_224 :: proc(data: []byte) -> [28]byte { + _create_sha3_ctx(28) + return _hash_impl->hash_bytes_28(data) +} + +// hash_stream_224 will read the stream in chunks and compute a +// hash from its contents +hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) { + _create_sha3_ctx(28) + return _hash_impl->hash_stream_28(s) +} + +// hash_file_224 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_224 :: proc(path: string, load_at_once: bool) -> ([28]byte, bool) { + _create_sha3_ctx(28) + return _hash_impl->hash_file_28(path, load_at_once) +} + +hash_224 :: proc { + hash_stream_224, + hash_file_224, + hash_bytes_224, + hash_string_224, +} + +// hash_string_256 will hash the given input and return the +// computed hash +hash_string_256 :: proc(data: string) -> [32]byte { + return hash_bytes_256(transmute([]byte)(data)) +} + +// hash_bytes_256 will hash the given input and return the +// computed hash +hash_bytes_256 :: proc(data: []byte) -> [32]byte { + _create_sha3_ctx(32) + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream_256 will read the stream in chunks and compute a +// hash from its contents +hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_sha3_ctx(32) + return _hash_impl->hash_stream_32(s) +} + +// hash_file_256 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_256 :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_sha3_ctx(32) + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash_256 :: proc { + hash_stream_256, + hash_file_256, + hash_bytes_256, + hash_string_256, +} + +// hash_string_384 will hash the given input and return the +// computed hash +hash_string_384 :: proc(data: string) -> [48]byte { + return hash_bytes_384(transmute([]byte)(data)) +} + +// hash_bytes_384 will hash the given input and return the +// computed hash +hash_bytes_384 :: proc(data: []byte) -> [48]byte { + _create_sha3_ctx(48) + return _hash_impl->hash_bytes_48(data) +} + +// hash_stream_384 will read the stream in chunks and compute a +// hash from its contents +hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) { + _create_sha3_ctx(48) + return _hash_impl->hash_stream_48(s) +} + +// hash_file_384 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_384 :: proc(path: string, load_at_once: bool) -> ([48]byte, bool) { + _create_sha3_ctx(48) + return _hash_impl->hash_file_48(path, load_at_once) +} + +hash_384 :: proc { + hash_stream_384, + hash_file_384, + hash_bytes_384, + hash_string_384, +} + +// hash_string_512 will hash the given input and return the +// computed hash +hash_string_512 :: proc(data: string) -> [64]byte { + return hash_bytes_512(transmute([]byte)(data)) +} + +// hash_bytes_512 will hash the given input and return the +// computed hash +hash_bytes_512 :: proc(data: []byte) -> [64]byte { + _create_sha3_ctx(64) + return _hash_impl->hash_bytes_64(data) +} + +// hash_stream_512 will read the stream in chunks and compute a +// hash from its contents +hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) { + _create_sha3_ctx(64) + return _hash_impl->hash_stream_64(s) +} + +// hash_file_512 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_512 :: proc(path: string, load_at_once: bool) -> ([64]byte, bool) { + _create_sha3_ctx(64) + return _hash_impl->hash_file_64(path, load_at_once) +} + +hash_512 :: proc { + hash_stream_512, + hash_file_512, + hash_bytes_512, + hash_string_512, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte { + hash: [28]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + _sha3.update_odin(&c, data) + _sha3.final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) { + hash: [28]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _sha3.update_odin(&c, buf[:read]) + } + } + _sha3.final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([28]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_28(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_28(ctx, buf[:]), read_ok + } + } + } + return [28]byte{}, false +} + +hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { + hash: [32]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + _sha3.update_odin(&c, data) + _sha3.final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { + hash: [32]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _sha3.update_odin(&c, buf[:read]) + } + } + _sha3.final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_32(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_32(ctx, buf[:]), read_ok + } + } + } + return [32]byte{}, false +} + +hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte { + hash: [48]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + _sha3.update_odin(&c, data) + _sha3.final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) { + hash: [48]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _sha3.update_odin(&c, buf[:read]) + } + } + _sha3.final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([48]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_48(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_48(ctx, buf[:]), read_ok + } + } + } + return [48]byte{}, false +} + +hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte { + hash: [64]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + _sha3.update_odin(&c, data) + _sha3.final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) { + hash: [64]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _sha3.update_odin(&c, buf[:read]) + } + } + _sha3.final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([64]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_64(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_64(ctx, buf[:]), read_ok + } + } + } + return [64]byte{}, false +} + +@(private) +_create_sha3_ctx :: #force_inline proc(mdlen: int) { + ctx: _sha3.Sha3_Context + ctx.mdlen = mdlen + ctx.is_keccak = true + _hash_impl.internal_ctx = ctx + switch mdlen { + case 28: _hash_impl.hash_size = ._28 + case 32: _hash_impl.hash_size = ._32 + case 48: _hash_impl.hash_size = ._48 + case 64: _hash_impl.hash_size = ._64 + } +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + #partial switch ctx.hash_size { + case ._28: _create_sha3_ctx(28) + case ._32: _create_sha3_ctx(32) + case ._48: _create_sha3_ctx(48) + case ._64: _create_sha3_ctx(64) + } + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.final_odin(&c, hash) + } +} diff --git a/core/crypto/md2/md2.odin b/core/crypto/md2/md2.odin new file mode 100644 index 000000000..a5ae0251c --- /dev/null +++ b/core/crypto/md2/md2.odin @@ -0,0 +1,265 @@ +package md2 + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the MD2 hashing algorithm, as defined in RFC 1319 +*/ + +import "core:os" +import "core:io" + +import "../_ctx" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_16 = hash_bytes_odin + ctx.hash_file_16 = hash_file_odin + ctx.hash_stream_16 = hash_stream_odin + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan does nothing, since MD2 is not available in Botan +@(warning="MD2 is not provided by the Botan API. Odin implementation will be used") +use_botan :: #force_inline proc() { + use_odin() +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string will hash the given input and return the +// computed hash +hash_string :: proc(data: string) -> [16]byte { + return hash_bytes(transmute([]byte)(data)) +} + +// hash_bytes will hash the given input and return the +// computed hash +hash_bytes :: proc(data: []byte) -> [16]byte { + _create_md2_ctx() + return _hash_impl->hash_bytes_16(data) +} + +// hash_stream will read the stream in chunks and compute a +// hash from its contents +hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) { + _create_md2_ctx() + return _hash_impl->hash_stream_16(s) +} + +// hash_file will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file :: proc(path: string, load_at_once: bool) -> ([16]byte, bool) { + _create_md2_ctx() + return _hash_impl->hash_file_16(path, load_at_once) +} + +hash :: proc { + hash_stream, + hash_file, + hash_bytes, + hash_string, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte { + hash: [16]byte + if c, ok := ctx.internal_ctx.(Md2_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) { + hash: [16]byte + if c, ok := ctx.internal_ctx.(Md2_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([16]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin(ctx, buf[:]), read_ok + } + } + } + return [16]byte{}, false +} + +@(private) +_create_md2_ctx :: #force_inline proc() { + ctx: Md2_Context + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = ._16 +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + _create_md2_ctx() + if c, ok := ctx.internal_ctx.(Md2_Context); ok { + init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(Md2_Context); ok { + update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(Md2_Context); ok { + final_odin(&c, hash) + } +} + +/* + MD2 implementation +*/ + +Md2_Context :: struct { + data: [16]byte, + state: [16 * 3]byte, + checksum: [16]byte, + datalen: int, +} + +PI_TABLE := [?]byte { + 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, + 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, 76, + 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, 138, + 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, 245, 142, + 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, 148, 194, 16, + 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, 39, 53, 62, + 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, 181, 209, 215, + 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, 150, 164, 125, 182, + 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, 112, 89, 100, 113, 135, + 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, 96, 37, 173, 174, 176, + 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, 85, 71, 163, 35, 221, + 81, 175, 58, 195, 92, 249, 206, 186, 197, 234, 38, 44, 83, 13, 110, + 133, 40, 132, 9, 211, 223, 205, 244, 65, 129, 77, 82, 106, 220, 55, + 200, 108, 193, 171, 250, 36, 225, 123, 8, 12, 189, 177, 74, 120, 136, + 149, 139, 227, 99, 232, 109, 233, 203, 213, 254, 59, 0, 29, 57, 242, + 239, 183, 14, 102, 88, 208, 228, 166, 119, 114, 248, 235, 117, 75, 10, + 49, 68, 80, 180, 143, 237, 31, 26, 219, 153, 141, 51, 159, 17, 131, + 20, +} + +transform :: proc(ctx: ^Md2_Context, data: []byte) { + j,k,t: byte + for j = 0; j < 16; j += 1 { + ctx.state[j + 16] = data[j] + ctx.state[j + 16 * 2] = (ctx.state[j + 16] ~ ctx.state[j]) + } + t = 0 + for j = 0; j < 16 + 2; j += 1 { + for k = 0; k < 16 * 3; k += 1 { + ctx.state[k] ~= PI_TABLE[t] + t = ctx.state[k] + } + t = (t + j) & 0xff + } + t = ctx.checksum[16 - 1] + for j = 0; j < 16; j += 1 { + ctx.checksum[j] ~= PI_TABLE[data[j] ~ t] + t = ctx.checksum[j] + } +} + +init_odin :: proc(ctx: ^Md2_Context) { + // No action needed here +} + +update_odin :: proc(ctx: ^Md2_Context, data: []byte) { + for i := 0; i < len(data); i += 1 { + ctx.data[ctx.datalen] = data[i] + ctx.datalen += 1 + if (ctx.datalen == 16) { + transform(ctx, ctx.data[:]) + ctx.datalen = 0 + } + } +} + +final_odin :: proc(ctx: ^Md2_Context, hash: []byte) { + to_pad := byte(16 - ctx.datalen) + for ctx.datalen < 16 { + ctx.data[ctx.datalen] = to_pad + ctx.datalen += 1 + } + transform(ctx, ctx.data[:]) + transform(ctx, ctx.checksum[:]) + for i := 0; i < 16; i += 1 { + hash[i] = ctx.state[i] + } +} \ No newline at end of file diff --git a/core/crypto/md4/md4.odin b/core/crypto/md4/md4.odin new file mode 100644 index 000000000..b25ad82c7 --- /dev/null +++ b/core/crypto/md4/md4.odin @@ -0,0 +1,345 @@ +package md4 + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the MD4 hashing algorithm, as defined in RFC 1320 +*/ + +import "core:mem" +import "core:os" +import "core:io" + +import "../util" +import "../botan" +import "../_ctx" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_16 = hash_bytes_odin + ctx.hash_file_16 = hash_file_odin + ctx.hash_stream_16 = hash_stream_odin + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan assigns the internal vtable of the hash context to use the Botan bindings +use_botan :: #force_inline proc() { + botan.assign_hash_vtable(_hash_impl, botan.HASH_MD4) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string will hash the given input and return the +// computed hash +hash_string :: proc(data: string) -> [16]byte { + return hash_bytes(transmute([]byte)(data)) +} + +// hash_bytes will hash the given input and return the +// computed hash +hash_bytes :: proc(data: []byte) -> [16]byte { + _create_md4_ctx() + return _hash_impl->hash_bytes_16(data) +} + +// hash_stream will read the stream in chunks and compute a +// hash from its contents +hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) { + _create_md4_ctx() + return _hash_impl->hash_stream_16(s) +} + +// hash_file will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file :: proc(path: string, load_at_once: bool) -> ([16]byte, bool) { + _create_md4_ctx() + return _hash_impl->hash_file_16(path, load_at_once) +} + +hash :: proc { + hash_stream, + hash_file, + hash_bytes, + hash_string, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte { + hash: [16]byte + if c, ok := ctx.internal_ctx.(Md4_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) { + hash: [16]byte + if c, ok := ctx.internal_ctx.(Md4_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([16]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin(ctx, buf[:]), read_ok + } + } + } + return [16]byte{}, false +} + +@(private) +_create_md4_ctx :: #force_inline proc() { + ctx: Md4_Context + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = ._16 +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + _create_md4_ctx() + if c, ok := ctx.internal_ctx.(Md4_Context); ok { + init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(Md4_Context); ok { + update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(Md4_Context); ok { + final_odin(&c, hash) + } +} + +/* + MD4 implementation +*/ + +BLOCK_SIZE :: 64 + +Md4_Context :: struct { + data: [64]byte, + state: [4]u32, + bitlen: u64, + datalen: u32, +} + +/* + @note(zh): F, G and H, as mentioned in the RFC, have been inlined into FF, GG + and HH respectively, instead of declaring them separately. +*/ + +FF :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 { + return util.ROTL32(a + ((b & c) | (~b & d)) + x, s) +} + +GG :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 { + return util.ROTL32(a + ((b & c) | (b & d) | (c & d)) + x + 0x5a827999, s) +} + +HH :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 { + return util.ROTL32(a + (b ~ c ~ d) + x + 0x6ed9eba1, s) +} + +transform :: proc(ctx: ^Md4_Context, data: []byte) { + a, b, c, d, i, j: u32 + m: [16]u32 + + for i, j = 0, 0; i < 16; i += 1 { + m[i] = u32(data[j]) | (u32(data[j + 1]) << 8) | (u32(data[j + 2]) << 16) | (u32(data[j + 3]) << 24) + j += 4 + } + + a = ctx.state[0] + b = ctx.state[1] + c = ctx.state[2] + d = ctx.state[3] + + a = FF(a, b, c, d, m[0], 3) + d = FF(d, a, b, c, m[1], 7) + c = FF(c, d, a, b, m[2], 11) + b = FF(b, c, d, a, m[3], 19) + a = FF(a, b, c, d, m[4], 3) + d = FF(d, a, b, c, m[5], 7) + c = FF(c, d, a, b, m[6], 11) + b = FF(b, c, d, a, m[7], 19) + a = FF(a, b, c, d, m[8], 3) + d = FF(d, a, b, c, m[9], 7) + c = FF(c, d, a, b, m[10], 11) + b = FF(b, c, d, a, m[11], 19) + a = FF(a, b, c, d, m[12], 3) + d = FF(d, a, b, c, m[13], 7) + c = FF(c, d, a, b, m[14], 11) + b = FF(b, c, d, a, m[15], 19) + + a = GG(a, b, c, d, m[0], 3) + d = GG(d, a, b, c, m[4], 5) + c = GG(c, d, a, b, m[8], 9) + b = GG(b, c, d, a, m[12], 13) + a = GG(a, b, c, d, m[1], 3) + d = GG(d, a, b, c, m[5], 5) + c = GG(c, d, a, b, m[9], 9) + b = GG(b, c, d, a, m[13], 13) + a = GG(a, b, c, d, m[2], 3) + d = GG(d, a, b, c, m[6], 5) + c = GG(c, d, a, b, m[10], 9) + b = GG(b, c, d, a, m[14], 13) + a = GG(a, b, c, d, m[3], 3) + d = GG(d, a, b, c, m[7], 5) + c = GG(c, d, a, b, m[11], 9) + b = GG(b, c, d, a, m[15], 13) + + a = HH(a, b, c, d, m[0], 3) + d = HH(d, a, b, c, m[8], 9) + c = HH(c, d, a, b, m[4], 11) + b = HH(b, c, d, a, m[12], 15) + a = HH(a, b, c, d, m[2], 3) + d = HH(d, a, b, c, m[10], 9) + c = HH(c, d, a, b, m[6], 11) + b = HH(b, c, d, a, m[14], 15) + a = HH(a, b, c, d, m[1], 3) + d = HH(d, a, b, c, m[9], 9) + c = HH(c, d, a, b, m[5], 11) + b = HH(b, c, d, a, m[13], 15) + a = HH(a, b, c, d, m[3], 3) + d = HH(d, a, b, c, m[11], 9) + c = HH(c, d, a, b, m[7], 11) + b = HH(b, c, d, a, m[15], 15) + + ctx.state[0] += a + ctx.state[1] += b + ctx.state[2] += c + ctx.state[3] += d +} + +init_odin :: proc(ctx: ^Md4_Context) { + ctx.state[0] = 0x67452301 + ctx.state[1] = 0xefcdab89 + ctx.state[2] = 0x98badcfe + ctx.state[3] = 0x10325476 +} + +update_odin :: proc(ctx: ^Md4_Context, data: []byte) { + for i := 0; i < len(data); i += 1 { + ctx.data[ctx.datalen] = data[i] + ctx.datalen += 1 + if(ctx.datalen == BLOCK_SIZE) { + transform(ctx, ctx.data[:]) + ctx.bitlen += 512 + ctx.datalen = 0 + } + } +} + +final_odin :: proc(ctx: ^Md4_Context, hash: []byte) { + i := ctx.datalen + if ctx.datalen < 56 { + ctx.data[i] = 0x80 + i += 1 + for i < 56 { + ctx.data[i] = 0x00 + i += 1 + } + } else if ctx.datalen >= 56 { + ctx.data[i] = 0x80 + i += 1 + for i < BLOCK_SIZE { + ctx.data[i] = 0x00 + i += 1 + } + transform(ctx, ctx.data[:]) + mem.set(&ctx.data, 0, 56) + } + + ctx.bitlen += u64(ctx.datalen * 8) + ctx.data[56] = byte(ctx.bitlen) + ctx.data[57] = byte(ctx.bitlen >> 8) + ctx.data[58] = byte(ctx.bitlen >> 16) + ctx.data[59] = byte(ctx.bitlen >> 24) + ctx.data[60] = byte(ctx.bitlen >> 32) + ctx.data[61] = byte(ctx.bitlen >> 40) + ctx.data[62] = byte(ctx.bitlen >> 48) + ctx.data[63] = byte(ctx.bitlen >> 56) + transform(ctx, ctx.data[:]) + + for i = 0; i < 4; i += 1 { + hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff + hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff + hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff + hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff + } +} diff --git a/core/crypto/md5/md5.odin b/core/crypto/md5/md5.odin new file mode 100644 index 000000000..037bb17f1 --- /dev/null +++ b/core/crypto/md5/md5.odin @@ -0,0 +1,368 @@ +package md5 + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the MD5 hashing algorithm, as defined in RFC 1321 +*/ + +import "core:mem" +import "core:os" +import "core:io" + +import "../util" +import "../botan" +import "../_ctx" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_16 = hash_bytes_odin + ctx.hash_file_16 = hash_file_odin + ctx.hash_stream_16 = hash_stream_odin + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan assigns the internal vtable of the hash context to use the Botan bindings +use_botan :: #force_inline proc() { + botan.assign_hash_vtable(_hash_impl, botan.HASH_MD5) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string will hash the given input and return the +// computed hash +hash_string :: proc(data: string) -> [16]byte { + return hash_bytes(transmute([]byte)(data)) +} + +// hash_bytes will hash the given input and return the +// computed hash +hash_bytes :: proc(data: []byte) -> [16]byte { + _create_md5_ctx() + return _hash_impl->hash_bytes_16(data) +} + +// hash_stream will read the stream in chunks and compute a +// hash from its contents +hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) { + _create_md5_ctx() + return _hash_impl->hash_stream_16(s) +} + +// hash_file will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file :: proc(path: string, load_at_once: bool) -> ([16]byte, bool) { + _create_md5_ctx() + return _hash_impl->hash_file_16(path, load_at_once) +} + +hash :: proc { + hash_stream, + hash_file, + hash_bytes, + hash_string, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte { + hash: [16]byte + if c, ok := ctx.internal_ctx.(Md5_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) { + hash: [16]byte + if c, ok := ctx.internal_ctx.(Md5_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([16]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin(ctx, buf[:]), read_ok + } + } + } + return [16]byte{}, false +} + +@(private) +_create_md5_ctx :: #force_inline proc() { + ctx: Md5_Context + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = ._16 +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + _create_md5_ctx() + if c, ok := ctx.internal_ctx.(Md5_Context); ok { + init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(Md5_Context); ok { + update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(Md5_Context); ok { + final_odin(&c, hash) + } +} + +/* + MD4 implementation +*/ + +BLOCK_SIZE :: 64 + +Md5_Context :: struct { + data: [BLOCK_SIZE]byte, + state: [4]u32, + bitlen: u64, + datalen: u32, +} + +/* + @note(zh): F, G, H and I, as mentioned in the RFC, have been inlined into FF, GG, HH + and II respectively, instead of declaring them separately. +*/ + +FF :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 { + return b + util.ROTL32(a + ((b & c) | (~b & d)) + m + t, s) +} + +GG :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 { + return b + util.ROTL32(a + ((b & d) | (c & ~d)) + m + t, s) +} + +HH :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 { + return b + util.ROTL32(a + (b ~ c ~ d) + m + t, s) +} + +II :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 { + return b + util.ROTL32(a + (c ~ (b | ~d)) + m + t, s) +} + +transform :: proc(ctx: ^Md5_Context, data: []byte) { + i, j: u32 + m: [16]u32 + + for i, j = 0, 0; i < 16; i+=1 { + m[i] = u32(data[j]) + u32(data[j + 1]) << 8 + u32(data[j + 2]) << 16 + u32(data[j + 3]) << 24 + j += 4 + } + + a := ctx.state[0] + b := ctx.state[1] + c := ctx.state[2] + d := ctx.state[3] + + a = FF(a, b, c, d, m[0], 7, 0xd76aa478) + d = FF(d, a, b, c, m[1], 12, 0xe8c7b756) + c = FF(c, d, a, b, m[2], 17, 0x242070db) + b = FF(b, c, d, a, m[3], 22, 0xc1bdceee) + a = FF(a, b, c, d, m[4], 7, 0xf57c0faf) + d = FF(d, a, b, c, m[5], 12, 0x4787c62a) + c = FF(c, d, a, b, m[6], 17, 0xa8304613) + b = FF(b, c, d, a, m[7], 22, 0xfd469501) + a = FF(a, b, c, d, m[8], 7, 0x698098d8) + d = FF(d, a, b, c, m[9], 12, 0x8b44f7af) + c = FF(c, d, a, b, m[10], 17, 0xffff5bb1) + b = FF(b, c, d, a, m[11], 22, 0x895cd7be) + a = FF(a, b, c, d, m[12], 7, 0x6b901122) + d = FF(d, a, b, c, m[13], 12, 0xfd987193) + c = FF(c, d, a, b, m[14], 17, 0xa679438e) + b = FF(b, c, d, a, m[15], 22, 0x49b40821) + + a = GG(a, b, c, d, m[1], 5, 0xf61e2562) + d = GG(d, a, b, c, m[6], 9, 0xc040b340) + c = GG(c, d, a, b, m[11], 14, 0x265e5a51) + b = GG(b, c, d, a, m[0], 20, 0xe9b6c7aa) + a = GG(a, b, c, d, m[5], 5, 0xd62f105d) + d = GG(d, a, b, c, m[10], 9, 0x02441453) + c = GG(c, d, a, b, m[15], 14, 0xd8a1e681) + b = GG(b, c, d, a, m[4], 20, 0xe7d3fbc8) + a = GG(a, b, c, d, m[9], 5, 0x21e1cde6) + d = GG(d, a, b, c, m[14], 9, 0xc33707d6) + c = GG(c, d, a, b, m[3], 14, 0xf4d50d87) + b = GG(b, c, d, a, m[8], 20, 0x455a14ed) + a = GG(a, b, c, d, m[13], 5, 0xa9e3e905) + d = GG(d, a, b, c, m[2], 9, 0xfcefa3f8) + c = GG(c, d, a, b, m[7], 14, 0x676f02d9) + b = GG(b, c, d, a, m[12], 20, 0x8d2a4c8a) + + a = HH(a, b, c, d, m[5], 4, 0xfffa3942) + d = HH(d, a, b, c, m[8], 11, 0x8771f681) + c = HH(c, d, a, b, m[11], 16, 0x6d9d6122) + b = HH(b, c, d, a, m[14], 23, 0xfde5380c) + a = HH(a, b, c, d, m[1], 4, 0xa4beea44) + d = HH(d, a, b, c, m[4], 11, 0x4bdecfa9) + c = HH(c, d, a, b, m[7], 16, 0xf6bb4b60) + b = HH(b, c, d, a, m[10], 23, 0xbebfbc70) + a = HH(a, b, c, d, m[13], 4, 0x289b7ec6) + d = HH(d, a, b, c, m[0], 11, 0xeaa127fa) + c = HH(c, d, a, b, m[3], 16, 0xd4ef3085) + b = HH(b, c, d, a, m[6], 23, 0x04881d05) + a = HH(a, b, c, d, m[9], 4, 0xd9d4d039) + d = HH(d, a, b, c, m[12], 11, 0xe6db99e5) + c = HH(c, d, a, b, m[15], 16, 0x1fa27cf8) + b = HH(b, c, d, a, m[2], 23, 0xc4ac5665) + + a = II(a, b, c, d, m[0], 6, 0xf4292244) + d = II(d, a, b, c, m[7], 10, 0x432aff97) + c = II(c, d, a, b, m[14], 15, 0xab9423a7) + b = II(b, c, d, a, m[5], 21, 0xfc93a039) + a = II(a, b, c, d, m[12], 6, 0x655b59c3) + d = II(d, a, b, c, m[3], 10, 0x8f0ccc92) + c = II(c, d, a, b, m[10], 15, 0xffeff47d) + b = II(b, c, d, a, m[1], 21, 0x85845dd1) + a = II(a, b, c, d, m[8], 6, 0x6fa87e4f) + d = II(d, a, b, c, m[15], 10, 0xfe2ce6e0) + c = II(c, d, a, b, m[6], 15, 0xa3014314) + b = II(b, c, d, a, m[13], 21, 0x4e0811a1) + a = II(a, b, c, d, m[4], 6, 0xf7537e82) + d = II(d, a, b, c, m[11], 10, 0xbd3af235) + c = II(c, d, a, b, m[2], 15, 0x2ad7d2bb) + b = II(b, c, d, a, m[9], 21, 0xeb86d391) + + ctx.state[0] += a + ctx.state[1] += b + ctx.state[2] += c + ctx.state[3] += d +} + +init_odin :: proc(ctx: ^Md5_Context) { + ctx.state[0] = 0x67452301 + ctx.state[1] = 0xefcdab89 + ctx.state[2] = 0x98badcfe + ctx.state[3] = 0x10325476 +} + +update_odin :: proc(ctx: ^Md5_Context, data: []byte) { + for i := 0; i < len(data); i += 1 { + ctx.data[ctx.datalen] = data[i] + ctx.datalen += 1 + if(ctx.datalen == BLOCK_SIZE) { + transform(ctx, ctx.data[:]) + ctx.bitlen += 512 + ctx.datalen = 0 + } + } +} + +final_odin :: proc(ctx: ^Md5_Context, hash: []byte){ + i : u32 + i = ctx.datalen + + if ctx.datalen < 56 { + ctx.data[i] = 0x80 + i += 1 + for i < 56 { + ctx.data[i] = 0x00 + i += 1 + } + } else if ctx.datalen >= 56 { + ctx.data[i] = 0x80 + i += 1 + for i < BLOCK_SIZE { + ctx.data[i] = 0x00 + i += 1 + } + transform(ctx, ctx.data[:]) + mem.set(&ctx.data, 0, 56) + } + + ctx.bitlen += u64(ctx.datalen * 8) + ctx.data[56] = byte(ctx.bitlen) + ctx.data[57] = byte(ctx.bitlen >> 8) + ctx.data[58] = byte(ctx.bitlen >> 16) + ctx.data[59] = byte(ctx.bitlen >> 24) + ctx.data[60] = byte(ctx.bitlen >> 32) + ctx.data[61] = byte(ctx.bitlen >> 40) + ctx.data[62] = byte(ctx.bitlen >> 48) + ctx.data[63] = byte(ctx.bitlen >> 56) + transform(ctx, ctx.data[:]) + + for i = 0; i < 4; i += 1 { + hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff + hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff + hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff + hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff + } +} \ No newline at end of file diff --git a/core/crypto/ripemd/ripemd.odin b/core/crypto/ripemd/ripemd.odin new file mode 100644 index 000000000..2ba2f0884 --- /dev/null +++ b/core/crypto/ripemd/ripemd.odin @@ -0,0 +1,1060 @@ +package ripemd + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation for the RIPEMD hashing algorithm as defined in +*/ + +import "core:os" +import "core:io" + +import "../util" +import "../botan" +import "../_ctx" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_16 = hash_bytes_odin_16 + ctx.hash_file_16 = hash_file_odin_16 + ctx.hash_stream_16 = hash_stream_odin_16 + ctx.hash_bytes_20 = hash_bytes_odin_20 + ctx.hash_file_20 = hash_file_odin_20 + ctx.hash_stream_20 = hash_stream_odin_20 + ctx.hash_bytes_32 = hash_bytes_odin_32 + ctx.hash_file_32 = hash_file_odin_32 + ctx.hash_stream_32 = hash_stream_odin_32 + ctx.hash_bytes_40 = hash_bytes_odin_40 + ctx.hash_file_40 = hash_file_odin_40 + ctx.hash_stream_40 = hash_stream_odin_40 + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan assigns the internal vtable of the hash context to use the Botan bindings +use_botan :: #force_inline proc() { + botan.assign_hash_vtable(_hash_impl, botan.HASH_RIPEMD_160) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string_128 will hash the given input and return the +// computed hash +hash_string_128 :: proc(data: string) -> [16]byte { + return hash_bytes_128(transmute([]byte)(data)) +} + +// hash_bytes_128 will hash the given input and return the +// computed hash +hash_bytes_128 :: proc(data: []byte) -> [16]byte { + _create_ripemd_ctx(16) + return _hash_impl->hash_bytes_16(data) +} + +// hash_stream_128 will read the stream in chunks and compute a +// hash from its contents +hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) { + _create_ripemd_ctx(16) + return _hash_impl->hash_stream_16(s) +} + +// hash_file_128 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_128 :: proc(path: string, load_at_once: bool) -> ([16]byte, bool) { + _create_ripemd_ctx(16) + return _hash_impl->hash_file_16(path, load_at_once) +} + +hash_128 :: proc { + hash_stream_128, + hash_file_128, + hash_bytes_128, + hash_string_128, +} + +// hash_string_160 will hash the given input and return the +// computed hash +hash_string_160 :: proc(data: string) -> [20]byte { + return hash_bytes_160(transmute([]byte)(data)) +} + +// hash_bytes_160 will hash the given input and return the +// computed hash +hash_bytes_160 :: proc(data: []byte) -> [20]byte { + _create_ripemd_ctx(20) + return _hash_impl->hash_bytes_20(data) +} + +// hash_stream_160 will read the stream in chunks and compute a +// hash from its contents +hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) { + _create_ripemd_ctx(20) + return _hash_impl->hash_stream_20(s) +} + +// hash_file_160 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_160 :: proc(path: string, load_at_once: bool) -> ([20]byte, bool) { + _create_ripemd_ctx(20) + return _hash_impl->hash_file_20(path, load_at_once) +} + +hash_160 :: proc { + hash_stream_160, + hash_file_160, + hash_bytes_160, + hash_string_160, +} + +// hash_string_256 will hash the given input and return the +// computed hash +hash_string_256 :: proc(data: string) -> [32]byte { + return hash_bytes_256(transmute([]byte)(data)) +} + +// hash_bytes_256 will hash the given input and return the +// computed hash +hash_bytes_256 :: proc(data: []byte) -> [32]byte { + _create_ripemd_ctx(32) + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream_256 will read the stream in chunks and compute a +// hash from its contents +hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_ripemd_ctx(32) + return _hash_impl->hash_stream_32(s) +} + +// hash_file_256 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_256 :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_ripemd_ctx(32) + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash_256 :: proc { + hash_stream_256, + hash_file_256, + hash_bytes_256, + hash_string_256, +} + +// hash_string_320 will hash the given input and return the +// computed hash +hash_string_320 :: proc(data: string) -> [40]byte { + return hash_bytes_320(transmute([]byte)(data)) +} + +// hash_bytes_320 will hash the given input and return the +// computed hash +hash_bytes_320 :: proc(data: []byte) -> [40]byte { + _create_ripemd_ctx(40) + return _hash_impl->hash_bytes_40(data) +} + +// hash_stream_320 will read the stream in chunks and compute a +// hash from its contents +hash_stream_320 :: proc(s: io.Stream) -> ([40]byte, bool) { + _create_ripemd_ctx(40) + return _hash_impl->hash_stream_40(s) +} + +// hash_file_320 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_320 :: proc(path: string, load_at_once: bool) -> ([40]byte, bool) { + _create_ripemd_ctx(40) + return _hash_impl->hash_file_40(path, load_at_once) +} + +hash_320 :: proc { + hash_stream_320, + hash_file_320, + hash_bytes_320, + hash_string_320, +} + +hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte { + hash: [16]byte + if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) { + hash: [16]byte + if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([16]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_16(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_16(ctx, buf[:]), read_ok + } + } + } + return [16]byte{}, false +} + +hash_bytes_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte { + hash: [20]byte + if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) { + hash: [20]byte + if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([20]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_20(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_20(ctx, buf[:]), read_ok + } + } + } + return [20]byte{}, false +} + +hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_32(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_32(ctx, buf[:]), read_ok + } + } + } + return [32]byte{}, false +} + +hash_bytes_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [40]byte { + hash: [40]byte + if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([40]byte, bool) { + hash: [40]byte + if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([40]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_40(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_40(ctx, buf[:]), read_ok + } + } + } + return [40]byte{}, false +} + +@(private) +_create_ripemd_ctx :: #force_inline proc(hash_size: int) { + switch hash_size { + case 16: + ctx: Ripemd128_Context + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = ._16 + case 20: + ctx: Ripemd160_Context + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = ._20 + case 32: + ctx: Ripemd256_Context + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = ._32 + case 40: + ctx: Ripemd320_Context + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = ._40 + } +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + #partial switch ctx.hash_size { + case ._16: + _create_ripemd_ctx(16) + if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok { + init_odin(&c) + } + case ._20: + _create_ripemd_ctx(20) + if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok { + init_odin(&c) + } + case ._32: + _create_ripemd_ctx(32) + if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok { + init_odin(&c) + } + case ._40: + _create_ripemd_ctx(40) + if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok { + init_odin(&c) + } + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + #partial switch ctx.hash_size { + case ._16: + if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok { + update_odin(&c, data) + } + case ._20: + if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok { + update_odin(&c, data) + } + case ._32: + if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok { + update_odin(&c, data) + } + case ._40: + if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok { + update_odin(&c, data) + } + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + #partial switch ctx.hash_size { + case ._16: + if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok { + final_odin(&c, hash) + } + case ._20: + if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok { + final_odin(&c, hash) + } + case ._32: + if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok { + final_odin(&c, hash) + } + case ._40: + if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok { + final_odin(&c, hash) + } + } +} + +/* + RIPEMD implementation +*/ + +Ripemd128_Context :: struct { + s: [4]u32, + x: [RIPEMD_128_BLOCK_SIZE]byte, + nx: int, + tc: u64, +} + +Ripemd160_Context :: struct { + s: [5]u32, + x: [RIPEMD_160_BLOCK_SIZE]byte, + nx: int, + tc: u64, +} + +Ripemd256_Context :: struct { + s: [8]u32, + x: [RIPEMD_256_BLOCK_SIZE]byte, + nx: int, + tc: u64, +} + +Ripemd320_Context :: struct { + s: [10]u32, + x: [RIPEMD_320_BLOCK_SIZE]byte, + nx: int, + tc: u64, +} + +RIPEMD_128_SIZE :: 16 +RIPEMD_128_BLOCK_SIZE :: 64 +RIPEMD_160_SIZE :: 20 +RIPEMD_160_BLOCK_SIZE :: 64 +RIPEMD_256_SIZE :: 32 +RIPEMD_256_BLOCK_SIZE :: 64 +RIPEMD_320_SIZE :: 40 +RIPEMD_320_BLOCK_SIZE :: 64 + +S0 :: 0x67452301 +S1 :: 0xefcdab89 +S2 :: 0x98badcfe +S3 :: 0x10325476 +S4 :: 0xc3d2e1f0 +S5 :: 0x76543210 +S6 :: 0xfedcba98 +S7 :: 0x89abcdef +S8 :: 0x01234567 +S9 :: 0x3c2d1e0f + +RIPEMD_128_N0 := [64]uint { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, + 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, + 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, +} + +RIPEMD_128_R0 := [64]uint { + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, + 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, + 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, + 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, +} + +RIPEMD_128_N1 := [64]uint { + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, + 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, + 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, + 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, +} + +RIPEMD_128_R1 := [64]uint { + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, + 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, + 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, + 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, +} + +RIPEMD_160_N0 := [80]uint { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, + 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, + 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, + 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13, +} + +RIPEMD_160_R0 := [80]uint { + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, + 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, + 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, + 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, + 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6, +} + +RIPEMD_160_N1 := [80]uint { + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, + 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, + 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, + 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, + 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11, +} + +RIPEMD_160_R1 := [80]uint { + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, + 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, + 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, + 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, + 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11, +} + +init_odin :: proc(ctx: ^$T) { + when T == Ripemd128_Context { + ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3 + } else when T == Ripemd160_Context { + ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4 + } else when T == Ripemd256_Context { + ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3 + ctx.s[4], ctx.s[5], ctx.s[6], ctx.s[7] = S5, S6, S7, S8 + } else when T == Ripemd320_Context { + ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4 + ctx.s[5], ctx.s[6], ctx.s[7], ctx.s[8], ctx.s[9] = S5, S6, S7, S8, S9 + } +} + +block :: #force_inline proc (ctx: ^$T, p: []byte) -> int { + when T == Ripemd128_Context { + return ripemd_128_block(ctx, p) + } + else when T == Ripemd160_Context { + return ripemd_160_block(ctx, p) + } + else when T == Ripemd256_Context { + return ripemd_256_block(ctx, p) + } + else when T == Ripemd320_Context { + return ripemd_320_block(ctx, p) + } +} + +ripemd_128_block :: proc(ctx: ^$T, p: []byte) -> int { + n := 0 + x: [16]u32 = --- + alpha: u32 = --- + p := p + for len(p) >= RIPEMD_128_BLOCK_SIZE { + a, b, c, d := ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] + aa, bb, cc, dd := a, b, c, d + for i,j := 0, 0; i < 16; i, j = i+1, j+4 { + x[i] = u32(p[j]) | u32(p[j+1])<<8 | u32(p[j+2])<<16 | u32(p[j+3])<<24 + } + i := 0 + for i < 16 { + alpha = a + (b ~ c ~ d) + x[RIPEMD_128_N0[i]] + s := int(RIPEMD_128_R0[i]) + alpha = util.ROTL32(alpha, s) + a, b, c, d = d, alpha, b, c + alpha = aa + (bb & dd | cc &~ dd) + x[RIPEMD_128_N1[i]] + 0x50a28be6 + s = int(RIPEMD_128_R1[i]) + alpha = util.ROTL32(alpha, s) + aa, bb, cc, dd= dd, alpha, bb, cc + i += 1 + } + for i < 32 { + alpha = a + (d ~ (b & (c~d))) + x[RIPEMD_128_N0[i]] + 0x5a827999 + s := int(RIPEMD_128_R0[i]) + alpha = util.ROTL32(alpha, s) + a, b, c, d = d, alpha, b, c + alpha = aa + (dd ~ (bb | ~cc)) + x[RIPEMD_128_N1[i]] + 0x5c4dd124 + s = int(RIPEMD_128_R1[i]) + alpha = util.ROTL32(alpha, s) + aa, bb, cc, dd = dd, alpha, bb, cc + i += 1 + } + for i < 48 { + alpha = a + (d ~ (b | ~c)) + x[RIPEMD_128_N0[i]] + 0x6ed9eba1 + s := int(RIPEMD_128_R0[i]) + alpha = util.ROTL32(alpha, s) + a, b, c, d = d, alpha, b, c + alpha = aa + (dd ~ (bb & (cc~dd))) + x[RIPEMD_128_N1[i]] + 0x6d703ef3 + s = int(RIPEMD_128_R1[i]) + alpha = util.ROTL32(alpha, s) + aa, bb, cc, dd = dd, alpha, bb, cc + i += 1 + } + for i < 64 { + alpha = a + (c ~ (d & (b~c))) + x[RIPEMD_128_N0[i]] + 0x8f1bbcdc + s := int(RIPEMD_128_R0[i]) + alpha = util.ROTL32(alpha, s) + a, b, c, d = d, alpha, b, c + alpha = aa + (bb ~ cc ~ dd) + x[RIPEMD_128_N1[i]] + s = int(RIPEMD_128_R1[i]) + alpha = util.ROTL32(alpha, s) + aa, bb, cc, dd = dd, alpha, bb, cc + i += 1 + } + c = ctx.s[1] + c + dd + ctx.s[1] = ctx.s[2] + d + aa + ctx.s[2] = ctx.s[3] + a + bb + ctx.s[3] = ctx.s[0] + b + cc + ctx.s[0] = c + p = p[RIPEMD_128_BLOCK_SIZE:] + n += RIPEMD_128_BLOCK_SIZE + } + return n +} + +ripemd_160_block :: proc(ctx: ^$T, p: []byte) -> int { + n := 0 + x: [16]u32 = --- + alpha, beta: u32 = ---, --- + p := p + for len(p) >= RIPEMD_160_BLOCK_SIZE { + a, b, c, d, e := ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] + aa, bb, cc, dd, ee := a, b, c, d, e + for i,j := 0, 0; i < 16; i, j = i+1, j+4 { + x[i] = u32(p[j]) | u32(p[j+1])<<8 | u32(p[j+2])<<16 | u32(p[j+3])<<24 + } + i := 0 + for i < 16 { + alpha = a + (b ~ c ~ d) + x[RIPEMD_160_N0[i]] + s := int(RIPEMD_160_R0[i]) + alpha = util.ROTL32(alpha, s) + e + beta = util.ROTL32(c, 10) + a, b, c, d, e = e, alpha, b, beta, d + alpha = aa + (bb ~ (cc | ~dd)) + x[RIPEMD_160_N1[i]] + 0x50a28be6 + s = int(RIPEMD_160_R1[i]) + alpha = util.ROTL32(alpha, s) + ee + beta = util.ROTL32(cc, 10) + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + i += 1 + } + for i < 32 { + alpha = a + (b&c | ~b&d) + x[RIPEMD_160_N0[i]] + 0x5a827999 + s := int(RIPEMD_160_R0[i]) + alpha = util.ROTL32(alpha, s) + e + beta = util.ROTL32(c, 10) + a, b, c, d, e = e, alpha, b, beta, d + alpha = aa + (bb&dd | cc&~dd) + x[RIPEMD_160_N1[i]] + 0x5c4dd124 + s = int(RIPEMD_160_R1[i]) + alpha = util.ROTL32(alpha, s) + ee + beta = util.ROTL32(cc, 10) + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + i += 1 + } + for i < 48 { + alpha = a + (b | ~c ~ d) + x[RIPEMD_160_N0[i]] + 0x6ed9eba1 + s := int(RIPEMD_160_R0[i]) + alpha = util.ROTL32(alpha, s) + e + beta = util.ROTL32(c, 10) + a, b, c, d, e = e, alpha, b, beta, d + alpha = aa + (bb | ~cc ~ dd) + x[RIPEMD_160_N1[i]] + 0x6d703ef3 + s = int(RIPEMD_160_R1[i]) + alpha = util.ROTL32(alpha, s) + ee + beta = util.ROTL32(cc, 10) + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + i += 1 + } + for i < 64 { + alpha = a + (b&d | c&~d) + x[RIPEMD_160_N0[i]] + 0x8f1bbcdc + s := int(RIPEMD_160_R0[i]) + alpha = util.ROTL32(alpha, s) + e + beta = util.ROTL32(c, 10) + a, b, c, d, e = e, alpha, b, beta, d + alpha = aa + (bb&cc | ~bb&dd) + x[RIPEMD_160_N1[i]] + 0x7a6d76e9 + s = int(RIPEMD_160_R1[i]) + alpha = util.ROTL32(alpha, s) + ee + beta = util.ROTL32(cc, 10) + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + i += 1 + } + for i < 80 { + alpha = a + (b ~ (c | ~d)) + x[RIPEMD_160_N0[i]] + 0xa953fd4e + s := int(RIPEMD_160_R0[i]) + alpha = util.ROTL32(alpha, s) + e + beta = util.ROTL32(c, 10) + a, b, c, d, e = e, alpha, b, beta, d + alpha = aa + (bb ~ cc ~ dd) + x[RIPEMD_160_N1[i]] + s = int(RIPEMD_160_R1[i]) + alpha = util.ROTL32(alpha, s) + ee + beta = util.ROTL32(cc, 10) + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + i += 1 + } + dd += c + ctx.s[1] + ctx.s[1] = ctx.s[2] + d + ee + ctx.s[2] = ctx.s[3] + e + aa + ctx.s[3] = ctx.s[4] + a + bb + ctx.s[4] = ctx.s[0] + b + cc + ctx.s[0] = dd + p = p[RIPEMD_160_BLOCK_SIZE:] + n += RIPEMD_160_BLOCK_SIZE + } + return n +} + +ripemd_256_block :: proc(ctx: ^$T, p: []byte) -> int { + n := 0 + x: [16]u32 = --- + alpha: u32 = --- + p := p + for len(p) >= RIPEMD_256_BLOCK_SIZE { + a, b, c, d := ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] + aa, bb, cc, dd := ctx.s[4], ctx.s[5], ctx.s[6], ctx.s[7] + for i,j := 0, 0; i < 16; i, j = i+1, j+4 { + x[i] = u32(p[j]) | u32(p[j+1])<<8 | u32(p[j+2])<<16 | u32(p[j+3])<<24 + } + i := 0 + for i < 16 { + alpha = a + (b ~ c ~ d) + x[RIPEMD_128_N0[i]] + s := int(RIPEMD_128_R0[i]) + alpha = util.ROTL32(alpha, s) + a, b, c, d = d, alpha, b, c + alpha = aa + (bb & dd | cc &~ dd) + x[RIPEMD_128_N1[i]] + 0x50a28be6 + s = int(RIPEMD_128_R1[i]) + alpha = util.ROTL32(alpha, s) + aa, bb, cc, dd= dd, alpha, bb, cc + i += 1 + } + t := a + a = aa + aa = t + for i < 32 { + alpha = a + (d ~ (b & (c~d))) + x[RIPEMD_128_N0[i]] + 0x5a827999 + s := int(RIPEMD_128_R0[i]) + alpha = util.ROTL32(alpha, s) + a, b, c, d = d, alpha, b, c + alpha = aa + (dd ~ (bb | ~cc)) + x[RIPEMD_128_N1[i]] + 0x5c4dd124 + s = int(RIPEMD_128_R1[i]) + alpha = util.ROTL32(alpha, s) + aa, bb, cc, dd = dd, alpha, bb, cc + i += 1 + } + t = b + b = bb + bb = t + for i < 48 { + alpha = a + (d ~ (b | ~c)) + x[RIPEMD_128_N0[i]] + 0x6ed9eba1 + s := int(RIPEMD_128_R0[i]) + alpha = util.ROTL32(alpha, s) + a, b, c, d = d, alpha, b, c + alpha = aa + (dd ~ (bb & (cc~dd))) + x[RIPEMD_128_N1[i]] + 0x6d703ef3 + s = int(RIPEMD_128_R1[i]) + alpha = util.ROTL32(alpha, s) + aa, bb, cc, dd = dd, alpha, bb, cc + i += 1 + } + t = c + c = cc + cc = t + for i < 64 { + alpha = a + (c ~ (d & (b~c))) + x[RIPEMD_128_N0[i]] + 0x8f1bbcdc + s := int(RIPEMD_128_R0[i]) + alpha = util.ROTL32(alpha, s) + a, b, c, d = d, alpha, b, c + alpha = aa + (bb ~ cc ~ dd) + x[RIPEMD_128_N1[i]] + s = int(RIPEMD_128_R1[i]) + alpha = util.ROTL32(alpha, s) + aa, bb, cc, dd = dd, alpha, bb, cc + i += 1 + } + t = d + d = dd + dd = t + ctx.s[0] += a + ctx.s[1] += b + ctx.s[2] += c + ctx.s[3] += d + ctx.s[4] += aa + ctx.s[5] += bb + ctx.s[6] += cc + ctx.s[7] += dd + p = p[RIPEMD_256_BLOCK_SIZE:] + n += RIPEMD_256_BLOCK_SIZE + } + return n +} + +ripemd_320_block :: proc(ctx: ^$T, p: []byte) -> int { + n := 0 + x: [16]u32 = --- + alpha, beta: u32 = ---, --- + p := p + for len(p) >= RIPEMD_320_BLOCK_SIZE { + a, b, c, d, e := ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] + aa, bb, cc, dd, ee := ctx.s[5], ctx.s[6], ctx.s[7], ctx.s[8], ctx.s[9] + for i,j := 0, 0; i < 16; i, j = i+1, j+4 { + x[i] = u32(p[j]) | u32(p[j+1])<<8 | u32(p[j+2])<<16 | u32(p[j+3])<<24 + } + i := 0 + for i < 16 { + alpha = a + (b ~ c ~ d) + x[RIPEMD_160_N0[i]] + s := int(RIPEMD_160_R0[i]) + alpha = util.ROTL32(alpha, s) + e + beta = util.ROTL32(c, 10) + a, b, c, d, e = e, alpha, b, beta, d + alpha = aa + (bb ~ (cc | ~dd)) + x[RIPEMD_160_N1[i]] + 0x50a28be6 + s = int(RIPEMD_160_R1[i]) + alpha = util.ROTL32(alpha, s) + ee + beta = util.ROTL32(cc, 10) + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + i += 1 + } + t := b + b = bb + bb = t + for i < 32 { + alpha = a + (b&c | ~b&d) + x[RIPEMD_160_N0[i]] + 0x5a827999 + s := int(RIPEMD_160_R0[i]) + alpha = util.ROTL32(alpha, s) + e + beta = util.ROTL32(c, 10) + a, b, c, d, e = e, alpha, b, beta, d + alpha = aa + (bb&dd | cc&~dd) + x[RIPEMD_160_N1[i]] + 0x5c4dd124 + s = int(RIPEMD_160_R1[i]) + alpha = util.ROTL32(alpha, s) + ee + beta = util.ROTL32(cc, 10) + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + i += 1 + } + t = d + d = dd + dd = t + for i < 48 { + alpha = a + (b | ~c ~ d) + x[RIPEMD_160_N0[i]] + 0x6ed9eba1 + s := int(RIPEMD_160_R0[i]) + alpha = util.ROTL32(alpha, s) + e + beta = util.ROTL32(c, 10) + a, b, c, d, e = e, alpha, b, beta, d + alpha = aa + (bb | ~cc ~ dd) + x[RIPEMD_160_N1[i]] + 0x6d703ef3 + s = int(RIPEMD_160_R1[i]) + alpha = util.ROTL32(alpha, s) + ee + beta = util.ROTL32(cc, 10) + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + i += 1 + } + t = a + a = aa + aa = t + for i < 64 { + alpha = a + (b&d | c&~d) + x[RIPEMD_160_N0[i]] + 0x8f1bbcdc + s := int(RIPEMD_160_R0[i]) + alpha = util.ROTL32(alpha, s) + e + beta = util.ROTL32(c, 10) + a, b, c, d, e = e, alpha, b, beta, d + alpha = aa + (bb&cc | ~bb&dd) + x[RIPEMD_160_N1[i]] + 0x7a6d76e9 + s = int(RIPEMD_160_R1[i]) + alpha = util.ROTL32(alpha, s) + ee + beta = util.ROTL32(cc, 10) + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + i += 1 + } + t = c + c = cc + cc = t + for i < 80 { + alpha = a + (b ~ (c | ~d)) + x[RIPEMD_160_N0[i]] + 0xa953fd4e + s := int(RIPEMD_160_R0[i]) + alpha = util.ROTL32(alpha, s) + e + beta = util.ROTL32(c, 10) + a, b, c, d, e = e, alpha, b, beta, d + alpha = aa + (bb ~ cc ~ dd) + x[RIPEMD_160_N1[i]] + s = int(RIPEMD_160_R1[i]) + alpha = util.ROTL32(alpha, s) + ee + beta = util.ROTL32(cc, 10) + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + i += 1 + } + t = e + e = ee + ee = t + ctx.s[0] += a + ctx.s[1] += b + ctx.s[2] += c + ctx.s[3] += d + ctx.s[4] += e + ctx.s[5] += aa + ctx.s[6] += bb + ctx.s[7] += cc + ctx.s[8] += dd + ctx.s[9] += ee + p = p[RIPEMD_320_BLOCK_SIZE:] + n += RIPEMD_320_BLOCK_SIZE + } + return n +} + +update_odin :: proc(ctx: ^$T, p: []byte) { + ctx.tc += u64(len(p)) + p := p + if ctx.nx > 0 { + n := len(p) + + when T == Ripemd128_Context { + if n > RIPEMD_128_BLOCK_SIZE - ctx.nx { + n = RIPEMD_128_BLOCK_SIZE - ctx.nx + } + } else when T == Ripemd160_Context { + if n > RIPEMD_160_BLOCK_SIZE - ctx.nx { + n = RIPEMD_160_BLOCK_SIZE - ctx.nx + } + } else when T == Ripemd256_Context{ + if n > RIPEMD_256_BLOCK_SIZE - ctx.nx { + n = RIPEMD_256_BLOCK_SIZE - ctx.nx + } + } else when T == Ripemd320_Context{ + if n > RIPEMD_320_BLOCK_SIZE - ctx.nx { + n = RIPEMD_320_BLOCK_SIZE - ctx.nx + } + } + + for i := 0; i < n; i += 1 { + ctx.x[ctx.nx + i] = p[i] + } + + ctx.nx += n + when T == Ripemd128_Context { + if ctx.nx == RIPEMD_128_BLOCK_SIZE { + block(ctx, ctx.x[0:]) + ctx.nx = 0 + } + } else when T == Ripemd160_Context { + if ctx.nx == RIPEMD_160_BLOCK_SIZE { + block(ctx, ctx.x[0:]) + ctx.nx = 0 + } + } else when T == Ripemd256_Context{ + if ctx.nx == RIPEMD_256_BLOCK_SIZE { + block(ctx, ctx.x[0:]) + ctx.nx = 0 + } + } else when T == Ripemd320_Context{ + if ctx.nx == RIPEMD_320_BLOCK_SIZE { + block(ctx, ctx.x[0:]) + ctx.nx = 0 + } + } + p = p[n:] + } + n := block(ctx, p) + p = p[n:] + if len(p) > 0 { + ctx.nx = copy(ctx.x[:], p) + } +} + +final_odin :: proc(ctx: ^$T, hash: []byte) { + d := ctx + tc := d.tc + tmp: [64]byte + tmp[0] = 0x80 + + if tc % 64 < 56 { + update_odin(d, tmp[0:56 - tc % 64]) + } else { + update_odin(d, tmp[0:64 + 56 - tc % 64]) + } + + tc <<= 3 + for i : u32 = 0; i < 8; i += 1 { + tmp[i] = byte(tc >> (8 * i)) + } + + update_odin(d, tmp[0:8]) + + when T == Ripemd128_Context { + size :: RIPEMD_128_SIZE + } else when T == Ripemd160_Context { + size :: RIPEMD_160_SIZE + } else when T == Ripemd256_Context{ + size :: RIPEMD_256_SIZE + } else when T == Ripemd320_Context{ + size :: RIPEMD_320_SIZE + } + + digest: [size]byte + for s, i in d.s { + digest[i * 4] = byte(s) + digest[i * 4 + 1] = byte(s >> 8) + digest[i * 4 + 2] = byte(s >> 16) + digest[i * 4 + 3] = byte(s >> 24) + } + copy(hash[:], digest[:]) +} diff --git a/core/crypto/sha1/sha1.odin b/core/crypto/sha1/sha1.odin new file mode 100644 index 000000000..fe2d732da --- /dev/null +++ b/core/crypto/sha1/sha1.odin @@ -0,0 +1,329 @@ +package sha1 + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the SHA1 hashing algorithm, as defined in RFC 3174 +*/ + +import "core:mem" +import "core:os" +import "core:io" + +import "../util" +import "../botan" +import "../_ctx" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_20 = hash_bytes_odin + ctx.hash_file_20 = hash_file_odin + ctx.hash_stream_20 = hash_stream_odin + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan assigns the internal vtable of the hash context to use the Botan bindings +use_botan :: #force_inline proc() { + botan.assign_hash_vtable(_hash_impl, botan.HASH_SHA1) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string will hash the given input and return the +// computed hash +hash_string :: proc(data: string) -> [20]byte { + return hash_bytes(transmute([]byte)(data)) +} + +// hash_bytes will hash the given input and return the +// computed hash +hash_bytes :: proc(data: []byte) -> [20]byte { + _create_sha1_ctx() + return _hash_impl->hash_bytes_20(data) +} + +// hash_stream will read the stream in chunks and compute a +// hash from its contents +hash_stream :: proc(s: io.Stream) -> ([20]byte, bool) { + _create_sha1_ctx() + return _hash_impl->hash_stream_20(s) +} + +// hash_file will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file :: proc(path: string, load_at_once: bool) -> ([20]byte, bool) { + _create_sha1_ctx() + return _hash_impl->hash_file_20(path, load_at_once) +} + +hash :: proc { + hash_stream, + hash_file, + hash_bytes, + hash_string, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte { + hash: [20]byte + if c, ok := ctx.internal_ctx.(Sha1_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) { + hash: [20]byte + if c, ok := ctx.internal_ctx.(Sha1_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([20]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin(ctx, buf[:]), read_ok + } + } + } + return [20]byte{}, false +} + +@(private) +_create_sha1_ctx :: #force_inline proc() { + ctx: Sha1_Context + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = ._20 +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + _create_sha1_ctx() + if c, ok := ctx.internal_ctx.(Sha1_Context); ok { + init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(Sha1_Context); ok { + update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(Sha1_Context); ok { + final_odin(&c, hash) + } +} + +/* + SHA1 implementation +*/ + +BLOCK_SIZE :: 64 + +Sha1_Context :: struct { + data: [BLOCK_SIZE]byte, + datalen: u32, + bitlen: u64, + state: [5]u32, + k: [4]u32, +} + +transform :: proc(ctx: ^Sha1_Context, data: []byte) { + a, b, c, d, e, i, j, t: u32 + m: [80]u32 + + for i, j = 0, 0; i < 16; i += 1 { + m[i] = u32(data[j]) << 24 + u32(data[j + 1]) << 16 + u32(data[j + 2]) << 8 + u32(data[j + 3]) + j += 4 + } + for i < 80 { + m[i] = (m[i - 3] ~ m[i - 8] ~ m[i - 14] ~ m[i - 16]) + m[i] = (m[i] << 1) | (m[i] >> 31) + i += 1 + } + + a = ctx.state[0] + b = ctx.state[1] + c = ctx.state[2] + d = ctx.state[3] + e = ctx.state[4] + + for i = 0; i < 20; i += 1 { + t = util.ROTL32(a, 5) + ((b & c) ~ (~b & d)) + e + ctx.k[0] + m[i] + e = d + d = c + c = util.ROTL32(b, 30) + b = a + a = t + } + for i < 40 { + t = util.ROTL32(a, 5) + (b ~ c ~ d) + e + ctx.k[1] + m[i] + e = d + d = c + c = util.ROTL32(b, 30) + b = a + a = t + i += 1 + } + for i < 60 { + t = util.ROTL32(a, 5) + ((b & c) ~ (b & d) ~ (c & d)) + e + ctx.k[2] + m[i] + e = d + d = c + c = util.ROTL32(b, 30) + b = a + a = t + i += 1 + } + for i < 80 { + t = util.ROTL32(a, 5) + (b ~ c ~ d) + e + ctx.k[3] + m[i] + e = d + d = c + c = util.ROTL32(b, 30) + b = a + a = t + i += 1 + } + + ctx.state[0] += a + ctx.state[1] += b + ctx.state[2] += c + ctx.state[3] += d + ctx.state[4] += e +} + +init_odin :: proc(ctx: ^Sha1_Context) { + ctx.state[0] = 0x67452301 + ctx.state[1] = 0xefcdab89 + ctx.state[2] = 0x98badcfe + ctx.state[3] = 0x10325476 + ctx.state[4] = 0xc3d2e1f0 + ctx.k[0] = 0x5a827999 + ctx.k[1] = 0x6ed9eba1 + ctx.k[2] = 0x8f1bbcdc + ctx.k[3] = 0xca62c1d6 +} + +update_odin :: proc(ctx: ^Sha1_Context, data: []byte) { + for i := 0; i < len(data); i += 1 { + ctx.data[ctx.datalen] = data[i] + ctx.datalen += 1 + if (ctx.datalen == BLOCK_SIZE) { + transform(ctx, ctx.data[:]) + ctx.bitlen += 512 + ctx.datalen = 0 + } + } +} + +final_odin :: proc(ctx: ^Sha1_Context, hash: []byte) { + i := ctx.datalen + + if ctx.datalen < 56 { + ctx.data[i] = 0x80 + i += 1 + for i < 56 { + ctx.data[i] = 0x00 + i += 1 + } + } + else { + ctx.data[i] = 0x80 + i += 1 + for i < BLOCK_SIZE { + ctx.data[i] = 0x00 + i += 1 + } + transform(ctx, ctx.data[:]) + mem.set(&ctx.data, 0, 56) + } + + ctx.bitlen += u64(ctx.datalen * 8) + ctx.data[63] = u8(ctx.bitlen) + ctx.data[62] = u8(ctx.bitlen >> 8) + ctx.data[61] = u8(ctx.bitlen >> 16) + ctx.data[60] = u8(ctx.bitlen >> 24) + ctx.data[59] = u8(ctx.bitlen >> 32) + ctx.data[58] = u8(ctx.bitlen >> 40) + ctx.data[57] = u8(ctx.bitlen >> 48) + ctx.data[56] = u8(ctx.bitlen >> 56) + transform(ctx, ctx.data[:]) + + for j: u32 = 0; j < 4; j += 1 { + hash[j] = u8(ctx.state[0] >> (24 - j * 8)) & 0x000000ff + hash[j + 4] = u8(ctx.state[1] >> (24 - j * 8)) & 0x000000ff + hash[j + 8] = u8(ctx.state[2] >> (24 - j * 8)) & 0x000000ff + hash[j + 12] = u8(ctx.state[3] >> (24 - j * 8)) & 0x000000ff + hash[j + 16] = u8(ctx.state[4] >> (24 - j * 8)) & 0x000000ff + } +} \ No newline at end of file diff --git a/core/crypto/sha2/sha2.odin b/core/crypto/sha2/sha2.odin new file mode 100644 index 000000000..ad23b473c --- /dev/null +++ b/core/crypto/sha2/sha2.odin @@ -0,0 +1,797 @@ +package sha2 + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the SHA2 hashing algorithm, as defined in + and in RFC 3874 +*/ + +import "core:mem" +import "core:os" +import "core:io" + +import "../util" +import "../botan" +import "../_ctx" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_28 = hash_bytes_odin_28 + ctx.hash_file_28 = hash_file_odin_28 + ctx.hash_stream_28 = hash_stream_odin_28 + ctx.hash_bytes_32 = hash_bytes_odin_32 + ctx.hash_file_32 = hash_file_odin_32 + ctx.hash_stream_32 = hash_stream_odin_32 + ctx.hash_bytes_48 = hash_bytes_odin_48 + ctx.hash_file_48 = hash_file_odin_48 + ctx.hash_stream_48 = hash_stream_odin_48 + ctx.hash_bytes_64 = hash_bytes_odin_64 + ctx.hash_file_64 = hash_file_odin_64 + ctx.hash_stream_64 = hash_stream_odin_64 + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan assigns the internal vtable of the hash context to use the Botan bindings +use_botan :: #force_inline proc() { + botan.assign_hash_vtable(_hash_impl, botan.HASH_SHA2) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +@(private) +_create_sha256_ctx :: #force_inline proc(is224: bool) { + ctx: Sha256_Context + ctx.is224 = is224 + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = is224 ? ._28 : ._32 +} + +@(private) +_create_sha512_ctx :: #force_inline proc(is384: bool) { + ctx: Sha512_Context + ctx.is384 = is384 + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = is384 ? ._48 : ._64 +} + +/* + High level API +*/ + +// hash_string_224 will hash the given input and return the +// computed hash +hash_string_224 :: proc(data: string) -> [28]byte { + return hash_bytes_224(transmute([]byte)(data)) +} + +// hash_bytes_224 will hash the given input and return the +// computed hash +hash_bytes_224 :: proc(data: []byte) -> [28]byte { + _create_sha256_ctx(true) + return _hash_impl->hash_bytes_28(data) +} + +// hash_stream_224 will read the stream in chunks and compute a +// hash from its contents +hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) { + _create_sha256_ctx(true) + return _hash_impl->hash_stream_28(s) +} + +// hash_file_224 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_224 :: proc(path: string, load_at_once: bool) -> ([28]byte, bool) { + _create_sha256_ctx(true) + return _hash_impl->hash_file_28(path, load_at_once) +} + +hash_224 :: proc { + hash_stream_224, + hash_file_224, + hash_bytes_224, + hash_string_224, +} + +// hash_string_256 will hash the given input and return the +// computed hash +hash_string_256 :: proc(data: string) -> [32]byte { + return hash_bytes_256(transmute([]byte)(data)) +} + +// hash_bytes_256 will hash the given input and return the +// computed hash +hash_bytes_256 :: proc(data: []byte) -> [32]byte { + _create_sha256_ctx(false) + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream_256 will read the stream in chunks and compute a +// hash from its contents +hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_sha256_ctx(false) + return _hash_impl->hash_stream_32(s) +} + +// hash_file_256 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_256 :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_sha256_ctx(false) + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash_256 :: proc { + hash_stream_256, + hash_file_256, + hash_bytes_256, + hash_string_256, +} + +// hash_string_384 will hash the given input and return the +// computed hash +hash_string_384 :: proc(data: string) -> [48]byte { + return hash_bytes_384(transmute([]byte)(data)) +} + +// hash_bytes_384 will hash the given input and return the +// computed hash +hash_bytes_384 :: proc(data: []byte) -> [48]byte { + _create_sha512_ctx(true) + return _hash_impl->hash_bytes_48(data) +} + +// hash_stream_384 will read the stream in chunks and compute a +// hash from its contents +hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) { + _create_sha512_ctx(true) + return _hash_impl->hash_stream_48(s) +} + +// hash_file_384 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_384 :: proc(path: string, load_at_once: bool) -> ([48]byte, bool) { + _create_sha512_ctx(true) + return _hash_impl->hash_file_48(path, load_at_once) +} + +hash_384 :: proc { + hash_stream_384, + hash_file_384, + hash_bytes_384, + hash_string_384, +} + +// hash_string_512 will hash the given input and return the +// computed hash +hash_string_512 :: proc(data: string) -> [64]byte { + return hash_bytes_512(transmute([]byte)(data)) +} + +// hash_bytes_512 will hash the given input and return the +// computed hash +hash_bytes_512 :: proc(data: []byte) -> [64]byte { + _create_sha512_ctx(false) + return _hash_impl->hash_bytes_64(data) +} + +// hash_stream_512 will read the stream in chunks and compute a +// hash from its contents +hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) { + _create_sha512_ctx(false) + return _hash_impl->hash_stream_64(s) +} + +// hash_file_512 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_512 :: proc(path: string, load_at_once: bool) -> ([64]byte, bool) { + _create_sha512_ctx(false) + return _hash_impl->hash_file_64(path, load_at_once) +} + +hash_512 :: proc { + hash_stream_512, + hash_file_512, + hash_bytes_512, + hash_string_512, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte { + hash: [28]byte + if c, ok := ctx.internal_ctx.(Sha256_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) { + hash: [28]byte + if c, ok := ctx.internal_ctx.(Sha256_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([28]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_28(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_28(ctx, buf[:]), read_ok + } + } + } + return [28]byte{}, false +} + +hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Sha256_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Sha256_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_32(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_32(ctx, buf[:]), read_ok + } + } + } + return [32]byte{}, false +} + +hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte { + hash: [48]byte + if c, ok := ctx.internal_ctx.(Sha512_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) { + hash: [48]byte + if c, ok := ctx.internal_ctx.(Sha512_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([48]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_48(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_48(ctx, buf[:]), read_ok + } + } + } + return [48]byte{}, false +} + +hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte { + hash: [64]byte + if c, ok := ctx.internal_ctx.(Sha512_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) { + hash: [64]byte + if c, ok := ctx.internal_ctx.(Sha512_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([64]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_64(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_64(ctx, buf[:]), read_ok + } + } + } + return [64]byte{}, false +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + if ctx.hash_size == ._28 || ctx.hash_size == ._32 { + _create_sha256_ctx(ctx.hash_size == ._28) + if c, ok := ctx.internal_ctx.(Sha256_Context); ok { + init_odin(&c) + } + return + } + if ctx.hash_size == ._48 || ctx.hash_size == ._64 { + _create_sha512_ctx(ctx.hash_size == ._48) + if c, ok := ctx.internal_ctx.(Sha512_Context); ok { + init_odin(&c) + } + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + #partial switch ctx.hash_size { + case ._28, ._32: + if c, ok := ctx.internal_ctx.(Sha256_Context); ok { + update_odin(&c, data) + } + case ._48, ._64: + if c, ok := ctx.internal_ctx.(Sha512_Context); ok { + update_odin(&c, data) + } + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + #partial switch ctx.hash_size { + case ._28, ._32: + if c, ok := ctx.internal_ctx.(Sha256_Context); ok { + final_odin(&c, hash) + } + case ._48, ._64: + if c, ok := ctx.internal_ctx.(Sha512_Context); ok { + final_odin(&c, hash) + } + } +} + +/* + SHA2 implementation +*/ + +SHA256_BLOCK_SIZE :: 64 +SHA512_BLOCK_SIZE :: 128 + +Sha256_Context :: struct { + tot_len: uint, + length: uint, + block: [128]byte, + h: [8]u32, + is224: bool, +} + +Sha512_Context :: struct { + tot_len: uint, + length: uint, + block: [256]byte, + h: [8]u64, + is384: bool, +} + +sha256_k := [64]u32 { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +} + +sha512_k := [80]u64 { + 0x428a2f98d728ae22, 0x7137449123ef65cd, + 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, + 0x3956c25bf348b538, 0x59f111f1b605d019, + 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, + 0xd807aa98a3030242, 0x12835b0145706fbe, + 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, + 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, + 0x9bdc06a725c71235, 0xc19bf174cf692694, + 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, + 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, + 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, + 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, + 0x983e5152ee66dfab, 0xa831c66d2db43210, + 0xb00327c898fb213f, 0xbf597fc7beef0ee4, + 0xc6e00bf33da88fc2, 0xd5a79147930aa725, + 0x06ca6351e003826f, 0x142929670a0e6e70, + 0x27b70a8546d22ffc, 0x2e1b21385c26c926, + 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, + 0x650a73548baf63de, 0x766a0abb3c77b2a8, + 0x81c2c92e47edaee6, 0x92722c851482353b, + 0xa2bfe8a14cf10364, 0xa81a664bbc423001, + 0xc24b8b70d0f89791, 0xc76c51a30654be30, + 0xd192e819d6ef5218, 0xd69906245565a910, + 0xf40e35855771202a, 0x106aa07032bbd1b8, + 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, + 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, + 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, + 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, + 0x748f82ee5defb2fc, 0x78a5636f43172f60, + 0x84c87814a1f0ab72, 0x8cc702081a6439ec, + 0x90befffa23631e28, 0xa4506cebde82bde9, + 0xbef9a3f7b2c67915, 0xc67178f2e372532b, + 0xca273eceea26619c, 0xd186b8c721c0c207, + 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, + 0x06f067aa72176fba, 0x0a637dc5a2c898a6, + 0x113f9804bef90dae, 0x1b710b35131c471b, + 0x28db77f523047d84, 0x32caab7b40c72493, + 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, + 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, + 0x5fcb6fab3ad6faec, 0x6c44198c4a475817, +} + +SHA256_CH :: #force_inline proc "contextless"(x, y, z: u32) -> u32 { + return (x & y) ~ (~x & z) +} + +SHA256_MAJ :: #force_inline proc "contextless"(x, y, z: u32) -> u32 { + return (x & y) ~ (x & z) ~ (y & z) +} + +SHA512_CH :: #force_inline proc "contextless"(x, y, z: u64) -> u64 { + return (x & y) ~ (~x & z) +} + +SHA512_MAJ :: #force_inline proc "contextless"(x, y, z: u64) -> u64 { + return (x & y) ~ (x & z) ~ (y & z) +} + +SHA256_F1 :: #force_inline proc "contextless"(x: u32) -> u32 { + return util.ROTR32(x, 2) ~ util.ROTR32(x, 13) ~ util.ROTR32(x, 22) +} + +SHA256_F2 :: #force_inline proc "contextless"(x: u32) -> u32 { + return util.ROTR32(x, 6) ~ util.ROTR32(x, 11) ~ util.ROTR32(x, 25) +} + +SHA256_F3 :: #force_inline proc "contextless"(x: u32) -> u32 { + return util.ROTR32(x, 7) ~ util.ROTR32(x, 18) ~ (x >> 3) +} + +SHA256_F4 :: #force_inline proc "contextless"(x: u32) -> u32 { + return util.ROTR32(x, 17) ~ util.ROTR32(x, 19) ~ (x >> 10) +} + +SHA512_F1 :: #force_inline proc "contextless"(x: u64) -> u64 { + return util.ROTR64(x, 28) ~ util.ROTR64(x, 34) ~ util.ROTR64(x, 39) +} + +SHA512_F2 :: #force_inline proc "contextless"(x: u64) -> u64 { + return util.ROTR64(x, 14) ~ util.ROTR64(x, 18) ~ util.ROTR64(x, 41) +} + +SHA512_F3 :: #force_inline proc "contextless"(x: u64) -> u64 { + return util.ROTR64(x, 1) ~ util.ROTR64(x, 8) ~ (x >> 7) +} + +SHA512_F4 :: #force_inline proc "contextless"(x: u64) -> u64 { + return util.ROTR64(x, 19) ~ util.ROTR64(x, 61) ~ (x >> 6) +} + +PACK32 :: #force_inline proc "contextless"(b: []byte, x: ^u32) { + x^ = u32(b[3]) | u32(b[2]) << 8 | u32(b[1]) << 16 | u32(b[0]) << 24 +} + +PACK64 :: #force_inline proc "contextless"(b: []byte, x: ^u64) { + x^ = u64(b[7]) | u64(b[6]) << 8 | u64(b[5]) << 16 | u64(b[4]) << 24 | u64(b[3]) << 32 | u64(b[2]) << 40 | u64(b[1]) << 48 | u64(b[0]) << 56 +} + +init_odin :: proc(ctx: ^$T) { + when T == Sha256_Context { + if ctx.is224 { + ctx.h[0] = 0xc1059ed8 + ctx.h[1] = 0x367cd507 + ctx.h[2] = 0x3070dd17 + ctx.h[3] = 0xf70e5939 + ctx.h[4] = 0xffc00b31 + ctx.h[5] = 0x68581511 + ctx.h[6] = 0x64f98fa7 + ctx.h[7] = 0xbefa4fa4 + } else { + ctx.h[0] = 0x6a09e667 + ctx.h[1] = 0xbb67ae85 + ctx.h[2] = 0x3c6ef372 + ctx.h[3] = 0xa54ff53a + ctx.h[4] = 0x510e527f + ctx.h[5] = 0x9b05688c + ctx.h[6] = 0x1f83d9ab + ctx.h[7] = 0x5be0cd19 + } + } else when T == Sha512_Context { + if ctx.is384 { + ctx.h[0] = 0xcbbb9d5dc1059ed8 + ctx.h[1] = 0x629a292a367cd507 + ctx.h[2] = 0x9159015a3070dd17 + ctx.h[3] = 0x152fecd8f70e5939 + ctx.h[4] = 0x67332667ffc00b31 + ctx.h[5] = 0x8eb44a8768581511 + ctx.h[6] = 0xdb0c2e0d64f98fa7 + ctx.h[7] = 0x47b5481dbefa4fa4 + } else { + ctx.h[0] = 0x6a09e667f3bcc908 + ctx.h[1] = 0xbb67ae8584caa73b + ctx.h[2] = 0x3c6ef372fe94f82b + ctx.h[3] = 0xa54ff53a5f1d36f1 + ctx.h[4] = 0x510e527fade682d1 + ctx.h[5] = 0x9b05688c2b3e6c1f + ctx.h[6] = 0x1f83d9abfb41bd6b + ctx.h[7] = 0x5be0cd19137e2179 + } + } +} + +sha2_transf :: proc(ctx: ^$T, data: []byte, block_nb: uint) { + when T == Sha256_Context { + w: [64]u32 + wv: [8]u32 + t1, t2: u32 + } else when T == Sha512_Context { + w: [80]u64 + wv: [8]u64 + t1, t2: u64 + } + + sub_block := make([]byte, len(data)) + i, j: i32 + + for i = 0; i < i32(block_nb); i += 1 { + when T == Sha256_Context { + sub_block = data[i << 6:] + } else when T == Sha512_Context { + sub_block = data[i << 7:] + } + + for j = 0; j < 16; j += 1 { + when T == Sha256_Context { + PACK32(sub_block[j << 2:], &w[j]) + } else when T == Sha512_Context { + PACK64(sub_block[j << 3:], &w[j]) + } + } + + when T == Sha256_Context { + for j = 16; j < 64; j += 1 { + w[j] = SHA256_F4(w[j - 2]) + w[j - 7] + SHA256_F3(w[j - 15]) + w[j - 16] + } + } else when T == Sha512_Context { + for j = 16; j < 80; j += 1 { + w[j] = SHA512_F4(w[j - 2]) + w[j - 7] + SHA512_F3(w[j - 15]) + w[j - 16] + } + } + + for j = 0; j < 8; j += 1 { + wv[j] = ctx.h[j] + } + + when T == Sha256_Context { + for j = 0; j < 64; j += 1 { + t1 = wv[7] + SHA256_F2(wv[4]) + SHA256_CH(wv[4], wv[5], wv[6]) + sha256_k[j] + w[j] + t2 = SHA256_F1(wv[0]) + SHA256_MAJ(wv[0], wv[1], wv[2]) + wv[7] = wv[6] + wv[6] = wv[5] + wv[5] = wv[4] + wv[4] = wv[3] + t1 + wv[3] = wv[2] + wv[2] = wv[1] + wv[1] = wv[0] + wv[0] = t1 + t2 + } + } else when T == Sha512_Context { + for j = 0; j < 80; j += 1 { + t1 = wv[7] + SHA512_F2(wv[4]) + SHA512_CH(wv[4], wv[5], wv[6]) + sha512_k[j] + w[j] + t2 = SHA512_F1(wv[0]) + SHA512_MAJ(wv[0], wv[1], wv[2]) + wv[7] = wv[6] + wv[6] = wv[5] + wv[5] = wv[4] + wv[4] = wv[3] + t1 + wv[3] = wv[2] + wv[2] = wv[1] + wv[1] = wv[0] + wv[0] = t1 + t2 + } + } + + for j = 0; j < 8; j += 1 { + ctx.h[j] += wv[j] + } + } +} + +update_odin :: proc(ctx: ^$T, data: []byte) { + length := uint(len(data)) + block_nb: uint + new_len, rem_len, tmp_len: uint + shifted_message := make([]byte, length) + + when T == Sha256_Context { + CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE + } else when T == Sha512_Context { + CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE + } + + tmp_len = CURR_BLOCK_SIZE - ctx.length + rem_len = length < tmp_len ? length : tmp_len + copy(ctx.block[ctx.length:], data[:rem_len]) + + if ctx.length + length < CURR_BLOCK_SIZE { + ctx.length += length + return + } + + new_len = length - rem_len + block_nb = new_len / CURR_BLOCK_SIZE + shifted_message = data[rem_len:] + + sha2_transf(ctx, ctx.block[:], 1) + sha2_transf(ctx, shifted_message, block_nb) + + rem_len = new_len % CURR_BLOCK_SIZE + when T == Sha256_Context {copy(ctx.block[:], shifted_message[block_nb << 6:rem_len])} + else when T == Sha512_Context {copy(ctx.block[:], shifted_message[block_nb << 7:rem_len])} + + ctx.length = rem_len + when T == Sha256_Context {ctx.tot_len += (block_nb + 1) << 6} + else when T == Sha512_Context {ctx.tot_len += (block_nb + 1) << 7} +} + +final_odin :: proc(ctx: ^$T, hash: []byte) { + block_nb, pm_len, len_b: u32 + i: i32 + + when T == Sha256_Context {CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE} + else when T == Sha512_Context {CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE} + + when T == Sha256_Context {block_nb = 1 + ((CURR_BLOCK_SIZE - 9) < (ctx.length % CURR_BLOCK_SIZE) ? 1 : 0)} + else when T == Sha512_Context {block_nb = 1 + ((CURR_BLOCK_SIZE - 17) < (ctx.length % CURR_BLOCK_SIZE) ? 1 : 0)} + + len_b = u32(ctx.tot_len + ctx.length) << 3 + when T == Sha256_Context {pm_len = block_nb << 6} + else when T == Sha512_Context {pm_len = block_nb << 7} + + mem.set(rawptr(&(ctx.block[ctx.length:])[0]), 0, int(uint(pm_len) - ctx.length)) + ctx.block[ctx.length] = 0x80 + + util.PUT_U32_BE(ctx.block[pm_len - 4:], len_b) + + sha2_transf(ctx, ctx.block[:], uint(block_nb)) + + when T == Sha256_Context { + if ctx.is224 { + for i = 0; i < 7; i += 1 {util.PUT_U32_BE(hash[i << 2:], ctx.h[i])} + } else { + for i = 0; i < 8; i += 1 {util.PUT_U32_BE(hash[i << 2:], ctx.h[i])} + } + } else when T == Sha512_Context { + if ctx.is384 { + for i = 0; i < 6; i += 1 {util.PUT_U64_BE(hash[i << 3:], ctx.h[i])} + } else { + for i = 0; i < 8; i += 1 {util.PUT_U64_BE(hash[i << 3:], ctx.h[i])} + } + } +} \ No newline at end of file diff --git a/core/crypto/sha3/sha3.odin b/core/crypto/sha3/sha3.odin new file mode 100644 index 000000000..184bd5358 --- /dev/null +++ b/core/crypto/sha3/sha3.odin @@ -0,0 +1,440 @@ +package sha3 + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Interface for the SHA3 hashing algorithm. The SHAKE functionality can be found in package shake. + If you wish to compute a Keccak hash, you can use the keccak package, it will use the original padding. +*/ + +import "core:os" +import "core:io" + +import "../botan" +import "../_ctx" +import "../_sha3" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_28 = hash_bytes_odin_28 + ctx.hash_file_28 = hash_file_odin_28 + ctx.hash_stream_28 = hash_stream_odin_28 + ctx.hash_bytes_32 = hash_bytes_odin_32 + ctx.hash_file_32 = hash_file_odin_32 + ctx.hash_stream_32 = hash_stream_odin_32 + ctx.hash_bytes_48 = hash_bytes_odin_48 + ctx.hash_file_48 = hash_file_odin_48 + ctx.hash_stream_48 = hash_stream_odin_48 + ctx.hash_bytes_64 = hash_bytes_odin_64 + ctx.hash_file_64 = hash_file_odin_64 + ctx.hash_stream_64 = hash_stream_odin_64 + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan assigns the internal vtable of the hash context to use the Botan bindings +use_botan :: #force_inline proc() { + botan.assign_hash_vtable(_hash_impl, botan.HASH_SHA3) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string_224 will hash the given input and return the +// computed hash +hash_string_224 :: proc(data: string) -> [28]byte { + return hash_bytes_224(transmute([]byte)(data)) +} + +// hash_bytes_224 will hash the given input and return the +// computed hash +hash_bytes_224 :: proc(data: []byte) -> [28]byte { + _create_sha3_ctx(28) + return _hash_impl->hash_bytes_28(data) +} + +// hash_stream_224 will read the stream in chunks and compute a +// hash from its contents +hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) { + _create_sha3_ctx(28) + return _hash_impl->hash_stream_28(s) +} + +// hash_file_224 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_224 :: proc(path: string, load_at_once: bool) -> ([28]byte, bool) { + _create_sha3_ctx(28) + return _hash_impl->hash_file_28(path, load_at_once) +} + +hash_224 :: proc { + hash_stream_224, + hash_file_224, + hash_bytes_224, + hash_string_224, +} + +// hash_string_256 will hash the given input and return the +// computed hash +hash_string_256 :: proc(data: string) -> [32]byte { + return hash_bytes_256(transmute([]byte)(data)) +} + +// hash_bytes_256 will hash the given input and return the +// computed hash +hash_bytes_256 :: proc(data: []byte) -> [32]byte { + _create_sha3_ctx(32) + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream_256 will read the stream in chunks and compute a +// hash from its contents +hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_sha3_ctx(32) + return _hash_impl->hash_stream_32(s) +} + +// hash_file_256 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_256 :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_sha3_ctx(32) + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash_256 :: proc { + hash_stream_256, + hash_file_256, + hash_bytes_256, + hash_string_256, +} + +// hash_string_384 will hash the given input and return the +// computed hash +hash_string_384 :: proc(data: string) -> [48]byte { + return hash_bytes_384(transmute([]byte)(data)) +} + +// hash_bytes_384 will hash the given input and return the +// computed hash +hash_bytes_384 :: proc(data: []byte) -> [48]byte { + _create_sha3_ctx(48) + return _hash_impl->hash_bytes_48(data) +} + +// hash_stream_384 will read the stream in chunks and compute a +// hash from its contents +hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) { + _create_sha3_ctx(48) + return _hash_impl->hash_stream_48(s) +} + +// hash_file_384 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_384 :: proc(path: string, load_at_once: bool) -> ([48]byte, bool) { + _create_sha3_ctx(48) + return _hash_impl->hash_file_48(path, load_at_once) +} + +hash_384 :: proc { + hash_stream_384, + hash_file_384, + hash_bytes_384, + hash_string_384, +} + +// hash_string_512 will hash the given input and return the +// computed hash +hash_string_512 :: proc(data: string) -> [64]byte { + return hash_bytes_512(transmute([]byte)(data)) +} + +// hash_bytes_512 will hash the given input and return the +// computed hash +hash_bytes_512 :: proc(data: []byte) -> [64]byte { + _create_sha3_ctx(64) + return _hash_impl->hash_bytes_64(data) +} + +// hash_stream_512 will read the stream in chunks and compute a +// hash from its contents +hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) { + _create_sha3_ctx(64) + return _hash_impl->hash_stream_64(s) +} + +// hash_file_512 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_512 :: proc(path: string, load_at_once: bool) -> ([64]byte, bool) { + _create_sha3_ctx(64) + return _hash_impl->hash_file_64(path, load_at_once) +} + +hash_512 :: proc { + hash_stream_512, + hash_file_512, + hash_bytes_512, + hash_string_512, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte { + hash: [28]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + _sha3.update_odin(&c, data) + _sha3.final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) { + hash: [28]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _sha3.update_odin(&c, buf[:read]) + } + } + _sha3.final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([28]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_28(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_28(ctx, buf[:]), read_ok + } + } + } + return [28]byte{}, false +} + +hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { + hash: [32]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + _sha3.update_odin(&c, data) + _sha3.final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { + hash: [32]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _sha3.update_odin(&c, buf[:read]) + } + } + _sha3.final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_32(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_32(ctx, buf[:]), read_ok + } + } + } + return [32]byte{}, false +} + +hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte { + hash: [48]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + _sha3.update_odin(&c, data) + _sha3.final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) { + hash: [48]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _sha3.update_odin(&c, buf[:read]) + } + } + _sha3.final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([48]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_48(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_48(ctx, buf[:]), read_ok + } + } + } + return [48]byte{}, false +} + +hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte { + hash: [64]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + _sha3.update_odin(&c, data) + _sha3.final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) { + hash: [64]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _sha3.update_odin(&c, buf[:read]) + } + } + _sha3.final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([64]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_64(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_64(ctx, buf[:]), read_ok + } + } + } + return [64]byte{}, false +} + +@(private) +_create_sha3_ctx :: #force_inline proc(mdlen: int) { + ctx: _sha3.Sha3_Context + ctx.mdlen = mdlen + _hash_impl.internal_ctx = ctx + switch mdlen { + case 28: _hash_impl.hash_size = ._28 + case 32: _hash_impl.hash_size = ._32 + case 48: _hash_impl.hash_size = ._48 + case 64: _hash_impl.hash_size = ._64 + } +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + #partial switch ctx.hash_size { + case ._28: _create_sha3_ctx(28) + case ._32: _create_sha3_ctx(32) + case ._48: _create_sha3_ctx(48) + case ._64: _create_sha3_ctx(64) + } + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.final_odin(&c, hash) + } +} diff --git a/core/crypto/shake/shake.odin b/core/crypto/shake/shake.odin new file mode 100644 index 000000000..ed6517751 --- /dev/null +++ b/core/crypto/shake/shake.odin @@ -0,0 +1,279 @@ +package shake + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Interface for the SHAKE hashing algorithm. + The SHA3 functionality can be found in package sha3. +*/ + +import "core:os" +import "core:io" + +import "../botan" +import "../_ctx" +import "../_sha3" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_16 = hash_bytes_odin_16 + ctx.hash_file_16 = hash_file_odin_16 + ctx.hash_stream_16 = hash_stream_odin_16 + ctx.hash_bytes_32 = hash_bytes_odin_32 + ctx.hash_file_32 = hash_file_odin_32 + ctx.hash_stream_32 = hash_stream_odin_32 + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan assigns the internal vtable of the hash context to use the Botan bindings +use_botan :: #force_inline proc() { + botan.assign_hash_vtable(_hash_impl, botan.HASH_SHAKE) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string_128 will hash the given input and return the +// computed hash +hash_string_128 :: proc(data: string) -> [16]byte { + return hash_bytes_128(transmute([]byte)(data)) +} + +// hash_bytes_128 will hash the given input and return the +// computed hash +hash_bytes_128 :: proc(data: []byte) -> [16]byte { + _create_sha3_ctx(16) + return _hash_impl->hash_bytes_16(data) +} + +// hash_stream_128 will read the stream in chunks and compute a +// hash from its contents +hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) { + _create_sha3_ctx(16) + return _hash_impl->hash_stream_16(s) +} + +// hash_file_128 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_128 :: proc(path: string, load_at_once: bool) -> ([16]byte, bool) { + _create_sha3_ctx(16) + return _hash_impl->hash_file_16(path, load_at_once) +} + +hash_128 :: proc { + hash_stream_128, + hash_file_128, + hash_bytes_128, + hash_string_128, +} + +// hash_string_256 will hash the given input and return the +// computed hash +hash_string_256 :: proc(data: string) -> [32]byte { + return hash_bytes_256(transmute([]byte)(data)) +} + +// hash_bytes_256 will hash the given input and return the +// computed hash +hash_bytes_256 :: proc(data: []byte) -> [32]byte { + _create_sha3_ctx(32) + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream_256 will read the stream in chunks and compute a +// hash from its contents +hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_sha3_ctx(32) + return _hash_impl->hash_stream_32(s) +} + +// hash_file_256 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_256 :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_sha3_ctx(32) + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash_256 :: proc { + hash_stream_256, + hash_file_256, + hash_bytes_256, + hash_string_256, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte { + hash: [16]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + _sha3.update_odin(&c, data) + _sha3.shake_xof_odin(&c) + _sha3.shake_out_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) { + hash: [16]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _sha3.update_odin(&c, buf[:read]) + } + } + _sha3.shake_xof_odin(&c) + _sha3.shake_out_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([16]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_16(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_16(ctx, buf[:]), read_ok + } + } + } + return [16]byte{}, false +} + +hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { + hash: [32]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + _sha3.update_odin(&c, data) + _sha3.shake_xof_odin(&c) + _sha3.shake_out_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { + hash: [32]byte + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _sha3.update_odin(&c, buf[:read]) + } + } + _sha3.shake_xof_odin(&c) + _sha3.shake_out_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_32(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_32(ctx, buf[:]), read_ok + } + } + } + return [32]byte{}, false +} + +@(private) +_create_sha3_ctx :: #force_inline proc(mdlen: int) { + ctx: _sha3.Sha3_Context + ctx.mdlen = mdlen + _hash_impl.internal_ctx = ctx + switch mdlen { + case 16: _hash_impl.hash_size = ._16 + case 32: _hash_impl.hash_size = ._32 + } +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + #partial switch ctx.hash_size { + case ._16: _create_sha3_ctx(16) + case ._32: _create_sha3_ctx(32) + } + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok { + _sha3.shake_xof_odin(&c) + _sha3.shake_out_odin(&c, hash[:]) + } +} diff --git a/core/crypto/skein/skein.odin b/core/crypto/skein/skein.odin new file mode 100644 index 000000000..08c53415a --- /dev/null +++ b/core/crypto/skein/skein.odin @@ -0,0 +1,496 @@ +package skein + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the SKEIN hashing algorithm, as defined in + + This package offers the internal state sizes of 256, 512 and 1024 bits and arbitrary output size. +*/ + +import "core:os" +import "core:io" + +import "../botan" +import "../_ctx" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + ctx.is_using_odin = false + } else { + _assign_hash_vtable(ctx) + ctx.is_using_odin = true + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + // @note(zh): Default to SKEIN-512 + ctx.hash_bytes_slice = hash_bytes_skein512_odin + ctx.hash_file_slice = hash_file_skein512_odin + ctx.hash_stream_slice = hash_stream_skein512_odin + ctx.init = _init_skein512_odin + ctx.update = _update_skein512_odin + ctx.final = _final_skein512_odin +} + +_hash_impl := _init_vtable() + +// use_botan assigns the internal vtable of the hash context to use the Botan bindings +use_botan :: #force_inline proc() { + _hash_impl.is_using_odin = false + // @note(zh): Botan only supports SKEIN-512. + botan.assign_hash_vtable(_hash_impl, botan.HASH_SKEIN_512) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +@(warning="SKEIN is not yet implemented in Odin. Botan bindings will be used") +use_odin :: #force_inline proc() { + // _hash_impl.is_using_odin = true + // _assign_hash_vtable(_hash_impl) + use_botan() +} + +@(private) +_create_skein256_ctx :: #force_inline proc(size: int) { + _hash_impl.hash_size_val = size + if _hash_impl.is_using_odin { + ctx: Skein256_Context + ctx.h.bit_length = u64(size) + _hash_impl.internal_ctx = ctx + _hash_impl.hash_bytes_slice = hash_bytes_skein256_odin + _hash_impl.hash_file_slice = hash_file_skein256_odin + _hash_impl.hash_stream_slice = hash_stream_skein256_odin + _hash_impl.init = _init_skein256_odin + _hash_impl.update = _update_skein256_odin + _hash_impl.final = _final_skein256_odin + } +} + +@(private) +_create_skein512_ctx :: #force_inline proc(size: int) { + _hash_impl.hash_size_val = size + if _hash_impl.is_using_odin { + ctx: Skein512_Context + ctx.h.bit_length = u64(size) + _hash_impl.internal_ctx = ctx + _hash_impl.hash_bytes_slice = hash_bytes_skein512_odin + _hash_impl.hash_file_slice = hash_file_skein512_odin + _hash_impl.hash_stream_slice = hash_stream_skein512_odin + _hash_impl.init = _init_skein512_odin + _hash_impl.update = _update_skein512_odin + _hash_impl.final = _final_skein512_odin + } +} + +@(private) +_create_skein1024_ctx :: #force_inline proc(size: int) { + _hash_impl.hash_size_val = size + if _hash_impl.is_using_odin { + ctx: Skein1024_Context + ctx.h.bit_length = u64(size) + _hash_impl.internal_ctx = ctx + _hash_impl.hash_bytes_slice = hash_bytes_skein1024_odin + _hash_impl.hash_file_slice = hash_file_skein1024_odin + _hash_impl.hash_stream_slice = hash_stream_skein1024_odin + _hash_impl.init = _init_skein1024_odin + _hash_impl.update = _update_skein1024_odin + _hash_impl.final = _final_skein1024_odin + } +} + +/* + High level API +*/ + +// hash_skein256_string will hash the given input and return the +// computed hash +hash_skein256_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte { + return hash_skein256_bytes(transmute([]byte)(data), bit_size, allocator) +} + +// hash_skein256_bytes will hash the given input and return the +// computed hash +hash_skein256_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte { + _create_skein256_ctx(bit_size) + return _hash_impl->hash_bytes_slice(data, bit_size, allocator) +} + +// hash_skein256_stream will read the stream in chunks and compute a +// hash from its contents +hash_skein256_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) { + _create_skein256_ctx(bit_size) + return _hash_impl->hash_stream_slice(s, bit_size, allocator) +} + +// hash_skein256_file will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_skein256_file :: proc(path: string, bit_size: int, load_at_once: bool, allocator := context.allocator) -> ([]byte, bool) { + _create_skein256_ctx(bit_size) + return _hash_impl->hash_file_slice(path, bit_size, load_at_once, allocator) +} + +hash_skein256 :: proc { + hash_skein256_stream, + hash_skein256_file, + hash_skein256_bytes, + hash_skein256_string, +} + +// hash_skein512_string will hash the given input and return the +// computed hash +hash_skein512_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte { + return hash_skein512_bytes(transmute([]byte)(data), bit_size, allocator) +} + +// hash_skein512_bytes will hash the given input and return the +// computed hash +hash_skein512_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte { + _create_skein512_ctx(bit_size) + return _hash_impl->hash_bytes_slice(data, bit_size, allocator) +} + +// hash_skein512_stream will read the stream in chunks and compute a +// hash from its contents +hash_skein512_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) { + _create_skein512_ctx(bit_size) + return _hash_impl->hash_stream_slice(s, bit_size, allocator) +} + +// hash_skein512_file will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_skein512_file :: proc(path: string, bit_size: int, load_at_once: bool, allocator := context.allocator) -> ([]byte, bool) { + _create_skein512_ctx(bit_size) + return _hash_impl->hash_file_slice(path, bit_size, load_at_once, allocator) +} + +hash_skein512 :: proc { + hash_skein512_stream, + hash_skein512_file, + hash_skein512_bytes, + hash_skein512_string, +} + +// hash_skein1024_string will hash the given input and return the +// computed hash +hash_skein1024_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte { + return hash_skein1024_bytes(transmute([]byte)(data), bit_size, allocator) +} + +// hash_skein1024_bytes will hash the given input and return the +// computed hash +hash_skein1024_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte { + _create_skein1024_ctx(bit_size) + return _hash_impl->hash_bytes_slice(data, bit_size, allocator) +} + +// hash_skein1024_stream will read the stream in chunks and compute a +// hash from its contents +hash_skein1024_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) { + _create_skein1024_ctx(bit_size) + return _hash_impl->hash_stream_slice(s, bit_size, allocator) +} + +// hash_skein1024_file will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_skein1024_file :: proc(path: string, bit_size: int, load_at_once: bool, allocator := context.allocator) -> ([]byte, bool) { + _create_skein1024_ctx(bit_size) + return _hash_impl->hash_file_slice(path, bit_size, load_at_once, allocator) +} + +hash_skein1024 :: proc { + hash_skein1024_stream, + hash_skein1024_file, + hash_skein1024_bytes, + hash_skein1024_string, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte { + hash := make([]byte, bit_size, allocator) + if c, ok := ctx.internal_ctx.(Skein256_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + return hash + } else { + delete(hash) + return nil + } +} + +hash_stream_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) { + hash := make([]byte, bit_size, allocator) + if c, ok := ctx.internal_ctx.(Skein256_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + delete(hash) + return nil, false + } +} + +hash_file_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, bit_size: int, load_at_once: bool, allocator := context.allocator) -> ([]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_skein256_odin(ctx, os.stream_from_handle(hd), bit_size, allocator) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_skein256_odin(ctx, buf[:], bit_size), read_ok + } + } + } + return nil, false +} + +hash_bytes_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte { + hash := make([]byte, bit_size, allocator) + if c, ok := ctx.internal_ctx.(Skein512_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + return hash + } else { + delete(hash) + return nil + } +} + +hash_stream_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) { + hash := make([]byte, bit_size, allocator) + if c, ok := ctx.internal_ctx.(Skein512_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + delete(hash) + return nil, false + } +} + +hash_file_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, bit_size: int, load_at_once: bool, allocator := context.allocator) -> ([]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_skein512_odin(ctx, os.stream_from_handle(hd), bit_size, allocator) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_skein512_odin(ctx, buf[:], bit_size), read_ok + } + } + } + return nil, false +} + +hash_bytes_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte { + hash := make([]byte, bit_size, allocator) + if c, ok := ctx.internal_ctx.(Skein1024_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + return hash + } else { + delete(hash) + return nil + } +} + +hash_stream_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) { + hash := make([]byte, bit_size, allocator) + if c, ok := ctx.internal_ctx.(Skein1024_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + delete(hash) + return nil, false + } +} + +hash_file_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, bit_size: int, load_at_once: bool, allocator := context.allocator) -> ([]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_skein1024_odin(ctx, os.stream_from_handle(hd), bit_size, allocator) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_skein1024_odin(ctx, buf[:], bit_size), read_ok + } + } + } + return nil, false +} + +@(private) +_init_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + _create_skein256_ctx(ctx.hash_size_val) + if c, ok := ctx.internal_ctx.(Skein256_Context); ok { + init_odin(&c) + } +} + +@(private) +_update_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(Skein256_Context); ok { + update_odin(&c, data) + } +} + +@(private) +_final_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(Skein256_Context); ok { + final_odin(&c, hash) + } +} + +@(private) +_init_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + _create_skein512_ctx(ctx.hash_size_val) + if c, ok := ctx.internal_ctx.(Skein512_Context); ok { + init_odin(&c) + } +} + +@(private) +_update_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(Skein512_Context); ok { + update_odin(&c, data) + } +} + +@(private) +_final_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(Skein512_Context); ok { + final_odin(&c, hash) + } +} + +@(private) +_init_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + _create_skein1024_ctx(ctx.hash_size_val) + if c, ok := ctx.internal_ctx.(Skein1024_Context); ok { + init_odin(&c) + } +} + +@(private) +_update_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(Skein1024_Context); ok { + update_odin(&c, data) + } +} + +@(private) +_final_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(Skein1024_Context); ok { + final_odin(&c, hash) + } +} + +/* + SKEIN implementation +*/ + +STATE_WORDS_256 :: 4 +STATE_WORDS_512 :: 8 +STATE_WORDS_1024 :: 16 + +STATE_BYTES_256 :: 32 +STATE_BYTES_512 :: 64 +STATE_BYTES_1024 :: 128 + +Skein_Header :: struct { + bit_length: u64, + bcnt: u64, + t: [2]u64, +} + +Skein256_Context :: struct { + h: Skein_Header, + x: [STATE_WORDS_256]u64, + b: [STATE_BYTES_256]byte, +} + +Skein512_Context :: struct { + h: Skein_Header, + x: [STATE_WORDS_512]u64, + b: [STATE_BYTES_512]byte, +} + +Skein1024_Context :: struct { + h: Skein_Header, + x: [STATE_WORDS_1024]u64, + b: [STATE_BYTES_1024]byte, +} + + +init_odin :: proc(ctx: ^$T) { + +} + +update_odin :: proc(ctx: ^$T, data: []byte) { + +} + +final_odin :: proc(ctx: ^$T, hash: []byte) { + +} \ No newline at end of file diff --git a/core/crypto/sm3/sm3.odin b/core/crypto/sm3/sm3.odin new file mode 100644 index 000000000..979c792f1 --- /dev/null +++ b/core/crypto/sm3/sm3.odin @@ -0,0 +1,336 @@ +package sm3 + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the SM3 hashing algorithm, as defined in +*/ + +import "core:os" +import "core:io" + +import "../util" +import "../botan" +import "../_ctx" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_32 = hash_bytes_odin + ctx.hash_file_32 = hash_file_odin + ctx.hash_stream_32 = hash_stream_odin + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan assigns the internal vtable of the hash context to use the Botan bindings +use_botan :: #force_inline proc() { + botan.assign_hash_vtable(_hash_impl, botan.HASH_SM3) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string will hash the given input and return the +// computed hash +hash_string :: proc(data: string) -> [32]byte { + return hash_bytes(transmute([]byte)(data)) +} + +// hash_bytes will hash the given input and return the +// computed hash +hash_bytes :: proc(data: []byte) -> [32]byte { + _create_sm3_ctx() + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream will read the stream in chunks and compute a +// hash from its contents +hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_sm3_ctx() + return _hash_impl->hash_stream_32(s) +} + +// hash_file will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_sm3_ctx() + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash :: proc { + hash_stream, + hash_file, + hash_bytes, + hash_string, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Sm3_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Sm3_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin(ctx, buf[:]), read_ok + } + } + } + return [32]byte{}, false +} + +@(private) +_create_sm3_ctx :: #force_inline proc() { + ctx: Sm3_Context + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = ._32 +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + _create_sm3_ctx() + if c, ok := ctx.internal_ctx.(Sm3_Context); ok { + init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(Sm3_Context); ok { + update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(Sm3_Context); ok { + final_odin(&c, hash) + } +} + +/* + SM3 implementation +*/ + +Sm3_Context :: struct { + state: [8]u32, + x: [64]byte, + bitlength: u64, + length: u64, +} + +BLOCK_SIZE_IN_BYTES :: 64 +BLOCK_SIZE_IN_32 :: 16 + +IV := [8]u32 { + 0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600, + 0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e, +} + +init_odin :: proc(ctx: ^Sm3_Context) { + ctx.state[0] = IV[0] + ctx.state[1] = IV[1] + ctx.state[2] = IV[2] + ctx.state[3] = IV[3] + ctx.state[4] = IV[4] + ctx.state[5] = IV[5] + ctx.state[6] = IV[6] + ctx.state[7] = IV[7] +} + +block :: proc "contextless" (ctx: ^Sm3_Context, buf: []byte) { + buf := buf + + w: [68]u32 + wp: [64]u32 + + state0, state1, state2, state3 := ctx.state[0], ctx.state[1], ctx.state[2], ctx.state[3] + state4, state5, state6, state7 := ctx.state[4], ctx.state[5], ctx.state[6], ctx.state[7] + + for len(buf) >= 64 { + for i := 0; i < 16; i += 1 { + j := i * 4 + w[i] = u32(buf[j]) << 24 | u32(buf[j + 1]) << 16 | u32(buf[j + 2]) << 8 | u32(buf[j + 3]) + } + for i := 16; i < 68; i += 1 { + p1v := w[i - 16] ~ w[i - 9] ~ util.ROTL32(w[i - 3], 15) + // @note(zh): inlined P1 + w[i] = p1v ~ util.ROTL32(p1v, 15) ~ util.ROTL32(p1v, 23) ~ util.ROTL32(w[i - 13], 7) ~ w[i - 6] + } + for i := 0; i < 64; i += 1 { + wp[i] = w[i] ~ w[i + 4] + } + + a, b, c, d := state0, state1, state2, state3 + e, f, g, h := state4, state5, state6, state7 + + for i := 0; i < 16; i += 1 { + v1 := util.ROTL32(u32(a), 12) + ss1 := util.ROTL32(v1 + u32(e) + util.ROTL32(0x79cc4519, i), 7) + ss2 := ss1 ~ v1 + + // @note(zh): inlined FF1 + tt1 := u32(a ~ b ~ c) + u32(d) + ss2 + wp[i] + // @note(zh): inlined GG1 + tt2 := u32(e ~ f ~ g) + u32(h) + ss1 + w[i] + + a, b, c, d = tt1, a, util.ROTL32(u32(b), 9), c + // @note(zh): inlined P0 + e, f, g, h = (tt2 ~ util.ROTL32(tt2, 9) ~ util.ROTL32(tt2, 17)), e, util.ROTL32(u32(f), 19), g + } + + for i := 16; i < 64; i += 1 { + v := util.ROTL32(u32(a), 12) + ss1 := util.ROTL32(v + u32(e) + util.ROTL32(0x7a879d8a, i % 32), 7) + ss2 := ss1 ~ v + + // @note(zh): inlined FF2 + tt1 := u32(((a & b) | (a & c) | (b & c)) + d) + ss2 + wp[i] + // @note(zh): inlined GG2 + tt2 := u32(((e & f) | ((~e) & g)) + h) + ss1 + w[i] + + a, b, c, d = tt1, a, util.ROTL32(u32(b), 9), c + // @note(zh): inlined P0 + e, f, g, h = (tt2 ~ util.ROTL32(tt2, 9) ~ util.ROTL32(tt2, 17)), e, util.ROTL32(u32(f), 19), g + } + + state0 ~= a + state1 ~= b + state2 ~= c + state3 ~= d + state4 ~= e + state5 ~= f + state6 ~= g + state7 ~= h + + buf = buf[64:] + } + + ctx.state[0], ctx.state[1], ctx.state[2], ctx.state[3] = state0, state1, state2, state3 + ctx.state[4], ctx.state[5], ctx.state[6], ctx.state[7] = state4, state5, state6, state7 +} + +update_odin :: proc(ctx: ^Sm3_Context, data: []byte) { + data := data + ctx.length += u64(len(data)) + + if ctx.bitlength > 0 { + n := copy(ctx.x[ctx.bitlength:], data[:]) + ctx.bitlength += u64(n) + if ctx.bitlength == 64 { + block(ctx, ctx.x[:]) + ctx.bitlength = 0 + } + data = data[n:] + } + if len(data) >= 64 { + n := len(data) &~ (64 - 1) + block(ctx, data[:n]) + data = data[n:] + } + if len(data) > 0 { + ctx.bitlength = u64(copy(ctx.x[:], data[:])) + } +} + +final_odin :: proc(ctx: ^Sm3_Context, hash: []byte) { + length := ctx.length + + pad: [64]byte + pad[0] = 0x80 + if length % 64 < 56 { + update_odin(ctx, pad[0: 56 - length % 64]) + } else { + update_odin(ctx, pad[0: 64 + 56 - length % 64]) + } + + length <<= 3 + util.PUT_U64_BE(pad[:], length) + update_odin(ctx, pad[0: 8]) + assert(ctx.bitlength == 0) + + util.PUT_U32_BE(hash[0:], ctx.state[0]) + util.PUT_U32_BE(hash[4:], ctx.state[1]) + util.PUT_U32_BE(hash[8:], ctx.state[2]) + util.PUT_U32_BE(hash[12:], ctx.state[3]) + util.PUT_U32_BE(hash[16:], ctx.state[4]) + util.PUT_U32_BE(hash[20:], ctx.state[5]) + util.PUT_U32_BE(hash[24:], ctx.state[6]) + util.PUT_U32_BE(hash[28:], ctx.state[7]) +} diff --git a/core/crypto/streebog/streebog.odin b/core/crypto/streebog/streebog.odin new file mode 100644 index 000000000..801e6dbc7 --- /dev/null +++ b/core/crypto/streebog/streebog.odin @@ -0,0 +1,602 @@ +package streebog + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the Streebog hashing algorithm, standardized as GOST R 34.11-2012 in RFC 6986 +*/ + +import "core:os" +import "core:io" + +import "../util" +import "../botan" +import "../_ctx" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_32 = hash_bytes_odin_32 + ctx.hash_file_32 = hash_file_odin_32 + ctx.hash_stream_32 = hash_stream_odin_32 + ctx.hash_bytes_64 = hash_bytes_odin_64 + ctx.hash_file_64 = hash_file_odin_64 + ctx.hash_stream_64 = hash_stream_odin_64 + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan assigns the internal vtable of the hash context to use the Botan bindings +use_botan :: #force_inline proc() { + botan.assign_hash_vtable(_hash_impl, botan.HASH_STREEBOG) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +@(private) +_create_streebog_ctx :: #force_inline proc(is256: bool) { + ctx: Streebog_Context + ctx.is256 = is256 + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = is256 ? ._32 : ._64 +} + +/* + High level API +*/ + +// hash_string_256 will hash the given input and return the +// computed hash +hash_string_256 :: proc(data: string) -> [32]byte { + return hash_bytes_256(transmute([]byte)(data)) +} + +// hash_bytes_256 will hash the given input and return the +// computed hash +hash_bytes_256 :: proc(data: []byte) -> [32]byte { + _create_streebog_ctx(true) + return _hash_impl->hash_bytes_32(data) +} + +// hash_stream_256 will read the stream in chunks and compute a +// hash from its contents +hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { + _create_streebog_ctx(true) + return _hash_impl->hash_stream_32(s) +} + +// hash_file_256 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_256 :: proc(path: string, load_at_once: bool) -> ([32]byte, bool) { + _create_streebog_ctx(true) + return _hash_impl->hash_file_32(path, load_at_once) +} + +hash_256 :: proc { + hash_stream_256, + hash_file_256, + hash_bytes_256, + hash_string_256, +} + +// hash_string_512 will hash the given input and return the +// computed hash +hash_string_512 :: proc(data: string) -> [64]byte { + return hash_bytes_512(transmute([]byte)(data)) +} + +// hash_bytes_512 will hash the given input and return the +// computed hash +hash_bytes_512 :: proc(data: []byte) -> [64]byte { + _create_streebog_ctx(false) + return _hash_impl->hash_bytes_64(data) +} + +// hash_stream_512 will read the stream in chunks and compute a +// hash from its contents +hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) { + _create_streebog_ctx(false) + return _hash_impl->hash_stream_64(s) +} + +// hash_file_512 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_512 :: proc(path: string, load_at_once: bool) -> ([64]byte, bool) { + _create_streebog_ctx(false) + return _hash_impl->hash_file_64(path, load_at_once) +} + +hash_512 :: proc { + hash_stream_512, + hash_file_512, + hash_bytes_512, + hash_string_512, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Streebog_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { + hash: [32]byte + if c, ok := ctx.internal_ctx.(Streebog_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([32]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_32(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_32(ctx, buf[:]), read_ok + } + } + } + return [32]byte{}, false +} + +hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte { + hash: [64]byte + if c, ok := ctx.internal_ctx.(Streebog_Context); ok { + init_odin(&c) + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) { + hash: [64]byte + if c, ok := ctx.internal_ctx.(Streebog_Context); ok { + init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([64]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_64(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_64(ctx, buf[:]), read_ok + } + } + } + return [64]byte{}, false +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + _create_streebog_ctx(ctx.hash_size == ._32) + if c, ok := ctx.internal_ctx.(Streebog_Context); ok { + init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(Streebog_Context); ok { + update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(Streebog_Context); ok { + final_odin(&c, hash) + } +} + +/* + Streebog implementation +*/ + +PI := [256]byte { + 252, 238, 221, 17, 207, 110, 49, 22, 251, 196, 250, 218, 35, 197, 4, 77, + 233, 119, 240, 219, 147, 46, 153, 186, 23, 54, 241, 187, 20, 205, 95, 193, + 249, 24, 101, 90, 226, 92, 239, 33, 129, 28, 60, 66, 139, 1, 142, 79, + 5, 132, 2, 174, 227, 106, 143, 160, 6, 11, 237, 152, 127, 212, 211, 31, + 235, 52, 44, 81, 234, 200, 72, 171, 242, 42, 104, 162, 253, 58, 206, 204, + 181, 112, 14, 86, 8, 12, 118, 18, 191, 114, 19, 71, 156, 183, 93, 135, + 21, 161, 150, 41, 16, 123, 154, 199, 243, 145, 120, 111, 157, 158, 178, 177, + 50, 117, 25, 61, 255, 53, 138, 126, 109, 84, 198, 128, 195, 189, 13, 87, + 223, 245, 36, 169, 62, 168, 67, 201, 215, 121, 214, 246, 124, 34, 185, 3, + 224, 15, 236, 222, 122, 148, 176, 188, 220, 232, 40, 80, 78, 51, 10, 74, + 167, 151, 96, 115, 30, 0, 98, 68, 26, 184, 56, 130, 100, 159, 38, 65, + 173, 69, 70, 146, 39, 94, 85, 47, 140, 163, 165, 125, 105, 213, 149, 59, + 7, 88, 179, 64, 134, 172, 29, 247, 48, 55, 107, 228, 136, 217, 231, 137, + 225, 27, 131, 73, 76, 63, 248, 254, 141, 83, 170, 144, 202, 216, 133, 97, + 32, 113, 103, 164, 45, 43, 9, 91, 203, 155, 37, 208, 190, 229, 108, 82, + 89, 166, 116, 210, 230, 244, 180, 192, 209, 102, 175, 194, 57, 75, 99, 182, +} + +TAU := [64]byte { + 0, 8, 16, 24, 32, 40, 48, 56, + 1, 9, 17, 25, 33, 41, 49, 57, + 2, 10, 18, 26, 34, 42, 50, 58, + 3, 11, 19, 27, 35, 43, 51, 59, + 4, 12, 20, 28, 36, 44, 52, 60, + 5, 13, 21, 29, 37, 45, 53, 61, + 6, 14, 22, 30, 38, 46, 54, 62, + 7, 15, 23, 31, 39, 47, 55, 63, +} + +STREEBOG_A := [64]u64 { + 0x8e20faa72ba0b470, 0x47107ddd9b505a38, 0xad08b0e0c3282d1c, 0xd8045870ef14980e, + 0x6c022c38f90a4c07, 0x3601161cf205268d, 0x1b8e0b0e798c13c8, 0x83478b07b2468764, + 0xa011d380818e8f40, 0x5086e740ce47c920, 0x2843fd2067adea10, 0x14aff010bdd87508, + 0x0ad97808d06cb404, 0x05e23c0468365a02, 0x8c711e02341b2d01, 0x46b60f011a83988e, + 0x90dab52a387ae76f, 0x486dd4151c3dfdb9, 0x24b86a840e90f0d2, 0x125c354207487869, + 0x092e94218d243cba, 0x8a174a9ec8121e5d, 0x4585254f64090fa0, 0xaccc9ca9328a8950, + 0x9d4df05d5f661451, 0xc0a878a0a1330aa6, 0x60543c50de970553, 0x302a1e286fc58ca7, + 0x18150f14b9ec46dd, 0x0c84890ad27623e0, 0x0642ca05693b9f70, 0x0321658cba93c138, + 0x86275df09ce8aaa8, 0x439da0784e745554, 0xafc0503c273aa42a, 0xd960281e9d1d5215, + 0xe230140fc0802984, 0x71180a8960409a42, 0xb60c05ca30204d21, 0x5b068c651810a89e, + 0x456c34887a3805b9, 0xac361a443d1c8cd2, 0x561b0d22900e4669, 0x2b838811480723ba, + 0x9bcf4486248d9f5d, 0xc3e9224312c8c1a0, 0xeffa11af0964ee50, 0xf97d86d98a327728, + 0xe4fa2054a80b329c, 0x727d102a548b194e, 0x39b008152acb8227, 0x9258048415eb419d, + 0x492c024284fbaec0, 0xaa16012142f35760, 0x550b8e9e21f7a530, 0xa48b474f9ef5dc18, + 0x70a6a56e2440598e, 0x3853dc371220a247, 0x1ca76e95091051ad, 0x0edd37c48a08a6d8, + 0x07e095624504536c, 0x8d70c431ac02a736, 0xc83862965601dd1b, 0x641c314b2b8ee083, +} + +STREEBOG_C := [12][64]byte { + { + 0x07, 0x45, 0xa6, 0xf2, 0x59, 0x65, 0x80, 0xdd, + 0x23, 0x4d, 0x74, 0xcc, 0x36, 0x74, 0x76, 0x05, + 0x15, 0xd3, 0x60, 0xa4, 0x08, 0x2a, 0x42, 0xa2, + 0x01, 0x69, 0x67, 0x92, 0x91, 0xe0, 0x7c, 0x4b, + 0xfc, 0xc4, 0x85, 0x75, 0x8d, 0xb8, 0x4e, 0x71, + 0x16, 0xd0, 0x45, 0x2e, 0x43, 0x76, 0x6a, 0x2f, + 0x1f, 0x7c, 0x65, 0xc0, 0x81, 0x2f, 0xcb, 0xeb, + 0xe9, 0xda, 0xca, 0x1e, 0xda, 0x5b, 0x08, 0xb1, + }, + { + 0xb7, 0x9b, 0xb1, 0x21, 0x70, 0x04, 0x79, 0xe6, + 0x56, 0xcd, 0xcb, 0xd7, 0x1b, 0xa2, 0xdd, 0x55, + 0xca, 0xa7, 0x0a, 0xdb, 0xc2, 0x61, 0xb5, 0x5c, + 0x58, 0x99, 0xd6, 0x12, 0x6b, 0x17, 0xb5, 0x9a, + 0x31, 0x01, 0xb5, 0x16, 0x0f, 0x5e, 0xd5, 0x61, + 0x98, 0x2b, 0x23, 0x0a, 0x72, 0xea, 0xfe, 0xf3, + 0xd7, 0xb5, 0x70, 0x0f, 0x46, 0x9d, 0xe3, 0x4f, + 0x1a, 0x2f, 0x9d, 0xa9, 0x8a, 0xb5, 0xa3, 0x6f, + }, + { + 0xb2, 0x0a, 0xba, 0x0a, 0xf5, 0x96, 0x1e, 0x99, + 0x31, 0xdb, 0x7a, 0x86, 0x43, 0xf4, 0xb6, 0xc2, + 0x09, 0xdb, 0x62, 0x60, 0x37, 0x3a, 0xc9, 0xc1, + 0xb1, 0x9e, 0x35, 0x90, 0xe4, 0x0f, 0xe2, 0xd3, + 0x7b, 0x7b, 0x29, 0xb1, 0x14, 0x75, 0xea, 0xf2, + 0x8b, 0x1f, 0x9c, 0x52, 0x5f, 0x5e, 0xf1, 0x06, + 0x35, 0x84, 0x3d, 0x6a, 0x28, 0xfc, 0x39, 0x0a, + 0xc7, 0x2f, 0xce, 0x2b, 0xac, 0xdc, 0x74, 0xf5, + }, + { + 0x2e, 0xd1, 0xe3, 0x84, 0xbc, 0xbe, 0x0c, 0x22, + 0xf1, 0x37, 0xe8, 0x93, 0xa1, 0xea, 0x53, 0x34, + 0xbe, 0x03, 0x52, 0x93, 0x33, 0x13, 0xb7, 0xd8, + 0x75, 0xd6, 0x03, 0xed, 0x82, 0x2c, 0xd7, 0xa9, + 0x3f, 0x35, 0x5e, 0x68, 0xad, 0x1c, 0x72, 0x9d, + 0x7d, 0x3c, 0x5c, 0x33, 0x7e, 0x85, 0x8e, 0x48, + 0xdd, 0xe4, 0x71, 0x5d, 0xa0, 0xe1, 0x48, 0xf9, + 0xd2, 0x66, 0x15, 0xe8, 0xb3, 0xdf, 0x1f, 0xef, + }, + { + 0x57, 0xfe, 0x6c, 0x7c, 0xfd, 0x58, 0x17, 0x60, + 0xf5, 0x63, 0xea, 0xa9, 0x7e, 0xa2, 0x56, 0x7a, + 0x16, 0x1a, 0x27, 0x23, 0xb7, 0x00, 0xff, 0xdf, + 0xa3, 0xf5, 0x3a, 0x25, 0x47, 0x17, 0xcd, 0xbf, + 0xbd, 0xff, 0x0f, 0x80, 0xd7, 0x35, 0x9e, 0x35, + 0x4a, 0x10, 0x86, 0x16, 0x1f, 0x1c, 0x15, 0x7f, + 0x63, 0x23, 0xa9, 0x6c, 0x0c, 0x41, 0x3f, 0x9a, + 0x99, 0x47, 0x47, 0xad, 0xac, 0x6b, 0xea, 0x4b, + }, + { + 0x6e, 0x7d, 0x64, 0x46, 0x7a, 0x40, 0x68, 0xfa, + 0x35, 0x4f, 0x90, 0x36, 0x72, 0xc5, 0x71, 0xbf, + 0xb6, 0xc6, 0xbe, 0xc2, 0x66, 0x1f, 0xf2, 0x0a, + 0xb4, 0xb7, 0x9a, 0x1c, 0xb7, 0xa6, 0xfa, 0xcf, + 0xc6, 0x8e, 0xf0, 0x9a, 0xb4, 0x9a, 0x7f, 0x18, + 0x6c, 0xa4, 0x42, 0x51, 0xf9, 0xc4, 0x66, 0x2d, + 0xc0, 0x39, 0x30, 0x7a, 0x3b, 0xc3, 0xa4, 0x6f, + 0xd9, 0xd3, 0x3a, 0x1d, 0xae, 0xae, 0x4f, 0xae, + }, + { + 0x93, 0xd4, 0x14, 0x3a, 0x4d, 0x56, 0x86, 0x88, + 0xf3, 0x4a, 0x3c, 0xa2, 0x4c, 0x45, 0x17, 0x35, + 0x04, 0x05, 0x4a, 0x28, 0x83, 0x69, 0x47, 0x06, + 0x37, 0x2c, 0x82, 0x2d, 0xc5, 0xab, 0x92, 0x09, + 0xc9, 0x93, 0x7a, 0x19, 0x33, 0x3e, 0x47, 0xd3, + 0xc9, 0x87, 0xbf, 0xe6, 0xc7, 0xc6, 0x9e, 0x39, + 0x54, 0x09, 0x24, 0xbf, 0xfe, 0x86, 0xac, 0x51, + 0xec, 0xc5, 0xaa, 0xee, 0x16, 0x0e, 0xc7, 0xf4, + }, + { + 0x1e, 0xe7, 0x02, 0xbf, 0xd4, 0x0d, 0x7f, 0xa4, + 0xd9, 0xa8, 0x51, 0x59, 0x35, 0xc2, 0xac, 0x36, + 0x2f, 0xc4, 0xa5, 0xd1, 0x2b, 0x8d, 0xd1, 0x69, + 0x90, 0x06, 0x9b, 0x92, 0xcb, 0x2b, 0x89, 0xf4, + 0x9a, 0xc4, 0xdb, 0x4d, 0x3b, 0x44, 0xb4, 0x89, + 0x1e, 0xde, 0x36, 0x9c, 0x71, 0xf8, 0xb7, 0x4e, + 0x41, 0x41, 0x6e, 0x0c, 0x02, 0xaa, 0xe7, 0x03, + 0xa7, 0xc9, 0x93, 0x4d, 0x42, 0x5b, 0x1f, 0x9b, + }, + { + 0xdb, 0x5a, 0x23, 0x83, 0x51, 0x44, 0x61, 0x72, + 0x60, 0x2a, 0x1f, 0xcb, 0x92, 0xdc, 0x38, 0x0e, + 0x54, 0x9c, 0x07, 0xa6, 0x9a, 0x8a, 0x2b, 0x7b, + 0xb1, 0xce, 0xb2, 0xdb, 0x0b, 0x44, 0x0a, 0x80, + 0x84, 0x09, 0x0d, 0xe0, 0xb7, 0x55, 0xd9, 0x3c, + 0x24, 0x42, 0x89, 0x25, 0x1b, 0x3a, 0x7d, 0x3a, + 0xde, 0x5f, 0x16, 0xec, 0xd8, 0x9a, 0x4c, 0x94, + 0x9b, 0x22, 0x31, 0x16, 0x54, 0x5a, 0x8f, 0x37, + }, + { + 0xed, 0x9c, 0x45, 0x98, 0xfb, 0xc7, 0xb4, 0x74, + 0xc3, 0xb6, 0x3b, 0x15, 0xd1, 0xfa, 0x98, 0x36, + 0xf4, 0x52, 0x76, 0x3b, 0x30, 0x6c, 0x1e, 0x7a, + 0x4b, 0x33, 0x69, 0xaf, 0x02, 0x67, 0xe7, 0x9f, + 0x03, 0x61, 0x33, 0x1b, 0x8a, 0xe1, 0xff, 0x1f, + 0xdb, 0x78, 0x8a, 0xff, 0x1c, 0xe7, 0x41, 0x89, + 0xf3, 0xf3, 0xe4, 0xb2, 0x48, 0xe5, 0x2a, 0x38, + 0x52, 0x6f, 0x05, 0x80, 0xa6, 0xde, 0xbe, 0xab, + }, + { + 0x1b, 0x2d, 0xf3, 0x81, 0xcd, 0xa4, 0xca, 0x6b, + 0x5d, 0xd8, 0x6f, 0xc0, 0x4a, 0x59, 0xa2, 0xde, + 0x98, 0x6e, 0x47, 0x7d, 0x1d, 0xcd, 0xba, 0xef, + 0xca, 0xb9, 0x48, 0xea, 0xef, 0x71, 0x1d, 0x8a, + 0x79, 0x66, 0x84, 0x14, 0x21, 0x80, 0x01, 0x20, + 0x61, 0x07, 0xab, 0xeb, 0xbb, 0x6b, 0xfa, 0xd8, + 0x94, 0xfe, 0x5a, 0x63, 0xcd, 0xc6, 0x02, 0x30, + 0xfb, 0x89, 0xc8, 0xef, 0xd0, 0x9e, 0xcd, 0x7b, + }, + { + 0x20, 0xd7, 0x1b, 0xf1, 0x4a, 0x92, 0xbc, 0x48, + 0x99, 0x1b, 0xb2, 0xd9, 0xd5, 0x17, 0xf4, 0xfa, + 0x52, 0x28, 0xe1, 0x88, 0xaa, 0xa4, 0x1d, 0xe7, + 0x86, 0xcc, 0x91, 0x18, 0x9d, 0xef, 0x80, 0x5d, + 0x9b, 0x9f, 0x21, 0x30, 0xd4, 0x12, 0x20, 0xf8, + 0x77, 0x1d, 0xdf, 0xbc, 0x32, 0x3c, 0xa4, 0xcd, + 0x7a, 0xb1, 0x49, 0x04, 0xb0, 0x80, 0x13, 0xd2, + 0xba, 0x31, 0x16, 0xf1, 0x67, 0xe7, 0x8e, 0x37, + }, +} + +Streebog_Context :: struct { + buffer: [64]byte, + h: [64]byte, + n: [64]byte, + sigma: [64]byte, + v_0: [64]byte, + v_512: [64]byte, + buf_size: u64, + hash_size: int, + is256: bool, +} + +add_mod_512 :: proc(first_vector, second_vector, result_vector: []byte) { + t: i32 = 0 + for i: i32 = 0; i < 64; i += 1 { + t = i32(first_vector[i]) + i32(second_vector[i]) + (t >> 8) + result_vector[i] = byte(t & 0xff) + } +} + +X :: #force_inline proc(a, k, out: []byte) { + for i := 0; i < 64; i += 1 { + out[i] = a[i] ~ k[i] + } +} + +S :: #force_inline proc(state: []byte) { + t: [64]byte + for i: i32 = 63; i >= 0; i -= 1 { + t[i] = PI[state[i]] + } + copy(state, t[:]) +} + +P :: #force_inline proc(state: []byte) { + t: [64]byte + for i: i32 = 63; i >= 0; i -= 1 { + t[i] = state[TAU[i]] + } + copy(state, t[:]) +} + +L :: #force_inline proc(state: []byte) { + ins := util.cast_slice([]u64, state) + out: [8]u64 + for i: i32 = 7; i >= 0; i -= 1 { + for j: i32 = 63; j >= 0; j -= 1 { + if (ins[i] >> u32(j)) & 1 != 0 { + out[i] ~= STREEBOG_A[63 - j] + } + } + } + copy(state, util.cast_slice([]byte, out[:])) +} + +E :: #force_inline proc(K, m, state: []byte) { + X(m, K, state) + for i: i32 = 0; i < 12; i += 1 { + S(state) + P(state) + L(state) + get_key(K, i) + X(state, K, state) + } +} + +get_key :: #force_inline proc(K: []byte, i: i32) { + X(K, STREEBOG_C[i][:], K) + S(K) + P(K) + L(K) +} + +G :: #force_inline proc(h, N, m: []byte) { + t, K: [64]byte + X(N, h, K[:]) + S(K[:]) + P(K[:]) + L(K[:]) + E(K[:], m, t[:]) + X(t[:], h, t[:]) + X(t[:], m, h) +} + +stage2 :: proc(ctx: ^Streebog_Context, m: []byte) { + G(ctx.h[:], ctx.n[:], m) + add_mod_512(ctx.n[:], ctx.v_512[:], ctx.n[:]) + add_mod_512(ctx.sigma[:], m, ctx.sigma[:]) +} + +padding :: proc(ctx: ^Streebog_Context) { + if ctx.buf_size < 64 { + t: [64]byte + copy(t[:], ctx.buffer[:int(ctx.buf_size)]) + t[ctx.buf_size] = 0x01 + copy(ctx.buffer[:], t[:]) + } +} + +init_odin :: proc(ctx: ^Streebog_Context) { + if ctx.is256 { + ctx.hash_size = 256 + for _, i in ctx.h { + ctx.h[i] = 0x01 + } + } else { + ctx.hash_size = 512 + } + ctx.v_512[1] = 0x02 +} + +update_odin :: proc(ctx: ^Streebog_Context, data: []byte) { + length := u64(len(data)) + chk_size: u64 + data := data + for (length > 63) && (ctx.buf_size == 0) { + stage2(ctx, data) + data = data[64:] + length -= 64 + } + + for length != 0 { + chk_size = 64 - ctx.buf_size + if chk_size > length { + chk_size = length + } + copy(ctx.buffer[ctx.buf_size:], data[:chk_size]) + ctx.buf_size += chk_size + length -= chk_size + data = data[chk_size:] + if ctx.buf_size == 64 { + stage2(ctx, ctx.buffer[:]) + ctx.buf_size = 0 + } + } +} + +final_odin :: proc(ctx: ^Streebog_Context, hash: []byte) { + t: [64]byte + t[1] = byte((ctx.buf_size * 8) >> 8) & 0xff + t[0] = byte((ctx.buf_size) * 8) & 0xff + + padding(ctx) + + G(ctx.h[:], ctx.n[:], ctx.buffer[:]) + + add_mod_512(ctx.n[:], t[:], ctx.n[:]) + add_mod_512(ctx.sigma[:], ctx.buffer[:], ctx.sigma[:]) + + G(ctx.h[:], ctx.v_0[:], ctx.n[:]) + G(ctx.h[:], ctx.v_0[:], ctx.sigma[:]) + + if ctx.is256 { + copy(hash[:], ctx.h[32:]) + } else { + copy(hash[:], ctx.h[:]) + } +} \ No newline at end of file diff --git a/core/crypto/tiger/tiger.odin b/core/crypto/tiger/tiger.odin new file mode 100644 index 000000000..bcc1a4a31 --- /dev/null +++ b/core/crypto/tiger/tiger.odin @@ -0,0 +1,340 @@ +package tiger + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Interface for the Tiger1 variant of the Tiger hashing algorithm as defined in +*/ + +import "core:os" +import "core:io" + +import "../botan" +import "../_ctx" +import "../_tiger" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_16 = hash_bytes_odin_16 + ctx.hash_file_16 = hash_file_odin_16 + ctx.hash_stream_16 = hash_stream_odin_16 + ctx.hash_bytes_20 = hash_bytes_odin_20 + ctx.hash_file_20 = hash_file_odin_20 + ctx.hash_stream_20 = hash_stream_odin_20 + ctx.hash_bytes_24 = hash_bytes_odin_24 + ctx.hash_file_24 = hash_file_odin_24 + ctx.hash_stream_24 = hash_stream_odin_24 + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan assigns the internal vtable of the hash context to use the Botan bindings +use_botan :: #force_inline proc() { + botan.assign_hash_vtable(_hash_impl, botan.HASH_TIGER) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string_128 will hash the given input and return the +// computed hash +hash_string_128 :: proc(data: string) -> [16]byte { + return hash_bytes_128(transmute([]byte)(data)) +} + +// hash_bytes_128 will hash the given input and return the +// computed hash +hash_bytes_128 :: proc(data: []byte) -> [16]byte { + _create_ripemd_ctx(16) + return _hash_impl->hash_bytes_16(data) +} + +// hash_stream_128 will read the stream in chunks and compute a +// hash from its contents +hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) { + _create_ripemd_ctx(16) + return _hash_impl->hash_stream_16(s) +} + +// hash_file_128 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_128 :: proc(path: string, load_at_once: bool) -> ([16]byte, bool) { + _create_ripemd_ctx(16) + return _hash_impl->hash_file_16(path, load_at_once) +} + +hash_128 :: proc { + hash_stream_128, + hash_file_128, + hash_bytes_128, + hash_string_128, +} + +// hash_string_160 will hash the given input and return the +// computed hash +hash_string_160 :: proc(data: string) -> [20]byte { + return hash_bytes_160(transmute([]byte)(data)) +} + +// hash_bytes_160 will hash the given input and return the +// computed hash +hash_bytes_160 :: proc(data: []byte) -> [20]byte { + _create_ripemd_ctx(20) + return _hash_impl->hash_bytes_20(data) +} + +// hash_stream_160 will read the stream in chunks and compute a +// hash from its contents +hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) { + _create_ripemd_ctx(20) + return _hash_impl->hash_stream_20(s) +} + +// hash_file_160 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_160 :: proc(path: string, load_at_once: bool) -> ([20]byte, bool) { + _create_ripemd_ctx(20) + return _hash_impl->hash_file_20(path, load_at_once) +} + +hash_160 :: proc { + hash_stream_160, + hash_file_160, + hash_bytes_160, + hash_string_160, +} + +// hash_string_192 will hash the given input and return the +// computed hash +hash_string_192 :: proc(data: string) -> [24]byte { + return hash_bytes_192(transmute([]byte)(data)) +} + +// hash_bytes_192 will hash the given input and return the +// computed hash +hash_bytes_192 :: proc(data: []byte) -> [24]byte { + _create_ripemd_ctx(24) + return _hash_impl->hash_bytes_24(data) +} + +// hash_stream_192 will read the stream in chunks and compute a +// hash from its contents +hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) { + _create_ripemd_ctx(24) + return _hash_impl->hash_stream_24(s) +} + +// hash_file_192 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_192 :: proc(path: string, load_at_once: bool) -> ([24]byte, bool) { + _create_ripemd_ctx(24) + return _hash_impl->hash_file_24(path, load_at_once) +} + +hash_192 :: proc { + hash_stream_192, + hash_file_192, + hash_bytes_192, + hash_string_192, +} + +hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte { + hash: [16]byte + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.init_odin(&c) + _tiger.update_odin(&c, data) + _tiger.final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) { + hash: [16]byte + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _tiger.update_odin(&c, buf[:read]) + } + } + _tiger.final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([16]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_16(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_16(ctx, buf[:]), read_ok + } + } + } + return [16]byte{}, false +} + +hash_bytes_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte { + hash: [20]byte + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.init_odin(&c) + _tiger.update_odin(&c, data) + _tiger.final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) { + hash: [20]byte + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _tiger.update_odin(&c, buf[:read]) + } + } + _tiger.final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([20]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_20(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_20(ctx, buf[:]), read_ok + } + } + } + return [20]byte{}, false +} + +hash_bytes_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [24]byte { + hash: [24]byte + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.init_odin(&c) + _tiger.update_odin(&c, data) + _tiger.final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([24]byte, bool) { + hash: [24]byte + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _tiger.update_odin(&c, buf[:read]) + } + } + _tiger.final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([24]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_24(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_24(ctx, buf[:]), read_ok + } + } + } + return [24]byte{}, false +} + +@(private) +_create_ripemd_ctx :: #force_inline proc(hash_size: int) { + ctx: _tiger.Tiger_Context + ctx.ver = 1 + _hash_impl.internal_ctx = ctx + switch hash_size { + case 16: _hash_impl.hash_size = ._16 + case 20: _hash_impl.hash_size = ._20 + case 24: _hash_impl.hash_size = ._24 + } +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + #partial switch ctx.hash_size { + case ._16: _create_ripemd_ctx(16) + case ._20: _create_ripemd_ctx(20) + case ._24: _create_ripemd_ctx(24) + } + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.final_odin(&c, hash) + } +} diff --git a/core/crypto/tiger2/tiger2.odin b/core/crypto/tiger2/tiger2.odin new file mode 100644 index 000000000..46ec25b2d --- /dev/null +++ b/core/crypto/tiger2/tiger2.odin @@ -0,0 +1,340 @@ +package tiger2 + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Interface for the Tiger2 variant of the Tiger hashing algorithm as defined in +*/ + +import "core:os" +import "core:io" + +import "../_ctx" +import "../_tiger" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_16 = hash_bytes_odin_16 + ctx.hash_file_16 = hash_file_odin_16 + ctx.hash_stream_16 = hash_stream_odin_16 + ctx.hash_bytes_20 = hash_bytes_odin_20 + ctx.hash_file_20 = hash_file_odin_20 + ctx.hash_stream_20 = hash_stream_odin_20 + ctx.hash_bytes_24 = hash_bytes_odin_24 + ctx.hash_file_24 = hash_file_odin_24 + ctx.hash_stream_24 = hash_stream_odin_24 + ctx.init = _init_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan does nothing, since Tiger2 is not available in Botan +@(warning="Tiger2 is not provided by the Botan API. Odin implementation will be used") +use_botan :: #force_inline proc() { + use_odin() +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string_128 will hash the given input and return the +// computed hash +hash_string_128 :: proc(data: string) -> [16]byte { + return hash_bytes_128(transmute([]byte)(data)) +} + +// hash_bytes_128 will hash the given input and return the +// computed hash +hash_bytes_128 :: proc(data: []byte) -> [16]byte { + _create_ripemd_ctx(16) + return _hash_impl->hash_bytes_16(data) +} + +// hash_stream_128 will read the stream in chunks and compute a +// hash from its contents +hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) { + _create_ripemd_ctx(16) + return _hash_impl->hash_stream_16(s) +} + +// hash_file_128 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_128 :: proc(path: string, load_at_once: bool) -> ([16]byte, bool) { + _create_ripemd_ctx(16) + return _hash_impl->hash_file_16(path, load_at_once) +} + +hash_128 :: proc { + hash_stream_128, + hash_file_128, + hash_bytes_128, + hash_string_128, +} + +// hash_string_160 will hash the given input and return the +// computed hash +hash_string_160 :: proc(data: string) -> [20]byte { + return hash_bytes_160(transmute([]byte)(data)) +} + +// hash_bytes_160 will hash the given input and return the +// computed hash +hash_bytes_160 :: proc(data: []byte) -> [20]byte { + _create_ripemd_ctx(20) + return _hash_impl->hash_bytes_20(data) +} + +// hash_stream_160 will read the stream in chunks and compute a +// hash from its contents +hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) { + _create_ripemd_ctx(20) + return _hash_impl->hash_stream_20(s) +} + +// hash_file_160 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_160 :: proc(path: string, load_at_once: bool) -> ([20]byte, bool) { + _create_ripemd_ctx(20) + return _hash_impl->hash_file_20(path, load_at_once) +} + +hash_160 :: proc { + hash_stream_160, + hash_file_160, + hash_bytes_160, + hash_string_160, +} + +// hash_string_192 will hash the given input and return the +// computed hash +hash_string_192 :: proc(data: string) -> [24]byte { + return hash_bytes_192(transmute([]byte)(data)) +} + +// hash_bytes_192 will hash the given input and return the +// computed hash +hash_bytes_192 :: proc(data: []byte) -> [24]byte { + _create_ripemd_ctx(24) + return _hash_impl->hash_bytes_24(data) +} + +// hash_stream_192 will read the stream in chunks and compute a +// hash from its contents +hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) { + _create_ripemd_ctx(24) + return _hash_impl->hash_stream_24(s) +} + +// hash_file_192 will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file_192 :: proc(path: string, load_at_once: bool) -> ([24]byte, bool) { + _create_ripemd_ctx(24) + return _hash_impl->hash_file_24(path, load_at_once) +} + +hash_192 :: proc { + hash_stream_192, + hash_file_192, + hash_bytes_192, + hash_string_192, +} + +hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte { + hash: [16]byte + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.init_odin(&c) + _tiger.update_odin(&c, data) + _tiger.final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) { + hash: [16]byte + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _tiger.update_odin(&c, buf[:read]) + } + } + _tiger.final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([16]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_16(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_16(ctx, buf[:]), read_ok + } + } + } + return [16]byte{}, false +} + +hash_bytes_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte { + hash: [20]byte + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.init_odin(&c) + _tiger.update_odin(&c, data) + _tiger.final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) { + hash: [20]byte + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _tiger.update_odin(&c, buf[:read]) + } + } + _tiger.final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([20]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_20(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_20(ctx, buf[:]), read_ok + } + } + } + return [20]byte{}, false +} + +hash_bytes_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [24]byte { + hash: [24]byte + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.init_odin(&c) + _tiger.update_odin(&c, data) + _tiger.final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([24]byte, bool) { + hash: [24]byte + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.init_odin(&c) + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + _tiger.update_odin(&c, buf[:read]) + } + } + _tiger.final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([24]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin_24(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin_24(ctx, buf[:]), read_ok + } + } + } + return [24]byte{}, false +} + +@(private) +_create_ripemd_ctx :: #force_inline proc(hash_size: int) { + ctx: _tiger.Tiger_Context + ctx.ver = 2 + _hash_impl.internal_ctx = ctx + switch hash_size { + case 16: _hash_impl.hash_size = ._16 + case 20: _hash_impl.hash_size = ._20 + case 24: _hash_impl.hash_size = ._24 + } +} + +@(private) +_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + #partial switch ctx.hash_size { + case ._16: _create_ripemd_ctx(16) + case ._20: _create_ripemd_ctx(20) + case ._24: _create_ripemd_ctx(24) + } + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.init_odin(&c) + } +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok { + _tiger.final_odin(&c, hash) + } +} \ No newline at end of file diff --git a/core/crypto/util/util.odin b/core/crypto/util/util.odin new file mode 100644 index 000000000..ce0e9a164 --- /dev/null +++ b/core/crypto/util/util.odin @@ -0,0 +1,144 @@ +package util + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + + Various utility procedures +*/ + +import "core:mem" + +// @note(bp): this can replace the other two +cast_slice :: #force_inline proc "contextless" ($D: typeid/[]$DE, src: $S/[]$SE) -> D { + src := src + dst := (^mem.Raw_Slice)(&src) + + when size_of(DE) < size_of(SE) { + when size_of(DE) % size_of(SE) == 0 { + dst.len /= size_of(SE) / size_of(DE) + } else { + dst.len *= size_of(SE) + dst.len /= size_of(DE) + } + } else when size_of(DE) > size_of(SE) { + when size_of(DE) % size_of(SE) == 0 { + dst.len *= size_of(DE) / size_of(SE) + } else { + dst.len *= size_of(SE) + dst.len /= size_of(DE) + } + } else when size_of(DE) != size_of(SE) { + #assert(size_of(DE) % size_of(SE) == 0, "Different size detected") + dst.len *= size_of(SE) + dst.len /= size_of(DE) + } + + return (^D)(dst)^ +} + +bytes_to_slice :: #force_inline proc "contextless" ($T: typeid/[]$E, bytes: []byte) -> T { + s := transmute(mem.Raw_Slice)bytes + s.len /= size_of(E) + return transmute(T)s +} + +slice_to_bytes :: #force_inline proc "contextless" (slice: $E/[]$T) -> []byte { + s := transmute(mem.Raw_Slice)slice + s.len *= size_of(T) + return transmute([]byte)s +} + +ROTL16 :: #force_inline proc "contextless" (a, b: u16) -> u16 { + return ((a << b) | (a >> (16 - b))) +} + +ROTR16 :: #force_inline proc "contextless" (a, b: u16) -> u16 { + return ((a >> b) | (a << (16 - b))) +} + +ROTL32 :: #force_inline proc "contextless"(a: u32, b: int) -> u32 { + s := uint(b) & 31 + return (a << s) | (a >> (32 - s)) +} + +ROTR32 :: #force_inline proc "contextless" (a: u32, b: int) -> u32 { + s := uint(b) & 31 + return (a >> s) | (a << (32 - s)) +} + +ROTL64 :: #force_inline proc "contextless" (a, b: u64) -> u64 { + return ((a << b) | (a >> (64 - b))) +} + +ROTR64 :: #force_inline proc "contextless" (a, b: u64) -> u64 { + return ((a >> b) | (a << (64 - b))) +} + +ROTL128 :: #force_inline proc "contextless" (a, b, c, d: ^u32, n: uint) { + a, b, c, d := a, b, c, d + t := a^ >> (32 - n) + a^ = ((a^ << n) | (b^ >> (32 - n))) + b^ = ((b^ << n) | (c^ >> (32 - n))) + c^ = ((c^ << n) | (d^ >> (32 - n))) + d^ = ((d^ << n) | t) +} + +U32_LE :: #force_inline proc "contextless" (b: []byte) -> u32 { + return u32(b[0]) | u32(b[1]) << 8 | u32(b[2]) << 16 | u32(b[3]) << 24 +} + +U64_LE :: #force_inline proc "contextless" (b: []byte) -> u64 { + return u64(b[0]) | u64(b[1]) << 8 | u64(b[2]) << 16 | u64(b[3]) << 24 | + u64(b[4]) << 32 | u64(b[5]) << 40 | u64(b[6]) << 48 | u64(b[7]) << 56 +} + +U64_BE :: #force_inline proc "contextless" (b: []byte) -> u64 { + return u64(b[7]) | u64(b[6]) << 8 | u64(b[5]) << 16 | u64(b[4]) << 24 | + u64(b[3]) << 32 | u64(b[2]) << 40 | u64(b[1]) << 48 | u64(b[0]) << 56 +} + +PUT_U64_LE :: #force_inline proc "contextless" (b: []byte, v: u64) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) +} + +PUT_U32_LE :: #force_inline proc "contextless" (b: []byte, v: u32) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) +} + +PUT_U32_BE :: #force_inline proc "contextless" (b: []byte, v: u32) { + b[0] = byte(v >> 24) + b[1] = byte(v >> 16) + b[2] = byte(v >> 8) + b[3] = byte(v) +} + +PUT_U64_BE :: #force_inline proc "contextless" (b: []byte, v: u64) { + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) +} + +XOR_BUF :: #force_inline proc "contextless" (input, output: []byte) { + for i := 0; i < len(input); i += 1 { + output[i] ~= input[i] + } +} \ No newline at end of file diff --git a/core/crypto/whirlpool/whirlpool.odin b/core/crypto/whirlpool/whirlpool.odin new file mode 100644 index 000000000..e74d4fe8e --- /dev/null +++ b/core/crypto/whirlpool/whirlpool.odin @@ -0,0 +1,873 @@ +package whirlpool + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings. + + Implementation of the Whirlpool hashing algorithm, as defined in +*/ + +import "core:os" +import "core:io" + +import "../botan" +import "../_ctx" +import "../util" + +/* + Context initialization and switching between the Odin implementation and the bindings +*/ + +USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false)) + +@(private) +_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context { + ctx := _ctx._init_vtable() + when USE_BOTAN_LIB { + use_botan() + } else { + _assign_hash_vtable(ctx) + } + return ctx +} + +@(private) +_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) { + ctx.hash_bytes_64 = hash_bytes_odin + ctx.hash_file_64 = hash_file_odin + ctx.hash_stream_64 = hash_stream_odin + ctx.update = _update_odin + ctx.final = _final_odin +} + +_hash_impl := _init_vtable() + +// use_botan assigns the internal vtable of the hash context to use the Botan bindings +use_botan :: #force_inline proc() { + botan.assign_hash_vtable(_hash_impl, botan.HASH_WHIRLPOOL) +} + +// use_odin assigns the internal vtable of the hash context to use the Odin implementation +use_odin :: #force_inline proc() { + _assign_hash_vtable(_hash_impl) +} + +/* + High level API +*/ + +// hash_string will hash the given input and return the +// computed hash +hash_string :: proc(data: string) -> [64]byte { + return hash_bytes(transmute([]byte)(data)) +} + +// hash_bytes will hash the given input and return the +// computed hash +hash_bytes :: proc(data: []byte) -> [64]byte { + _create_whirlpool_ctx() + return _hash_impl->hash_bytes_64(data) +} + +// hash_stream will read the stream in chunks and compute a +// hash from its contents +hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) { + _create_whirlpool_ctx() + return _hash_impl->hash_stream_64(s) +} + +// hash_file will try to open the file provided by the given +// path and pass it to hash_stream to compute a hash +hash_file :: proc(path: string, load_at_once: bool) -> ([64]byte, bool) { + _create_whirlpool_ctx() + return _hash_impl->hash_file_64(path, load_at_once) +} + +hash :: proc { + hash_stream, + hash_file, + hash_bytes, + hash_string, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^_ctx.Hash_Context) { + _hash_impl->init() +} + +update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { + _hash_impl->update(data) +} + +final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + _hash_impl->final(hash) +} + +hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte { + hash: [64]byte + if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok { + update_odin(&c, data) + final_odin(&c, hash[:]) + } + return hash +} + +hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) { + hash: [64]byte + if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok { + buf := make([]byte, 512) + defer delete(buf) + read := 1 + for read > 0 { + read, _ = fs->impl_read(buf) + if read > 0 { + update_odin(&c, buf[:read]) + } + } + final_odin(&c, hash[:]) + return hash, true + } else { + return hash, false + } +} + +hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, path: string, load_at_once: bool) -> ([64]byte, bool) { + if hd, err := os.open(path); err == os.ERROR_NONE { + defer os.close(hd) + if !load_at_once { + return hash_stream_odin(ctx, os.stream_from_handle(hd)) + } else { + if buf, read_ok := os.read_entire_file(path); read_ok { + return hash_bytes_odin(ctx, buf[:]), read_ok + } + } + } + return [64]byte{}, false +} + +@(private) +_create_whirlpool_ctx :: #force_inline proc() { + ctx: Whirlpool_Context + _hash_impl.internal_ctx = ctx + _hash_impl.hash_size = ._64 +} + +@(private) +_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { + if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok { + update_odin(&c, data) + } +} + +@(private) +_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { + if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok { + final_odin(&c, hash) + } +} + +/* + Whirlpool implementation +*/ + +ROUNDS :: 10 + +Whirlpool_Context :: struct { + bitlength: [32]byte, + buffer: [64]byte, + buffer_bits: int, + buffer_pos: int, + hash: [8]u64, +} + +C0 := [256]u64 { + 0x18186018c07830d8, 0x23238c2305af4626, 0xc6c63fc67ef991b8, 0xe8e887e8136fcdfb, + 0x878726874ca113cb, 0xb8b8dab8a9626d11, 0x0101040108050209, 0x4f4f214f426e9e0d, + 0x3636d836adee6c9b, 0xa6a6a2a6590451ff, 0xd2d26fd2debdb90c, 0xf5f5f3f5fb06f70e, + 0x7979f979ef80f296, 0x6f6fa16f5fcede30, 0x91917e91fcef3f6d, 0x52525552aa07a4f8, + 0x60609d6027fdc047, 0xbcbccabc89766535, 0x9b9b569baccd2b37, 0x8e8e028e048c018a, + 0xa3a3b6a371155bd2, 0x0c0c300c603c186c, 0x7b7bf17bff8af684, 0x3535d435b5e16a80, + 0x1d1d741de8693af5, 0xe0e0a7e05347ddb3, 0xd7d77bd7f6acb321, 0xc2c22fc25eed999c, + 0x2e2eb82e6d965c43, 0x4b4b314b627a9629, 0xfefedffea321e15d, 0x575741578216aed5, + 0x15155415a8412abd, 0x7777c1779fb6eee8, 0x3737dc37a5eb6e92, 0xe5e5b3e57b56d79e, + 0x9f9f469f8cd92313, 0xf0f0e7f0d317fd23, 0x4a4a354a6a7f9420, 0xdada4fda9e95a944, + 0x58587d58fa25b0a2, 0xc9c903c906ca8fcf, 0x2929a429558d527c, 0x0a0a280a5022145a, + 0xb1b1feb1e14f7f50, 0xa0a0baa0691a5dc9, 0x6b6bb16b7fdad614, 0x85852e855cab17d9, + 0xbdbdcebd8173673c, 0x5d5d695dd234ba8f, 0x1010401080502090, 0xf4f4f7f4f303f507, + 0xcbcb0bcb16c08bdd, 0x3e3ef83eedc67cd3, 0x0505140528110a2d, 0x676781671fe6ce78, + 0xe4e4b7e47353d597, 0x27279c2725bb4e02, 0x4141194132588273, 0x8b8b168b2c9d0ba7, + 0xa7a7a6a7510153f6, 0x7d7de97dcf94fab2, 0x95956e95dcfb3749, 0xd8d847d88e9fad56, + 0xfbfbcbfb8b30eb70, 0xeeee9fee2371c1cd, 0x7c7ced7cc791f8bb, 0x6666856617e3cc71, + 0xdddd53dda68ea77b, 0x17175c17b84b2eaf, 0x4747014702468e45, 0x9e9e429e84dc211a, + 0xcaca0fca1ec589d4, 0x2d2db42d75995a58, 0xbfbfc6bf9179632e, 0x07071c07381b0e3f, + 0xadad8ead012347ac, 0x5a5a755aea2fb4b0, 0x838336836cb51bef, 0x3333cc3385ff66b6, + 0x636391633ff2c65c, 0x02020802100a0412, 0xaaaa92aa39384993, 0x7171d971afa8e2de, + 0xc8c807c80ecf8dc6, 0x19196419c87d32d1, 0x494939497270923b, 0xd9d943d9869aaf5f, + 0xf2f2eff2c31df931, 0xe3e3abe34b48dba8, 0x5b5b715be22ab6b9, 0x88881a8834920dbc, + 0x9a9a529aa4c8293e, 0x262698262dbe4c0b, 0x3232c8328dfa64bf, 0xb0b0fab0e94a7d59, + 0xe9e983e91b6acff2, 0x0f0f3c0f78331e77, 0xd5d573d5e6a6b733, 0x80803a8074ba1df4, + 0xbebec2be997c6127, 0xcdcd13cd26de87eb, 0x3434d034bde46889, 0x48483d487a759032, + 0xffffdbffab24e354, 0x7a7af57af78ff48d, 0x90907a90f4ea3d64, 0x5f5f615fc23ebe9d, + 0x202080201da0403d, 0x6868bd6867d5d00f, 0x1a1a681ad07234ca, 0xaeae82ae192c41b7, + 0xb4b4eab4c95e757d, 0x54544d549a19a8ce, 0x93937693ece53b7f, 0x222288220daa442f, + 0x64648d6407e9c863, 0xf1f1e3f1db12ff2a, 0x7373d173bfa2e6cc, 0x12124812905a2482, + 0x40401d403a5d807a, 0x0808200840281048, 0xc3c32bc356e89b95, 0xecec97ec337bc5df, + 0xdbdb4bdb9690ab4d, 0xa1a1bea1611f5fc0, 0x8d8d0e8d1c830791, 0x3d3df43df5c97ac8, + 0x97976697ccf1335b, 0x0000000000000000, 0xcfcf1bcf36d483f9, 0x2b2bac2b4587566e, + 0x7676c57697b3ece1, 0x8282328264b019e6, 0xd6d67fd6fea9b128, 0x1b1b6c1bd87736c3, + 0xb5b5eeb5c15b7774, 0xafaf86af112943be, 0x6a6ab56a77dfd41d, 0x50505d50ba0da0ea, + 0x45450945124c8a57, 0xf3f3ebf3cb18fb38, 0x3030c0309df060ad, 0xefef9bef2b74c3c4, + 0x3f3ffc3fe5c37eda, 0x55554955921caac7, 0xa2a2b2a2791059db, 0xeaea8fea0365c9e9, + 0x656589650fecca6a, 0xbabad2bab9686903, 0x2f2fbc2f65935e4a, 0xc0c027c04ee79d8e, + 0xdede5fdebe81a160, 0x1c1c701ce06c38fc, 0xfdfdd3fdbb2ee746, 0x4d4d294d52649a1f, + 0x92927292e4e03976, 0x7575c9758fbceafa, 0x06061806301e0c36, 0x8a8a128a249809ae, + 0xb2b2f2b2f940794b, 0xe6e6bfe66359d185, 0x0e0e380e70361c7e, 0x1f1f7c1ff8633ee7, + 0x6262956237f7c455, 0xd4d477d4eea3b53a, 0xa8a89aa829324d81, 0x96966296c4f43152, + 0xf9f9c3f99b3aef62, 0xc5c533c566f697a3, 0x2525942535b14a10, 0x59597959f220b2ab, + 0x84842a8454ae15d0, 0x7272d572b7a7e4c5, 0x3939e439d5dd72ec, 0x4c4c2d4c5a619816, + 0x5e5e655eca3bbc94, 0x7878fd78e785f09f, 0x3838e038ddd870e5, 0x8c8c0a8c14860598, + 0xd1d163d1c6b2bf17, 0xa5a5aea5410b57e4, 0xe2e2afe2434dd9a1, 0x616199612ff8c24e, + 0xb3b3f6b3f1457b42, 0x2121842115a54234, 0x9c9c4a9c94d62508, 0x1e1e781ef0663cee, + 0x4343114322528661, 0xc7c73bc776fc93b1, 0xfcfcd7fcb32be54f, 0x0404100420140824, + 0x51515951b208a2e3, 0x99995e99bcc72f25, 0x6d6da96d4fc4da22, 0x0d0d340d68391a65, + 0xfafacffa8335e979, 0xdfdf5bdfb684a369, 0x7e7ee57ed79bfca9, 0x242490243db44819, + 0x3b3bec3bc5d776fe, 0xabab96ab313d4b9a, 0xcece1fce3ed181f0, 0x1111441188552299, + 0x8f8f068f0c890383, 0x4e4e254e4a6b9c04, 0xb7b7e6b7d1517366, 0xebeb8beb0b60cbe0, + 0x3c3cf03cfdcc78c1, 0x81813e817cbf1ffd, 0x94946a94d4fe3540, 0xf7f7fbf7eb0cf31c, + 0xb9b9deb9a1676f18, 0x13134c13985f268b, 0x2c2cb02c7d9c5851, 0xd3d36bd3d6b8bb05, + 0xe7e7bbe76b5cd38c, 0x6e6ea56e57cbdc39, 0xc4c437c46ef395aa, 0x03030c03180f061b, + 0x565645568a13acdc, 0x44440d441a49885e, 0x7f7fe17fdf9efea0, 0xa9a99ea921374f88, + 0x2a2aa82a4d825467, 0xbbbbd6bbb16d6b0a, 0xc1c123c146e29f87, 0x53535153a202a6f1, + 0xdcdc57dcae8ba572, 0x0b0b2c0b58271653, 0x9d9d4e9d9cd32701, 0x6c6cad6c47c1d82b, + 0x3131c43195f562a4, 0x7474cd7487b9e8f3, 0xf6f6fff6e309f115, 0x464605460a438c4c, + 0xacac8aac092645a5, 0x89891e893c970fb5, 0x14145014a04428b4, 0xe1e1a3e15b42dfba, + 0x16165816b04e2ca6, 0x3a3ae83acdd274f7, 0x6969b9696fd0d206, 0x09092409482d1241, + 0x7070dd70a7ade0d7, 0xb6b6e2b6d954716f, 0xd0d067d0ceb7bd1e, 0xeded93ed3b7ec7d6, + 0xcccc17cc2edb85e2, 0x424215422a578468, 0x98985a98b4c22d2c, 0xa4a4aaa4490e55ed, + 0x2828a0285d885075, 0x5c5c6d5cda31b886, 0xf8f8c7f8933fed6b, 0x8686228644a411c2, +} + +C1 := [256]u64 { + 0xd818186018c07830, 0x2623238c2305af46, 0xb8c6c63fc67ef991, 0xfbe8e887e8136fcd, + 0xcb878726874ca113, 0x11b8b8dab8a9626d, 0x0901010401080502, 0x0d4f4f214f426e9e, + 0x9b3636d836adee6c, 0xffa6a6a2a6590451, 0x0cd2d26fd2debdb9, 0x0ef5f5f3f5fb06f7, + 0x967979f979ef80f2, 0x306f6fa16f5fcede, 0x6d91917e91fcef3f, 0xf852525552aa07a4, + 0x4760609d6027fdc0, 0x35bcbccabc897665, 0x379b9b569baccd2b, 0x8a8e8e028e048c01, + 0xd2a3a3b6a371155b, 0x6c0c0c300c603c18, 0x847b7bf17bff8af6, 0x803535d435b5e16a, + 0xf51d1d741de8693a, 0xb3e0e0a7e05347dd, 0x21d7d77bd7f6acb3, 0x9cc2c22fc25eed99, + 0x432e2eb82e6d965c, 0x294b4b314b627a96, 0x5dfefedffea321e1, 0xd5575741578216ae, + 0xbd15155415a8412a, 0xe87777c1779fb6ee, 0x923737dc37a5eb6e, 0x9ee5e5b3e57b56d7, + 0x139f9f469f8cd923, 0x23f0f0e7f0d317fd, 0x204a4a354a6a7f94, 0x44dada4fda9e95a9, + 0xa258587d58fa25b0, 0xcfc9c903c906ca8f, 0x7c2929a429558d52, 0x5a0a0a280a502214, + 0x50b1b1feb1e14f7f, 0xc9a0a0baa0691a5d, 0x146b6bb16b7fdad6, 0xd985852e855cab17, + 0x3cbdbdcebd817367, 0x8f5d5d695dd234ba, 0x9010104010805020, 0x07f4f4f7f4f303f5, + 0xddcbcb0bcb16c08b, 0xd33e3ef83eedc67c, 0x2d0505140528110a, 0x78676781671fe6ce, + 0x97e4e4b7e47353d5, 0x0227279c2725bb4e, 0x7341411941325882, 0xa78b8b168b2c9d0b, + 0xf6a7a7a6a7510153, 0xb27d7de97dcf94fa, 0x4995956e95dcfb37, 0x56d8d847d88e9fad, + 0x70fbfbcbfb8b30eb, 0xcdeeee9fee2371c1, 0xbb7c7ced7cc791f8, 0x716666856617e3cc, + 0x7bdddd53dda68ea7, 0xaf17175c17b84b2e, 0x454747014702468e, 0x1a9e9e429e84dc21, + 0xd4caca0fca1ec589, 0x582d2db42d75995a, 0x2ebfbfc6bf917963, 0x3f07071c07381b0e, + 0xacadad8ead012347, 0xb05a5a755aea2fb4, 0xef838336836cb51b, 0xb63333cc3385ff66, + 0x5c636391633ff2c6, 0x1202020802100a04, 0x93aaaa92aa393849, 0xde7171d971afa8e2, + 0xc6c8c807c80ecf8d, 0xd119196419c87d32, 0x3b49493949727092, 0x5fd9d943d9869aaf, + 0x31f2f2eff2c31df9, 0xa8e3e3abe34b48db, 0xb95b5b715be22ab6, 0xbc88881a8834920d, + 0x3e9a9a529aa4c829, 0x0b262698262dbe4c, 0xbf3232c8328dfa64, 0x59b0b0fab0e94a7d, + 0xf2e9e983e91b6acf, 0x770f0f3c0f78331e, 0x33d5d573d5e6a6b7, 0xf480803a8074ba1d, + 0x27bebec2be997c61, 0xebcdcd13cd26de87, 0x893434d034bde468, 0x3248483d487a7590, + 0x54ffffdbffab24e3, 0x8d7a7af57af78ff4, 0x6490907a90f4ea3d, 0x9d5f5f615fc23ebe, + 0x3d202080201da040, 0x0f6868bd6867d5d0, 0xca1a1a681ad07234, 0xb7aeae82ae192c41, + 0x7db4b4eab4c95e75, 0xce54544d549a19a8, 0x7f93937693ece53b, 0x2f222288220daa44, + 0x6364648d6407e9c8, 0x2af1f1e3f1db12ff, 0xcc7373d173bfa2e6, 0x8212124812905a24, + 0x7a40401d403a5d80, 0x4808082008402810, 0x95c3c32bc356e89b, 0xdfecec97ec337bc5, + 0x4ddbdb4bdb9690ab, 0xc0a1a1bea1611f5f, 0x918d8d0e8d1c8307, 0xc83d3df43df5c97a, + 0x5b97976697ccf133, 0x0000000000000000, 0xf9cfcf1bcf36d483, 0x6e2b2bac2b458756, + 0xe17676c57697b3ec, 0xe68282328264b019, 0x28d6d67fd6fea9b1, 0xc31b1b6c1bd87736, + 0x74b5b5eeb5c15b77, 0xbeafaf86af112943, 0x1d6a6ab56a77dfd4, 0xea50505d50ba0da0, + 0x5745450945124c8a, 0x38f3f3ebf3cb18fb, 0xad3030c0309df060, 0xc4efef9bef2b74c3, + 0xda3f3ffc3fe5c37e, 0xc755554955921caa, 0xdba2a2b2a2791059, 0xe9eaea8fea0365c9, + 0x6a656589650fecca, 0x03babad2bab96869, 0x4a2f2fbc2f65935e, 0x8ec0c027c04ee79d, + 0x60dede5fdebe81a1, 0xfc1c1c701ce06c38, 0x46fdfdd3fdbb2ee7, 0x1f4d4d294d52649a, + 0x7692927292e4e039, 0xfa7575c9758fbcea, 0x3606061806301e0c, 0xae8a8a128a249809, + 0x4bb2b2f2b2f94079, 0x85e6e6bfe66359d1, 0x7e0e0e380e70361c, 0xe71f1f7c1ff8633e, + 0x556262956237f7c4, 0x3ad4d477d4eea3b5, 0x81a8a89aa829324d, 0x5296966296c4f431, + 0x62f9f9c3f99b3aef, 0xa3c5c533c566f697, 0x102525942535b14a, 0xab59597959f220b2, + 0xd084842a8454ae15, 0xc57272d572b7a7e4, 0xec3939e439d5dd72, 0x164c4c2d4c5a6198, + 0x945e5e655eca3bbc, 0x9f7878fd78e785f0, 0xe53838e038ddd870, 0x988c8c0a8c148605, + 0x17d1d163d1c6b2bf, 0xe4a5a5aea5410b57, 0xa1e2e2afe2434dd9, 0x4e616199612ff8c2, + 0x42b3b3f6b3f1457b, 0x342121842115a542, 0x089c9c4a9c94d625, 0xee1e1e781ef0663c, + 0x6143431143225286, 0xb1c7c73bc776fc93, 0x4ffcfcd7fcb32be5, 0x2404041004201408, + 0xe351515951b208a2, 0x2599995e99bcc72f, 0x226d6da96d4fc4da, 0x650d0d340d68391a, + 0x79fafacffa8335e9, 0x69dfdf5bdfb684a3, 0xa97e7ee57ed79bfc, 0x19242490243db448, + 0xfe3b3bec3bc5d776, 0x9aabab96ab313d4b, 0xf0cece1fce3ed181, 0x9911114411885522, + 0x838f8f068f0c8903, 0x044e4e254e4a6b9c, 0x66b7b7e6b7d15173, 0xe0ebeb8beb0b60cb, + 0xc13c3cf03cfdcc78, 0xfd81813e817cbf1f, 0x4094946a94d4fe35, 0x1cf7f7fbf7eb0cf3, + 0x18b9b9deb9a1676f, 0x8b13134c13985f26, 0x512c2cb02c7d9c58, 0x05d3d36bd3d6b8bb, + 0x8ce7e7bbe76b5cd3, 0x396e6ea56e57cbdc, 0xaac4c437c46ef395, 0x1b03030c03180f06, + 0xdc565645568a13ac, 0x5e44440d441a4988, 0xa07f7fe17fdf9efe, 0x88a9a99ea921374f, + 0x672a2aa82a4d8254, 0x0abbbbd6bbb16d6b, 0x87c1c123c146e29f, 0xf153535153a202a6, + 0x72dcdc57dcae8ba5, 0x530b0b2c0b582716, 0x019d9d4e9d9cd327, 0x2b6c6cad6c47c1d8, + 0xa43131c43195f562, 0xf37474cd7487b9e8, 0x15f6f6fff6e309f1, 0x4c464605460a438c, + 0xa5acac8aac092645, 0xb589891e893c970f, 0xb414145014a04428, 0xbae1e1a3e15b42df, + 0xa616165816b04e2c, 0xf73a3ae83acdd274, 0x066969b9696fd0d2, 0x4109092409482d12, + 0xd77070dd70a7ade0, 0x6fb6b6e2b6d95471, 0x1ed0d067d0ceb7bd, 0xd6eded93ed3b7ec7, + 0xe2cccc17cc2edb85, 0x68424215422a5784, 0x2c98985a98b4c22d, 0xeda4a4aaa4490e55, + 0x752828a0285d8850, 0x865c5c6d5cda31b8, 0x6bf8f8c7f8933fed, 0xc28686228644a411, +} + +C2 := [256]u64 { + 0x30d818186018c078, 0x462623238c2305af, 0x91b8c6c63fc67ef9, 0xcdfbe8e887e8136f, + 0x13cb878726874ca1, 0x6d11b8b8dab8a962, 0x0209010104010805, 0x9e0d4f4f214f426e, + 0x6c9b3636d836adee, 0x51ffa6a6a2a65904, 0xb90cd2d26fd2debd, 0xf70ef5f5f3f5fb06, + 0xf2967979f979ef80, 0xde306f6fa16f5fce, 0x3f6d91917e91fcef, 0xa4f852525552aa07, + 0xc04760609d6027fd, 0x6535bcbccabc8976, 0x2b379b9b569baccd, 0x018a8e8e028e048c, + 0x5bd2a3a3b6a37115, 0x186c0c0c300c603c, 0xf6847b7bf17bff8a, 0x6a803535d435b5e1, + 0x3af51d1d741de869, 0xddb3e0e0a7e05347, 0xb321d7d77bd7f6ac, 0x999cc2c22fc25eed, + 0x5c432e2eb82e6d96, 0x96294b4b314b627a, 0xe15dfefedffea321, 0xaed5575741578216, + 0x2abd15155415a841, 0xeee87777c1779fb6, 0x6e923737dc37a5eb, 0xd79ee5e5b3e57b56, + 0x23139f9f469f8cd9, 0xfd23f0f0e7f0d317, 0x94204a4a354a6a7f, 0xa944dada4fda9e95, + 0xb0a258587d58fa25, 0x8fcfc9c903c906ca, 0x527c2929a429558d, 0x145a0a0a280a5022, + 0x7f50b1b1feb1e14f, 0x5dc9a0a0baa0691a, 0xd6146b6bb16b7fda, 0x17d985852e855cab, + 0x673cbdbdcebd8173, 0xba8f5d5d695dd234, 0x2090101040108050, 0xf507f4f4f7f4f303, + 0x8bddcbcb0bcb16c0, 0x7cd33e3ef83eedc6, 0x0a2d050514052811, 0xce78676781671fe6, + 0xd597e4e4b7e47353, 0x4e0227279c2725bb, 0x8273414119413258, 0x0ba78b8b168b2c9d, + 0x53f6a7a7a6a75101, 0xfab27d7de97dcf94, 0x374995956e95dcfb, 0xad56d8d847d88e9f, + 0xeb70fbfbcbfb8b30, 0xc1cdeeee9fee2371, 0xf8bb7c7ced7cc791, 0xcc716666856617e3, + 0xa77bdddd53dda68e, 0x2eaf17175c17b84b, 0x8e45474701470246, 0x211a9e9e429e84dc, + 0x89d4caca0fca1ec5, 0x5a582d2db42d7599, 0x632ebfbfc6bf9179, 0x0e3f07071c07381b, + 0x47acadad8ead0123, 0xb4b05a5a755aea2f, 0x1bef838336836cb5, 0x66b63333cc3385ff, + 0xc65c636391633ff2, 0x041202020802100a, 0x4993aaaa92aa3938, 0xe2de7171d971afa8, + 0x8dc6c8c807c80ecf, 0x32d119196419c87d, 0x923b494939497270, 0xaf5fd9d943d9869a, + 0xf931f2f2eff2c31d, 0xdba8e3e3abe34b48, 0xb6b95b5b715be22a, 0x0dbc88881a883492, + 0x293e9a9a529aa4c8, 0x4c0b262698262dbe, 0x64bf3232c8328dfa, 0x7d59b0b0fab0e94a, + 0xcff2e9e983e91b6a, 0x1e770f0f3c0f7833, 0xb733d5d573d5e6a6, 0x1df480803a8074ba, + 0x6127bebec2be997c, 0x87ebcdcd13cd26de, 0x68893434d034bde4, 0x903248483d487a75, + 0xe354ffffdbffab24, 0xf48d7a7af57af78f, 0x3d6490907a90f4ea, 0xbe9d5f5f615fc23e, + 0x403d202080201da0, 0xd00f6868bd6867d5, 0x34ca1a1a681ad072, 0x41b7aeae82ae192c, + 0x757db4b4eab4c95e, 0xa8ce54544d549a19, 0x3b7f93937693ece5, 0x442f222288220daa, + 0xc86364648d6407e9, 0xff2af1f1e3f1db12, 0xe6cc7373d173bfa2, 0x248212124812905a, + 0x807a40401d403a5d, 0x1048080820084028, 0x9b95c3c32bc356e8, 0xc5dfecec97ec337b, + 0xab4ddbdb4bdb9690, 0x5fc0a1a1bea1611f, 0x07918d8d0e8d1c83, 0x7ac83d3df43df5c9, + 0x335b97976697ccf1, 0x0000000000000000, 0x83f9cfcf1bcf36d4, 0x566e2b2bac2b4587, + 0xece17676c57697b3, 0x19e68282328264b0, 0xb128d6d67fd6fea9, 0x36c31b1b6c1bd877, + 0x7774b5b5eeb5c15b, 0x43beafaf86af1129, 0xd41d6a6ab56a77df, 0xa0ea50505d50ba0d, + 0x8a5745450945124c, 0xfb38f3f3ebf3cb18, 0x60ad3030c0309df0, 0xc3c4efef9bef2b74, + 0x7eda3f3ffc3fe5c3, 0xaac755554955921c, 0x59dba2a2b2a27910, 0xc9e9eaea8fea0365, + 0xca6a656589650fec, 0x6903babad2bab968, 0x5e4a2f2fbc2f6593, 0x9d8ec0c027c04ee7, + 0xa160dede5fdebe81, 0x38fc1c1c701ce06c, 0xe746fdfdd3fdbb2e, 0x9a1f4d4d294d5264, + 0x397692927292e4e0, 0xeafa7575c9758fbc, 0x0c3606061806301e, 0x09ae8a8a128a2498, + 0x794bb2b2f2b2f940, 0xd185e6e6bfe66359, 0x1c7e0e0e380e7036, 0x3ee71f1f7c1ff863, + 0xc4556262956237f7, 0xb53ad4d477d4eea3, 0x4d81a8a89aa82932, 0x315296966296c4f4, + 0xef62f9f9c3f99b3a, 0x97a3c5c533c566f6, 0x4a102525942535b1, 0xb2ab59597959f220, + 0x15d084842a8454ae, 0xe4c57272d572b7a7, 0x72ec3939e439d5dd, 0x98164c4c2d4c5a61, + 0xbc945e5e655eca3b, 0xf09f7878fd78e785, 0x70e53838e038ddd8, 0x05988c8c0a8c1486, + 0xbf17d1d163d1c6b2, 0x57e4a5a5aea5410b, 0xd9a1e2e2afe2434d, 0xc24e616199612ff8, + 0x7b42b3b3f6b3f145, 0x42342121842115a5, 0x25089c9c4a9c94d6, 0x3cee1e1e781ef066, + 0x8661434311432252, 0x93b1c7c73bc776fc, 0xe54ffcfcd7fcb32b, 0x0824040410042014, + 0xa2e351515951b208, 0x2f2599995e99bcc7, 0xda226d6da96d4fc4, 0x1a650d0d340d6839, + 0xe979fafacffa8335, 0xa369dfdf5bdfb684, 0xfca97e7ee57ed79b, 0x4819242490243db4, + 0x76fe3b3bec3bc5d7, 0x4b9aabab96ab313d, 0x81f0cece1fce3ed1, 0x2299111144118855, + 0x03838f8f068f0c89, 0x9c044e4e254e4a6b, 0x7366b7b7e6b7d151, 0xcbe0ebeb8beb0b60, + 0x78c13c3cf03cfdcc, 0x1ffd81813e817cbf, 0x354094946a94d4fe, 0xf31cf7f7fbf7eb0c, + 0x6f18b9b9deb9a167, 0x268b13134c13985f, 0x58512c2cb02c7d9c, 0xbb05d3d36bd3d6b8, + 0xd38ce7e7bbe76b5c, 0xdc396e6ea56e57cb, 0x95aac4c437c46ef3, 0x061b03030c03180f, + 0xacdc565645568a13, 0x885e44440d441a49, 0xfea07f7fe17fdf9e, 0x4f88a9a99ea92137, + 0x54672a2aa82a4d82, 0x6b0abbbbd6bbb16d, 0x9f87c1c123c146e2, 0xa6f153535153a202, + 0xa572dcdc57dcae8b, 0x16530b0b2c0b5827, 0x27019d9d4e9d9cd3, 0xd82b6c6cad6c47c1, + 0x62a43131c43195f5, 0xe8f37474cd7487b9, 0xf115f6f6fff6e309, 0x8c4c464605460a43, + 0x45a5acac8aac0926, 0x0fb589891e893c97, 0x28b414145014a044, 0xdfbae1e1a3e15b42, + 0x2ca616165816b04e, 0x74f73a3ae83acdd2, 0xd2066969b9696fd0, 0x124109092409482d, + 0xe0d77070dd70a7ad, 0x716fb6b6e2b6d954, 0xbd1ed0d067d0ceb7, 0xc7d6eded93ed3b7e, + 0x85e2cccc17cc2edb, 0x8468424215422a57, 0x2d2c98985a98b4c2, 0x55eda4a4aaa4490e, + 0x50752828a0285d88, 0xb8865c5c6d5cda31, 0xed6bf8f8c7f8933f, 0x11c28686228644a4, +} + +C3 := [256]u64 { + 0x7830d818186018c0, 0xaf462623238c2305, 0xf991b8c6c63fc67e, 0x6fcdfbe8e887e813, + 0xa113cb878726874c, 0x626d11b8b8dab8a9, 0x0502090101040108, 0x6e9e0d4f4f214f42, + 0xee6c9b3636d836ad, 0x0451ffa6a6a2a659, 0xbdb90cd2d26fd2de, 0x06f70ef5f5f3f5fb, + 0x80f2967979f979ef, 0xcede306f6fa16f5f, 0xef3f6d91917e91fc, 0x07a4f852525552aa, + 0xfdc04760609d6027, 0x766535bcbccabc89, 0xcd2b379b9b569bac, 0x8c018a8e8e028e04, + 0x155bd2a3a3b6a371, 0x3c186c0c0c300c60, 0x8af6847b7bf17bff, 0xe16a803535d435b5, + 0x693af51d1d741de8, 0x47ddb3e0e0a7e053, 0xacb321d7d77bd7f6, 0xed999cc2c22fc25e, + 0x965c432e2eb82e6d, 0x7a96294b4b314b62, 0x21e15dfefedffea3, 0x16aed55757415782, + 0x412abd15155415a8, 0xb6eee87777c1779f, 0xeb6e923737dc37a5, 0x56d79ee5e5b3e57b, + 0xd923139f9f469f8c, 0x17fd23f0f0e7f0d3, 0x7f94204a4a354a6a, 0x95a944dada4fda9e, + 0x25b0a258587d58fa, 0xca8fcfc9c903c906, 0x8d527c2929a42955, 0x22145a0a0a280a50, + 0x4f7f50b1b1feb1e1, 0x1a5dc9a0a0baa069, 0xdad6146b6bb16b7f, 0xab17d985852e855c, + 0x73673cbdbdcebd81, 0x34ba8f5d5d695dd2, 0x5020901010401080, 0x03f507f4f4f7f4f3, + 0xc08bddcbcb0bcb16, 0xc67cd33e3ef83eed, 0x110a2d0505140528, 0xe6ce78676781671f, + 0x53d597e4e4b7e473, 0xbb4e0227279c2725, 0x5882734141194132, 0x9d0ba78b8b168b2c, + 0x0153f6a7a7a6a751, 0x94fab27d7de97dcf, 0xfb374995956e95dc, 0x9fad56d8d847d88e, + 0x30eb70fbfbcbfb8b, 0x71c1cdeeee9fee23, 0x91f8bb7c7ced7cc7, 0xe3cc716666856617, + 0x8ea77bdddd53dda6, 0x4b2eaf17175c17b8, 0x468e454747014702, 0xdc211a9e9e429e84, + 0xc589d4caca0fca1e, 0x995a582d2db42d75, 0x79632ebfbfc6bf91, 0x1b0e3f07071c0738, + 0x2347acadad8ead01, 0x2fb4b05a5a755aea, 0xb51bef838336836c, 0xff66b63333cc3385, + 0xf2c65c636391633f, 0x0a04120202080210, 0x384993aaaa92aa39, 0xa8e2de7171d971af, + 0xcf8dc6c8c807c80e, 0x7d32d119196419c8, 0x70923b4949394972, 0x9aaf5fd9d943d986, + 0x1df931f2f2eff2c3, 0x48dba8e3e3abe34b, 0x2ab6b95b5b715be2, 0x920dbc88881a8834, + 0xc8293e9a9a529aa4, 0xbe4c0b262698262d, 0xfa64bf3232c8328d, 0x4a7d59b0b0fab0e9, + 0x6acff2e9e983e91b, 0x331e770f0f3c0f78, 0xa6b733d5d573d5e6, 0xba1df480803a8074, + 0x7c6127bebec2be99, 0xde87ebcdcd13cd26, 0xe468893434d034bd, 0x75903248483d487a, + 0x24e354ffffdbffab, 0x8ff48d7a7af57af7, 0xea3d6490907a90f4, 0x3ebe9d5f5f615fc2, + 0xa0403d202080201d, 0xd5d00f6868bd6867, 0x7234ca1a1a681ad0, 0x2c41b7aeae82ae19, + 0x5e757db4b4eab4c9, 0x19a8ce54544d549a, 0xe53b7f93937693ec, 0xaa442f222288220d, + 0xe9c86364648d6407, 0x12ff2af1f1e3f1db, 0xa2e6cc7373d173bf, 0x5a24821212481290, + 0x5d807a40401d403a, 0x2810480808200840, 0xe89b95c3c32bc356, 0x7bc5dfecec97ec33, + 0x90ab4ddbdb4bdb96, 0x1f5fc0a1a1bea161, 0x8307918d8d0e8d1c, 0xc97ac83d3df43df5, + 0xf1335b97976697cc, 0x0000000000000000, 0xd483f9cfcf1bcf36, 0x87566e2b2bac2b45, + 0xb3ece17676c57697, 0xb019e68282328264, 0xa9b128d6d67fd6fe, 0x7736c31b1b6c1bd8, + 0x5b7774b5b5eeb5c1, 0x2943beafaf86af11, 0xdfd41d6a6ab56a77, 0x0da0ea50505d50ba, + 0x4c8a574545094512, 0x18fb38f3f3ebf3cb, 0xf060ad3030c0309d, 0x74c3c4efef9bef2b, + 0xc37eda3f3ffc3fe5, 0x1caac75555495592, 0x1059dba2a2b2a279, 0x65c9e9eaea8fea03, + 0xecca6a656589650f, 0x686903babad2bab9, 0x935e4a2f2fbc2f65, 0xe79d8ec0c027c04e, + 0x81a160dede5fdebe, 0x6c38fc1c1c701ce0, 0x2ee746fdfdd3fdbb, 0x649a1f4d4d294d52, + 0xe0397692927292e4, 0xbceafa7575c9758f, 0x1e0c360606180630, 0x9809ae8a8a128a24, + 0x40794bb2b2f2b2f9, 0x59d185e6e6bfe663, 0x361c7e0e0e380e70, 0x633ee71f1f7c1ff8, + 0xf7c4556262956237, 0xa3b53ad4d477d4ee, 0x324d81a8a89aa829, 0xf4315296966296c4, + 0x3aef62f9f9c3f99b, 0xf697a3c5c533c566, 0xb14a102525942535, 0x20b2ab59597959f2, + 0xae15d084842a8454, 0xa7e4c57272d572b7, 0xdd72ec3939e439d5, 0x6198164c4c2d4c5a, + 0x3bbc945e5e655eca, 0x85f09f7878fd78e7, 0xd870e53838e038dd, 0x8605988c8c0a8c14, + 0xb2bf17d1d163d1c6, 0x0b57e4a5a5aea541, 0x4dd9a1e2e2afe243, 0xf8c24e616199612f, + 0x457b42b3b3f6b3f1, 0xa542342121842115, 0xd625089c9c4a9c94, 0x663cee1e1e781ef0, + 0x5286614343114322, 0xfc93b1c7c73bc776, 0x2be54ffcfcd7fcb3, 0x1408240404100420, + 0x08a2e351515951b2, 0xc72f2599995e99bc, 0xc4da226d6da96d4f, 0x391a650d0d340d68, + 0x35e979fafacffa83, 0x84a369dfdf5bdfb6, 0x9bfca97e7ee57ed7, 0xb44819242490243d, + 0xd776fe3b3bec3bc5, 0x3d4b9aabab96ab31, 0xd181f0cece1fce3e, 0x5522991111441188, + 0x8903838f8f068f0c, 0x6b9c044e4e254e4a, 0x517366b7b7e6b7d1, 0x60cbe0ebeb8beb0b, + 0xcc78c13c3cf03cfd, 0xbf1ffd81813e817c, 0xfe354094946a94d4, 0x0cf31cf7f7fbf7eb, + 0x676f18b9b9deb9a1, 0x5f268b13134c1398, 0x9c58512c2cb02c7d, 0xb8bb05d3d36bd3d6, + 0x5cd38ce7e7bbe76b, 0xcbdc396e6ea56e57, 0xf395aac4c437c46e, 0x0f061b03030c0318, + 0x13acdc565645568a, 0x49885e44440d441a, 0x9efea07f7fe17fdf, 0x374f88a9a99ea921, + 0x8254672a2aa82a4d, 0x6d6b0abbbbd6bbb1, 0xe29f87c1c123c146, 0x02a6f153535153a2, + 0x8ba572dcdc57dcae, 0x2716530b0b2c0b58, 0xd327019d9d4e9d9c, 0xc1d82b6c6cad6c47, + 0xf562a43131c43195, 0xb9e8f37474cd7487, 0x09f115f6f6fff6e3, 0x438c4c464605460a, + 0x2645a5acac8aac09, 0x970fb589891e893c, 0x4428b414145014a0, 0x42dfbae1e1a3e15b, + 0x4e2ca616165816b0, 0xd274f73a3ae83acd, 0xd0d2066969b9696f, 0x2d12410909240948, + 0xade0d77070dd70a7, 0x54716fb6b6e2b6d9, 0xb7bd1ed0d067d0ce, 0x7ec7d6eded93ed3b, + 0xdb85e2cccc17cc2e, 0x578468424215422a, 0xc22d2c98985a98b4, 0x0e55eda4a4aaa449, + 0x8850752828a0285d, 0x31b8865c5c6d5cda, 0x3fed6bf8f8c7f893, 0xa411c28686228644, +} + +C4 := [256]u64 { + 0xc07830d818186018, 0x05af462623238c23, 0x7ef991b8c6c63fc6, 0x136fcdfbe8e887e8, + 0x4ca113cb87872687, 0xa9626d11b8b8dab8, 0x0805020901010401, 0x426e9e0d4f4f214f, + 0xadee6c9b3636d836, 0x590451ffa6a6a2a6, 0xdebdb90cd2d26fd2, 0xfb06f70ef5f5f3f5, + 0xef80f2967979f979, 0x5fcede306f6fa16f, 0xfcef3f6d91917e91, 0xaa07a4f852525552, + 0x27fdc04760609d60, 0x89766535bcbccabc, 0xaccd2b379b9b569b, 0x048c018a8e8e028e, + 0x71155bd2a3a3b6a3, 0x603c186c0c0c300c, 0xff8af6847b7bf17b, 0xb5e16a803535d435, + 0xe8693af51d1d741d, 0x5347ddb3e0e0a7e0, 0xf6acb321d7d77bd7, 0x5eed999cc2c22fc2, + 0x6d965c432e2eb82e, 0x627a96294b4b314b, 0xa321e15dfefedffe, 0x8216aed557574157, + 0xa8412abd15155415, 0x9fb6eee87777c177, 0xa5eb6e923737dc37, 0x7b56d79ee5e5b3e5, + 0x8cd923139f9f469f, 0xd317fd23f0f0e7f0, 0x6a7f94204a4a354a, 0x9e95a944dada4fda, + 0xfa25b0a258587d58, 0x06ca8fcfc9c903c9, 0x558d527c2929a429, 0x5022145a0a0a280a, + 0xe14f7f50b1b1feb1, 0x691a5dc9a0a0baa0, 0x7fdad6146b6bb16b, 0x5cab17d985852e85, + 0x8173673cbdbdcebd, 0xd234ba8f5d5d695d, 0x8050209010104010, 0xf303f507f4f4f7f4, + 0x16c08bddcbcb0bcb, 0xedc67cd33e3ef83e, 0x28110a2d05051405, 0x1fe6ce7867678167, + 0x7353d597e4e4b7e4, 0x25bb4e0227279c27, 0x3258827341411941, 0x2c9d0ba78b8b168b, + 0x510153f6a7a7a6a7, 0xcf94fab27d7de97d, 0xdcfb374995956e95, 0x8e9fad56d8d847d8, + 0x8b30eb70fbfbcbfb, 0x2371c1cdeeee9fee, 0xc791f8bb7c7ced7c, 0x17e3cc7166668566, + 0xa68ea77bdddd53dd, 0xb84b2eaf17175c17, 0x02468e4547470147, 0x84dc211a9e9e429e, + 0x1ec589d4caca0fca, 0x75995a582d2db42d, 0x9179632ebfbfc6bf, 0x381b0e3f07071c07, + 0x012347acadad8ead, 0xea2fb4b05a5a755a, 0x6cb51bef83833683, 0x85ff66b63333cc33, + 0x3ff2c65c63639163, 0x100a041202020802, 0x39384993aaaa92aa, 0xafa8e2de7171d971, + 0x0ecf8dc6c8c807c8, 0xc87d32d119196419, 0x7270923b49493949, 0x869aaf5fd9d943d9, + 0xc31df931f2f2eff2, 0x4b48dba8e3e3abe3, 0xe22ab6b95b5b715b, 0x34920dbc88881a88, + 0xa4c8293e9a9a529a, 0x2dbe4c0b26269826, 0x8dfa64bf3232c832, 0xe94a7d59b0b0fab0, + 0x1b6acff2e9e983e9, 0x78331e770f0f3c0f, 0xe6a6b733d5d573d5, 0x74ba1df480803a80, + 0x997c6127bebec2be, 0x26de87ebcdcd13cd, 0xbde468893434d034, 0x7a75903248483d48, + 0xab24e354ffffdbff, 0xf78ff48d7a7af57a, 0xf4ea3d6490907a90, 0xc23ebe9d5f5f615f, + 0x1da0403d20208020, 0x67d5d00f6868bd68, 0xd07234ca1a1a681a, 0x192c41b7aeae82ae, + 0xc95e757db4b4eab4, 0x9a19a8ce54544d54, 0xece53b7f93937693, 0x0daa442f22228822, + 0x07e9c86364648d64, 0xdb12ff2af1f1e3f1, 0xbfa2e6cc7373d173, 0x905a248212124812, + 0x3a5d807a40401d40, 0x4028104808082008, 0x56e89b95c3c32bc3, 0x337bc5dfecec97ec, + 0x9690ab4ddbdb4bdb, 0x611f5fc0a1a1bea1, 0x1c8307918d8d0e8d, 0xf5c97ac83d3df43d, + 0xccf1335b97976697, 0x0000000000000000, 0x36d483f9cfcf1bcf, 0x4587566e2b2bac2b, + 0x97b3ece17676c576, 0x64b019e682823282, 0xfea9b128d6d67fd6, 0xd87736c31b1b6c1b, + 0xc15b7774b5b5eeb5, 0x112943beafaf86af, 0x77dfd41d6a6ab56a, 0xba0da0ea50505d50, + 0x124c8a5745450945, 0xcb18fb38f3f3ebf3, 0x9df060ad3030c030, 0x2b74c3c4efef9bef, + 0xe5c37eda3f3ffc3f, 0x921caac755554955, 0x791059dba2a2b2a2, 0x0365c9e9eaea8fea, + 0x0fecca6a65658965, 0xb9686903babad2ba, 0x65935e4a2f2fbc2f, 0x4ee79d8ec0c027c0, + 0xbe81a160dede5fde, 0xe06c38fc1c1c701c, 0xbb2ee746fdfdd3fd, 0x52649a1f4d4d294d, + 0xe4e0397692927292, 0x8fbceafa7575c975, 0x301e0c3606061806, 0x249809ae8a8a128a, + 0xf940794bb2b2f2b2, 0x6359d185e6e6bfe6, 0x70361c7e0e0e380e, 0xf8633ee71f1f7c1f, + 0x37f7c45562629562, 0xeea3b53ad4d477d4, 0x29324d81a8a89aa8, 0xc4f4315296966296, + 0x9b3aef62f9f9c3f9, 0x66f697a3c5c533c5, 0x35b14a1025259425, 0xf220b2ab59597959, + 0x54ae15d084842a84, 0xb7a7e4c57272d572, 0xd5dd72ec3939e439, 0x5a6198164c4c2d4c, + 0xca3bbc945e5e655e, 0xe785f09f7878fd78, 0xddd870e53838e038, 0x148605988c8c0a8c, + 0xc6b2bf17d1d163d1, 0x410b57e4a5a5aea5, 0x434dd9a1e2e2afe2, 0x2ff8c24e61619961, + 0xf1457b42b3b3f6b3, 0x15a5423421218421, 0x94d625089c9c4a9c, 0xf0663cee1e1e781e, + 0x2252866143431143, 0x76fc93b1c7c73bc7, 0xb32be54ffcfcd7fc, 0x2014082404041004, + 0xb208a2e351515951, 0xbcc72f2599995e99, 0x4fc4da226d6da96d, 0x68391a650d0d340d, + 0x8335e979fafacffa, 0xb684a369dfdf5bdf, 0xd79bfca97e7ee57e, 0x3db4481924249024, + 0xc5d776fe3b3bec3b, 0x313d4b9aabab96ab, 0x3ed181f0cece1fce, 0x8855229911114411, + 0x0c8903838f8f068f, 0x4a6b9c044e4e254e, 0xd1517366b7b7e6b7, 0x0b60cbe0ebeb8beb, + 0xfdcc78c13c3cf03c, 0x7cbf1ffd81813e81, 0xd4fe354094946a94, 0xeb0cf31cf7f7fbf7, + 0xa1676f18b9b9deb9, 0x985f268b13134c13, 0x7d9c58512c2cb02c, 0xd6b8bb05d3d36bd3, + 0x6b5cd38ce7e7bbe7, 0x57cbdc396e6ea56e, 0x6ef395aac4c437c4, 0x180f061b03030c03, + 0x8a13acdc56564556, 0x1a49885e44440d44, 0xdf9efea07f7fe17f, 0x21374f88a9a99ea9, + 0x4d8254672a2aa82a, 0xb16d6b0abbbbd6bb, 0x46e29f87c1c123c1, 0xa202a6f153535153, + 0xae8ba572dcdc57dc, 0x582716530b0b2c0b, 0x9cd327019d9d4e9d, 0x47c1d82b6c6cad6c, + 0x95f562a43131c431, 0x87b9e8f37474cd74, 0xe309f115f6f6fff6, 0x0a438c4c46460546, + 0x092645a5acac8aac, 0x3c970fb589891e89, 0xa04428b414145014, 0x5b42dfbae1e1a3e1, + 0xb04e2ca616165816, 0xcdd274f73a3ae83a, 0x6fd0d2066969b969, 0x482d124109092409, + 0xa7ade0d77070dd70, 0xd954716fb6b6e2b6, 0xceb7bd1ed0d067d0, 0x3b7ec7d6eded93ed, + 0x2edb85e2cccc17cc, 0x2a57846842421542, 0xb4c22d2c98985a98, 0x490e55eda4a4aaa4, + 0x5d8850752828a028, 0xda31b8865c5c6d5c, 0x933fed6bf8f8c7f8, 0x44a411c286862286, +} + +C5 := [256]u64 { + 0x18c07830d8181860, 0x2305af462623238c, 0xc67ef991b8c6c63f, 0xe8136fcdfbe8e887, + 0x874ca113cb878726, 0xb8a9626d11b8b8da, 0x0108050209010104, 0x4f426e9e0d4f4f21, + 0x36adee6c9b3636d8, 0xa6590451ffa6a6a2, 0xd2debdb90cd2d26f, 0xf5fb06f70ef5f5f3, + 0x79ef80f2967979f9, 0x6f5fcede306f6fa1, 0x91fcef3f6d91917e, 0x52aa07a4f8525255, + 0x6027fdc04760609d, 0xbc89766535bcbcca, 0x9baccd2b379b9b56, 0x8e048c018a8e8e02, + 0xa371155bd2a3a3b6, 0x0c603c186c0c0c30, 0x7bff8af6847b7bf1, 0x35b5e16a803535d4, + 0x1de8693af51d1d74, 0xe05347ddb3e0e0a7, 0xd7f6acb321d7d77b, 0xc25eed999cc2c22f, + 0x2e6d965c432e2eb8, 0x4b627a96294b4b31, 0xfea321e15dfefedf, 0x578216aed5575741, + 0x15a8412abd151554, 0x779fb6eee87777c1, 0x37a5eb6e923737dc, 0xe57b56d79ee5e5b3, + 0x9f8cd923139f9f46, 0xf0d317fd23f0f0e7, 0x4a6a7f94204a4a35, 0xda9e95a944dada4f, + 0x58fa25b0a258587d, 0xc906ca8fcfc9c903, 0x29558d527c2929a4, 0x0a5022145a0a0a28, + 0xb1e14f7f50b1b1fe, 0xa0691a5dc9a0a0ba, 0x6b7fdad6146b6bb1, 0x855cab17d985852e, + 0xbd8173673cbdbdce, 0x5dd234ba8f5d5d69, 0x1080502090101040, 0xf4f303f507f4f4f7, + 0xcb16c08bddcbcb0b, 0x3eedc67cd33e3ef8, 0x0528110a2d050514, 0x671fe6ce78676781, + 0xe47353d597e4e4b7, 0x2725bb4e0227279c, 0x4132588273414119, 0x8b2c9d0ba78b8b16, + 0xa7510153f6a7a7a6, 0x7dcf94fab27d7de9, 0x95dcfb374995956e, 0xd88e9fad56d8d847, + 0xfb8b30eb70fbfbcb, 0xee2371c1cdeeee9f, 0x7cc791f8bb7c7ced, 0x6617e3cc71666685, + 0xdda68ea77bdddd53, 0x17b84b2eaf17175c, 0x4702468e45474701, 0x9e84dc211a9e9e42, + 0xca1ec589d4caca0f, 0x2d75995a582d2db4, 0xbf9179632ebfbfc6, 0x07381b0e3f07071c, + 0xad012347acadad8e, 0x5aea2fb4b05a5a75, 0x836cb51bef838336, 0x3385ff66b63333cc, + 0x633ff2c65c636391, 0x02100a0412020208, 0xaa39384993aaaa92, 0x71afa8e2de7171d9, + 0xc80ecf8dc6c8c807, 0x19c87d32d1191964, 0x497270923b494939, 0xd9869aaf5fd9d943, + 0xf2c31df931f2f2ef, 0xe34b48dba8e3e3ab, 0x5be22ab6b95b5b71, 0x8834920dbc88881a, + 0x9aa4c8293e9a9a52, 0x262dbe4c0b262698, 0x328dfa64bf3232c8, 0xb0e94a7d59b0b0fa, + 0xe91b6acff2e9e983, 0x0f78331e770f0f3c, 0xd5e6a6b733d5d573, 0x8074ba1df480803a, + 0xbe997c6127bebec2, 0xcd26de87ebcdcd13, 0x34bde468893434d0, 0x487a75903248483d, + 0xffab24e354ffffdb, 0x7af78ff48d7a7af5, 0x90f4ea3d6490907a, 0x5fc23ebe9d5f5f61, + 0x201da0403d202080, 0x6867d5d00f6868bd, 0x1ad07234ca1a1a68, 0xae192c41b7aeae82, + 0xb4c95e757db4b4ea, 0x549a19a8ce54544d, 0x93ece53b7f939376, 0x220daa442f222288, + 0x6407e9c86364648d, 0xf1db12ff2af1f1e3, 0x73bfa2e6cc7373d1, 0x12905a2482121248, + 0x403a5d807a40401d, 0x0840281048080820, 0xc356e89b95c3c32b, 0xec337bc5dfecec97, + 0xdb9690ab4ddbdb4b, 0xa1611f5fc0a1a1be, 0x8d1c8307918d8d0e, 0x3df5c97ac83d3df4, + 0x97ccf1335b979766, 0x0000000000000000, 0xcf36d483f9cfcf1b, 0x2b4587566e2b2bac, + 0x7697b3ece17676c5, 0x8264b019e6828232, 0xd6fea9b128d6d67f, 0x1bd87736c31b1b6c, + 0xb5c15b7774b5b5ee, 0xaf112943beafaf86, 0x6a77dfd41d6a6ab5, 0x50ba0da0ea50505d, + 0x45124c8a57454509, 0xf3cb18fb38f3f3eb, 0x309df060ad3030c0, 0xef2b74c3c4efef9b, + 0x3fe5c37eda3f3ffc, 0x55921caac7555549, 0xa2791059dba2a2b2, 0xea0365c9e9eaea8f, + 0x650fecca6a656589, 0xbab9686903babad2, 0x2f65935e4a2f2fbc, 0xc04ee79d8ec0c027, + 0xdebe81a160dede5f, 0x1ce06c38fc1c1c70, 0xfdbb2ee746fdfdd3, 0x4d52649a1f4d4d29, + 0x92e4e03976929272, 0x758fbceafa7575c9, 0x06301e0c36060618, 0x8a249809ae8a8a12, + 0xb2f940794bb2b2f2, 0xe66359d185e6e6bf, 0x0e70361c7e0e0e38, 0x1ff8633ee71f1f7c, + 0x6237f7c455626295, 0xd4eea3b53ad4d477, 0xa829324d81a8a89a, 0x96c4f43152969662, + 0xf99b3aef62f9f9c3, 0xc566f697a3c5c533, 0x2535b14a10252594, 0x59f220b2ab595979, + 0x8454ae15d084842a, 0x72b7a7e4c57272d5, 0x39d5dd72ec3939e4, 0x4c5a6198164c4c2d, + 0x5eca3bbc945e5e65, 0x78e785f09f7878fd, 0x38ddd870e53838e0, 0x8c148605988c8c0a, + 0xd1c6b2bf17d1d163, 0xa5410b57e4a5a5ae, 0xe2434dd9a1e2e2af, 0x612ff8c24e616199, + 0xb3f1457b42b3b3f6, 0x2115a54234212184, 0x9c94d625089c9c4a, 0x1ef0663cee1e1e78, + 0x4322528661434311, 0xc776fc93b1c7c73b, 0xfcb32be54ffcfcd7, 0x0420140824040410, + 0x51b208a2e3515159, 0x99bcc72f2599995e, 0x6d4fc4da226d6da9, 0x0d68391a650d0d34, + 0xfa8335e979fafacf, 0xdfb684a369dfdf5b, 0x7ed79bfca97e7ee5, 0x243db44819242490, + 0x3bc5d776fe3b3bec, 0xab313d4b9aabab96, 0xce3ed181f0cece1f, 0x1188552299111144, + 0x8f0c8903838f8f06, 0x4e4a6b9c044e4e25, 0xb7d1517366b7b7e6, 0xeb0b60cbe0ebeb8b, + 0x3cfdcc78c13c3cf0, 0x817cbf1ffd81813e, 0x94d4fe354094946a, 0xf7eb0cf31cf7f7fb, + 0xb9a1676f18b9b9de, 0x13985f268b13134c, 0x2c7d9c58512c2cb0, 0xd3d6b8bb05d3d36b, + 0xe76b5cd38ce7e7bb, 0x6e57cbdc396e6ea5, 0xc46ef395aac4c437, 0x03180f061b03030c, + 0x568a13acdc565645, 0x441a49885e44440d, 0x7fdf9efea07f7fe1, 0xa921374f88a9a99e, + 0x2a4d8254672a2aa8, 0xbbb16d6b0abbbbd6, 0xc146e29f87c1c123, 0x53a202a6f1535351, + 0xdcae8ba572dcdc57, 0x0b582716530b0b2c, 0x9d9cd327019d9d4e, 0x6c47c1d82b6c6cad, + 0x3195f562a43131c4, 0x7487b9e8f37474cd, 0xf6e309f115f6f6ff, 0x460a438c4c464605, + 0xac092645a5acac8a, 0x893c970fb589891e, 0x14a04428b4141450, 0xe15b42dfbae1e1a3, + 0x16b04e2ca6161658, 0x3acdd274f73a3ae8, 0x696fd0d2066969b9, 0x09482d1241090924, + 0x70a7ade0d77070dd, 0xb6d954716fb6b6e2, 0xd0ceb7bd1ed0d067, 0xed3b7ec7d6eded93, + 0xcc2edb85e2cccc17, 0x422a578468424215, 0x98b4c22d2c98985a, 0xa4490e55eda4a4aa, + 0x285d8850752828a0, 0x5cda31b8865c5c6d, 0xf8933fed6bf8f8c7, 0x8644a411c2868622, +} + +C6 := [256]u64 { + 0x6018c07830d81818, 0x8c2305af46262323, 0x3fc67ef991b8c6c6, 0x87e8136fcdfbe8e8, + 0x26874ca113cb8787, 0xdab8a9626d11b8b8, 0x0401080502090101, 0x214f426e9e0d4f4f, + 0xd836adee6c9b3636, 0xa2a6590451ffa6a6, 0x6fd2debdb90cd2d2, 0xf3f5fb06f70ef5f5, + 0xf979ef80f2967979, 0xa16f5fcede306f6f, 0x7e91fcef3f6d9191, 0x5552aa07a4f85252, + 0x9d6027fdc0476060, 0xcabc89766535bcbc, 0x569baccd2b379b9b, 0x028e048c018a8e8e, + 0xb6a371155bd2a3a3, 0x300c603c186c0c0c, 0xf17bff8af6847b7b, 0xd435b5e16a803535, + 0x741de8693af51d1d, 0xa7e05347ddb3e0e0, 0x7bd7f6acb321d7d7, 0x2fc25eed999cc2c2, + 0xb82e6d965c432e2e, 0x314b627a96294b4b, 0xdffea321e15dfefe, 0x41578216aed55757, + 0x5415a8412abd1515, 0xc1779fb6eee87777, 0xdc37a5eb6e923737, 0xb3e57b56d79ee5e5, + 0x469f8cd923139f9f, 0xe7f0d317fd23f0f0, 0x354a6a7f94204a4a, 0x4fda9e95a944dada, + 0x7d58fa25b0a25858, 0x03c906ca8fcfc9c9, 0xa429558d527c2929, 0x280a5022145a0a0a, + 0xfeb1e14f7f50b1b1, 0xbaa0691a5dc9a0a0, 0xb16b7fdad6146b6b, 0x2e855cab17d98585, + 0xcebd8173673cbdbd, 0x695dd234ba8f5d5d, 0x4010805020901010, 0xf7f4f303f507f4f4, + 0x0bcb16c08bddcbcb, 0xf83eedc67cd33e3e, 0x140528110a2d0505, 0x81671fe6ce786767, + 0xb7e47353d597e4e4, 0x9c2725bb4e022727, 0x1941325882734141, 0x168b2c9d0ba78b8b, + 0xa6a7510153f6a7a7, 0xe97dcf94fab27d7d, 0x6e95dcfb37499595, 0x47d88e9fad56d8d8, + 0xcbfb8b30eb70fbfb, 0x9fee2371c1cdeeee, 0xed7cc791f8bb7c7c, 0x856617e3cc716666, + 0x53dda68ea77bdddd, 0x5c17b84b2eaf1717, 0x014702468e454747, 0x429e84dc211a9e9e, + 0x0fca1ec589d4caca, 0xb42d75995a582d2d, 0xc6bf9179632ebfbf, 0x1c07381b0e3f0707, + 0x8ead012347acadad, 0x755aea2fb4b05a5a, 0x36836cb51bef8383, 0xcc3385ff66b63333, + 0x91633ff2c65c6363, 0x0802100a04120202, 0x92aa39384993aaaa, 0xd971afa8e2de7171, + 0x07c80ecf8dc6c8c8, 0x6419c87d32d11919, 0x39497270923b4949, 0x43d9869aaf5fd9d9, + 0xeff2c31df931f2f2, 0xabe34b48dba8e3e3, 0x715be22ab6b95b5b, 0x1a8834920dbc8888, + 0x529aa4c8293e9a9a, 0x98262dbe4c0b2626, 0xc8328dfa64bf3232, 0xfab0e94a7d59b0b0, + 0x83e91b6acff2e9e9, 0x3c0f78331e770f0f, 0x73d5e6a6b733d5d5, 0x3a8074ba1df48080, + 0xc2be997c6127bebe, 0x13cd26de87ebcdcd, 0xd034bde468893434, 0x3d487a7590324848, + 0xdbffab24e354ffff, 0xf57af78ff48d7a7a, 0x7a90f4ea3d649090, 0x615fc23ebe9d5f5f, + 0x80201da0403d2020, 0xbd6867d5d00f6868, 0x681ad07234ca1a1a, 0x82ae192c41b7aeae, + 0xeab4c95e757db4b4, 0x4d549a19a8ce5454, 0x7693ece53b7f9393, 0x88220daa442f2222, + 0x8d6407e9c8636464, 0xe3f1db12ff2af1f1, 0xd173bfa2e6cc7373, 0x4812905a24821212, + 0x1d403a5d807a4040, 0x2008402810480808, 0x2bc356e89b95c3c3, 0x97ec337bc5dfecec, + 0x4bdb9690ab4ddbdb, 0xbea1611f5fc0a1a1, 0x0e8d1c8307918d8d, 0xf43df5c97ac83d3d, + 0x6697ccf1335b9797, 0x0000000000000000, 0x1bcf36d483f9cfcf, 0xac2b4587566e2b2b, + 0xc57697b3ece17676, 0x328264b019e68282, 0x7fd6fea9b128d6d6, 0x6c1bd87736c31b1b, + 0xeeb5c15b7774b5b5, 0x86af112943beafaf, 0xb56a77dfd41d6a6a, 0x5d50ba0da0ea5050, + 0x0945124c8a574545, 0xebf3cb18fb38f3f3, 0xc0309df060ad3030, 0x9bef2b74c3c4efef, + 0xfc3fe5c37eda3f3f, 0x4955921caac75555, 0xb2a2791059dba2a2, 0x8fea0365c9e9eaea, + 0x89650fecca6a6565, 0xd2bab9686903baba, 0xbc2f65935e4a2f2f, 0x27c04ee79d8ec0c0, + 0x5fdebe81a160dede, 0x701ce06c38fc1c1c, 0xd3fdbb2ee746fdfd, 0x294d52649a1f4d4d, + 0x7292e4e039769292, 0xc9758fbceafa7575, 0x1806301e0c360606, 0x128a249809ae8a8a, + 0xf2b2f940794bb2b2, 0xbfe66359d185e6e6, 0x380e70361c7e0e0e, 0x7c1ff8633ee71f1f, + 0x956237f7c4556262, 0x77d4eea3b53ad4d4, 0x9aa829324d81a8a8, 0x6296c4f431529696, + 0xc3f99b3aef62f9f9, 0x33c566f697a3c5c5, 0x942535b14a102525, 0x7959f220b2ab5959, + 0x2a8454ae15d08484, 0xd572b7a7e4c57272, 0xe439d5dd72ec3939, 0x2d4c5a6198164c4c, + 0x655eca3bbc945e5e, 0xfd78e785f09f7878, 0xe038ddd870e53838, 0x0a8c148605988c8c, + 0x63d1c6b2bf17d1d1, 0xaea5410b57e4a5a5, 0xafe2434dd9a1e2e2, 0x99612ff8c24e6161, + 0xf6b3f1457b42b3b3, 0x842115a542342121, 0x4a9c94d625089c9c, 0x781ef0663cee1e1e, + 0x1143225286614343, 0x3bc776fc93b1c7c7, 0xd7fcb32be54ffcfc, 0x1004201408240404, + 0x5951b208a2e35151, 0x5e99bcc72f259999, 0xa96d4fc4da226d6d, 0x340d68391a650d0d, + 0xcffa8335e979fafa, 0x5bdfb684a369dfdf, 0xe57ed79bfca97e7e, 0x90243db448192424, + 0xec3bc5d776fe3b3b, 0x96ab313d4b9aabab, 0x1fce3ed181f0cece, 0x4411885522991111, + 0x068f0c8903838f8f, 0x254e4a6b9c044e4e, 0xe6b7d1517366b7b7, 0x8beb0b60cbe0ebeb, + 0xf03cfdcc78c13c3c, 0x3e817cbf1ffd8181, 0x6a94d4fe35409494, 0xfbf7eb0cf31cf7f7, + 0xdeb9a1676f18b9b9, 0x4c13985f268b1313, 0xb02c7d9c58512c2c, 0x6bd3d6b8bb05d3d3, + 0xbbe76b5cd38ce7e7, 0xa56e57cbdc396e6e, 0x37c46ef395aac4c4, 0x0c03180f061b0303, + 0x45568a13acdc5656, 0x0d441a49885e4444, 0xe17fdf9efea07f7f, 0x9ea921374f88a9a9, + 0xa82a4d8254672a2a, 0xd6bbb16d6b0abbbb, 0x23c146e29f87c1c1, 0x5153a202a6f15353, + 0x57dcae8ba572dcdc, 0x2c0b582716530b0b, 0x4e9d9cd327019d9d, 0xad6c47c1d82b6c6c, + 0xc43195f562a43131, 0xcd7487b9e8f37474, 0xfff6e309f115f6f6, 0x05460a438c4c4646, + 0x8aac092645a5acac, 0x1e893c970fb58989, 0x5014a04428b41414, 0xa3e15b42dfbae1e1, + 0x5816b04e2ca61616, 0xe83acdd274f73a3a, 0xb9696fd0d2066969, 0x2409482d12410909, + 0xdd70a7ade0d77070, 0xe2b6d954716fb6b6, 0x67d0ceb7bd1ed0d0, 0x93ed3b7ec7d6eded, + 0x17cc2edb85e2cccc, 0x15422a5784684242, 0x5a98b4c22d2c9898, 0xaaa4490e55eda4a4, + 0xa0285d8850752828, 0x6d5cda31b8865c5c, 0xc7f8933fed6bf8f8, 0x228644a411c28686, +} + +C7 := [256]u64 { + 0x186018c07830d818, 0x238c2305af462623, 0xc63fc67ef991b8c6, 0xe887e8136fcdfbe8, + 0x8726874ca113cb87, 0xb8dab8a9626d11b8, 0x0104010805020901, 0x4f214f426e9e0d4f, + 0x36d836adee6c9b36, 0xa6a2a6590451ffa6, 0xd26fd2debdb90cd2, 0xf5f3f5fb06f70ef5, + 0x79f979ef80f29679, 0x6fa16f5fcede306f, 0x917e91fcef3f6d91, 0x525552aa07a4f852, + 0x609d6027fdc04760, 0xbccabc89766535bc, 0x9b569baccd2b379b, 0x8e028e048c018a8e, + 0xa3b6a371155bd2a3, 0x0c300c603c186c0c, 0x7bf17bff8af6847b, 0x35d435b5e16a8035, + 0x1d741de8693af51d, 0xe0a7e05347ddb3e0, 0xd77bd7f6acb321d7, 0xc22fc25eed999cc2, + 0x2eb82e6d965c432e, 0x4b314b627a96294b, 0xfedffea321e15dfe, 0x5741578216aed557, + 0x155415a8412abd15, 0x77c1779fb6eee877, 0x37dc37a5eb6e9237, 0xe5b3e57b56d79ee5, + 0x9f469f8cd923139f, 0xf0e7f0d317fd23f0, 0x4a354a6a7f94204a, 0xda4fda9e95a944da, + 0x587d58fa25b0a258, 0xc903c906ca8fcfc9, 0x29a429558d527c29, 0x0a280a5022145a0a, + 0xb1feb1e14f7f50b1, 0xa0baa0691a5dc9a0, 0x6bb16b7fdad6146b, 0x852e855cab17d985, + 0xbdcebd8173673cbd, 0x5d695dd234ba8f5d, 0x1040108050209010, 0xf4f7f4f303f507f4, + 0xcb0bcb16c08bddcb, 0x3ef83eedc67cd33e, 0x05140528110a2d05, 0x6781671fe6ce7867, + 0xe4b7e47353d597e4, 0x279c2725bb4e0227, 0x4119413258827341, 0x8b168b2c9d0ba78b, + 0xa7a6a7510153f6a7, 0x7de97dcf94fab27d, 0x956e95dcfb374995, 0xd847d88e9fad56d8, + 0xfbcbfb8b30eb70fb, 0xee9fee2371c1cdee, 0x7ced7cc791f8bb7c, 0x66856617e3cc7166, + 0xdd53dda68ea77bdd, 0x175c17b84b2eaf17, 0x47014702468e4547, 0x9e429e84dc211a9e, + 0xca0fca1ec589d4ca, 0x2db42d75995a582d, 0xbfc6bf9179632ebf, 0x071c07381b0e3f07, + 0xad8ead012347acad, 0x5a755aea2fb4b05a, 0x8336836cb51bef83, 0x33cc3385ff66b633, + 0x6391633ff2c65c63, 0x020802100a041202, 0xaa92aa39384993aa, 0x71d971afa8e2de71, + 0xc807c80ecf8dc6c8, 0x196419c87d32d119, 0x4939497270923b49, 0xd943d9869aaf5fd9, + 0xf2eff2c31df931f2, 0xe3abe34b48dba8e3, 0x5b715be22ab6b95b, 0x881a8834920dbc88, + 0x9a529aa4c8293e9a, 0x2698262dbe4c0b26, 0x32c8328dfa64bf32, 0xb0fab0e94a7d59b0, + 0xe983e91b6acff2e9, 0x0f3c0f78331e770f, 0xd573d5e6a6b733d5, 0x803a8074ba1df480, + 0xbec2be997c6127be, 0xcd13cd26de87ebcd, 0x34d034bde4688934, 0x483d487a75903248, + 0xffdbffab24e354ff, 0x7af57af78ff48d7a, 0x907a90f4ea3d6490, 0x5f615fc23ebe9d5f, + 0x2080201da0403d20, 0x68bd6867d5d00f68, 0x1a681ad07234ca1a, 0xae82ae192c41b7ae, + 0xb4eab4c95e757db4, 0x544d549a19a8ce54, 0x937693ece53b7f93, 0x2288220daa442f22, + 0x648d6407e9c86364, 0xf1e3f1db12ff2af1, 0x73d173bfa2e6cc73, 0x124812905a248212, + 0x401d403a5d807a40, 0x0820084028104808, 0xc32bc356e89b95c3, 0xec97ec337bc5dfec, + 0xdb4bdb9690ab4ddb, 0xa1bea1611f5fc0a1, 0x8d0e8d1c8307918d, 0x3df43df5c97ac83d, + 0x976697ccf1335b97, 0x0000000000000000, 0xcf1bcf36d483f9cf, 0x2bac2b4587566e2b, + 0x76c57697b3ece176, 0x82328264b019e682, 0xd67fd6fea9b128d6, 0x1b6c1bd87736c31b, + 0xb5eeb5c15b7774b5, 0xaf86af112943beaf, 0x6ab56a77dfd41d6a, 0x505d50ba0da0ea50, + 0x450945124c8a5745, 0xf3ebf3cb18fb38f3, 0x30c0309df060ad30, 0xef9bef2b74c3c4ef, + 0x3ffc3fe5c37eda3f, 0x554955921caac755, 0xa2b2a2791059dba2, 0xea8fea0365c9e9ea, + 0x6589650fecca6a65, 0xbad2bab9686903ba, 0x2fbc2f65935e4a2f, 0xc027c04ee79d8ec0, + 0xde5fdebe81a160de, 0x1c701ce06c38fc1c, 0xfdd3fdbb2ee746fd, 0x4d294d52649a1f4d, + 0x927292e4e0397692, 0x75c9758fbceafa75, 0x061806301e0c3606, 0x8a128a249809ae8a, + 0xb2f2b2f940794bb2, 0xe6bfe66359d185e6, 0x0e380e70361c7e0e, 0x1f7c1ff8633ee71f, + 0x62956237f7c45562, 0xd477d4eea3b53ad4, 0xa89aa829324d81a8, 0x966296c4f4315296, + 0xf9c3f99b3aef62f9, 0xc533c566f697a3c5, 0x25942535b14a1025, 0x597959f220b2ab59, + 0x842a8454ae15d084, 0x72d572b7a7e4c572, 0x39e439d5dd72ec39, 0x4c2d4c5a6198164c, + 0x5e655eca3bbc945e, 0x78fd78e785f09f78, 0x38e038ddd870e538, 0x8c0a8c148605988c, + 0xd163d1c6b2bf17d1, 0xa5aea5410b57e4a5, 0xe2afe2434dd9a1e2, 0x6199612ff8c24e61, + 0xb3f6b3f1457b42b3, 0x21842115a5423421, 0x9c4a9c94d625089c, 0x1e781ef0663cee1e, + 0x4311432252866143, 0xc73bc776fc93b1c7, 0xfcd7fcb32be54ffc, 0x0410042014082404, + 0x515951b208a2e351, 0x995e99bcc72f2599, 0x6da96d4fc4da226d, 0x0d340d68391a650d, + 0xfacffa8335e979fa, 0xdf5bdfb684a369df, 0x7ee57ed79bfca97e, 0x2490243db4481924, + 0x3bec3bc5d776fe3b, 0xab96ab313d4b9aab, 0xce1fce3ed181f0ce, 0x1144118855229911, + 0x8f068f0c8903838f, 0x4e254e4a6b9c044e, 0xb7e6b7d1517366b7, 0xeb8beb0b60cbe0eb, + 0x3cf03cfdcc78c13c, 0x813e817cbf1ffd81, 0x946a94d4fe354094, 0xf7fbf7eb0cf31cf7, + 0xb9deb9a1676f18b9, 0x134c13985f268b13, 0x2cb02c7d9c58512c, 0xd36bd3d6b8bb05d3, + 0xe7bbe76b5cd38ce7, 0x6ea56e57cbdc396e, 0xc437c46ef395aac4, 0x030c03180f061b03, + 0x5645568a13acdc56, 0x440d441a49885e44, 0x7fe17fdf9efea07f, 0xa99ea921374f88a9, + 0x2aa82a4d8254672a, 0xbbd6bbb16d6b0abb, 0xc123c146e29f87c1, 0x535153a202a6f153, + 0xdc57dcae8ba572dc, 0x0b2c0b582716530b, 0x9d4e9d9cd327019d, 0x6cad6c47c1d82b6c, + 0x31c43195f562a431, 0x74cd7487b9e8f374, 0xf6fff6e309f115f6, 0x4605460a438c4c46, + 0xac8aac092645a5ac, 0x891e893c970fb589, 0x145014a04428b414, 0xe1a3e15b42dfbae1, + 0x165816b04e2ca616, 0x3ae83acdd274f73a, 0x69b9696fd0d20669, 0x092409482d124109, + 0x70dd70a7ade0d770, 0xb6e2b6d954716fb6, 0xd067d0ceb7bd1ed0, 0xed93ed3b7ec7d6ed, + 0xcc17cc2edb85e2cc, 0x4215422a57846842, 0x985a98b4c22d2c98, 0xa4aaa4490e55eda4, + 0x28a0285d88507528, 0x5c6d5cda31b8865c, 0xf8c7f8933fed6bf8, 0x86228644a411c286, +} + +RC := [ROUNDS + 1]u64 { + 0x0000000000000000, + 0x1823c6e887b8014f, + 0x36a6d2f5796f9152, + 0x60bc9b8ea30c7b35, + 0x1de0d7c22e4bfe57, + 0x157737e59ff04ada, + 0x58c9290ab1a06b85, + 0xbd5d10f4cb3e0567, + 0xe427418ba77d95d8, + 0xfbee7c66dd17479e, + 0xca2dbf07ad5a8333, +} + +transform :: proc (ctx: ^Whirlpool_Context) { + K, block, state, L: [8]u64 + + for i := 0; i < 8; i += 1 {block[i] = util.U64_BE(ctx.buffer[8 * i:])} + + for i := 0; i < 8; i += 1 { + K[i] = ctx.hash[i] + state[i] = block[i] ~ K[i] + } + + for r := 1; r <= ROUNDS; r += 1 { + for i := 0; i < 8; i += 1 { + L[i] = C0[byte(K[i % 8] >> 56)] ~ + C1[byte(K[(i + 7) % 8] >> 48)] ~ + C2[byte(K[(i + 6) % 8] >> 40)] ~ + C3[byte(K[(i + 5) % 8] >> 32)] ~ + C4[byte(K[(i + 4) % 8] >> 24)] ~ + C5[byte(K[(i + 3) % 8] >> 16)] ~ + C6[byte(K[(i + 2) % 8] >> 8)] ~ + C7[byte(K[(i + 1) % 8])] + } + L[0] ~= RC[r] + + for i := 0; i < 8; i += 1 {K[i] = L[i]} + + for i := 0; i < 8; i += 1 { + L[i] = C0[byte(state[i % 8] >> 56)] ~ + C1[byte(state[(i + 7) % 8] >> 48)] ~ + C2[byte(state[(i + 6) % 8] >> 40)] ~ + C3[byte(state[(i + 5) % 8] >> 32)] ~ + C4[byte(state[(i + 4) % 8] >> 24)] ~ + C5[byte(state[(i + 3) % 8] >> 16)] ~ + C6[byte(state[(i + 2) % 8] >> 8)] ~ + C7[byte(state[(i + 1) % 8])] ~ + K[i % 8] + } + for i := 0; i < 8; i += 1 {state[i] = L[i]} + } + for i := 0; i < 8; i += 1 {ctx.hash[i] ~= state[i] ~ block[i]} +} + +update_odin :: proc(ctx: ^Whirlpool_Context, source: []byte) { + source_pos: int + nn := len(source) + source_bits := u64(nn * 8) + source_gap := u32((8 - (int(source_bits & 7))) & 7) + buffer_rem := uint(ctx.buffer_bits & 7) + b: u32 + + for i, carry, value := 31, u32(0), u32(source_bits); i >= 0 && (carry != 0 || value != 0); i -= 1 { + carry += u32(ctx.bitlength[i]) + (u32(value & 0xff)) + ctx.bitlength[i] = byte(carry) + carry >>= 8 + value >>= 8 + } + + for source_bits > 8 { + b = u32(u32((source[source_pos] << source_gap) & 0xff) | u32((source[source_pos+1] & 0xff) >> (8 - source_gap))) + + ctx.buffer[ctx.buffer_pos] |= u8(b >> buffer_rem) + ctx.buffer_pos += 1 + ctx.buffer_bits += int(8 - buffer_rem) + + if ctx.buffer_bits == 512 { + transform(ctx) + ctx.buffer_bits = 0 + ctx.buffer_pos = 0 + } + ctx.buffer[ctx.buffer_pos] = byte(b << (8 - buffer_rem)) + ctx.buffer_bits += int(buffer_rem) + source_bits -= 8 + source_pos += 1 + } + + if source_bits > 0 { + b = u32((source[source_pos] << source_gap) & 0xff) + ctx.buffer[ctx.buffer_pos] |= byte(b) >> buffer_rem + } else {b = 0} + + if u64(buffer_rem) + source_bits < 8 { + ctx.buffer_bits += int(source_bits) + } else { + ctx.buffer_pos += 1 + ctx.buffer_bits += 8 - int(buffer_rem) + source_bits -= u64(8 - buffer_rem) + + if ctx.buffer_bits == 512 { + transform(ctx) + ctx.buffer_bits = 0 + ctx.buffer_pos = 0 + } + ctx.buffer[ctx.buffer_pos] = byte(b << (8 - buffer_rem)) + ctx.buffer_bits += int(source_bits) + } +} + +final_odin :: proc(ctx: ^Whirlpool_Context, hash: []byte) { + n := ctx + n.buffer[n.buffer_pos] |= 0x80 >> (uint(n.buffer_bits) & 7) + n.buffer_pos += 1 + + if n.buffer_pos > 64 - 32 { + if n.buffer_pos < 64 { + for i := 0; i < 64 - n.buffer_pos; i += 1 { + n.buffer[n.buffer_pos + i] = 0 + } + } + transform(ctx) + n.buffer_pos = 0 + } + + if n.buffer_pos < 64 - 32 { + for i := 0; i < (64 - 32) - n.buffer_pos; i += 1 { + n.buffer[n.buffer_pos + i] = 0 + } + } + n.buffer_pos = 64 - 32 + + for i := 0; i < 32; i += 1 { + n.buffer[n.buffer_pos + i] = n.bitlength[i] + } + transform(ctx) + + for i := 0; i < 8; i += 1 { + hash[i * 8] = byte(n.hash[i] >> 56) + hash[i * 8 + 1] = byte(n.hash[i] >> 48) + hash[i * 8 + 2] = byte(n.hash[i] >> 40) + hash[i * 8 + 3] = byte(n.hash[i] >> 32) + hash[i * 8 + 4] = byte(n.hash[i] >> 24) + hash[i * 8 + 5] = byte(n.hash[i] >> 16) + hash[i * 8 + 6] = byte(n.hash[i] >> 8) + hash[i * 8 + 7] = byte(n.hash[i]) + } +} \ No newline at end of file diff --git a/tests/core/build.bat b/tests/core/build.bat index d90759841..e0a98eeb1 100644 --- a/tests/core/build.bat +++ b/tests/core/build.bat @@ -25,4 +25,9 @@ echo --- echo --- echo Running core:odin tests echo --- -%PATH_TO_ODIN% run odin %COMMON% -o:size \ No newline at end of file +%PATH_TO_ODIN% run odin %COMMON% -o:size + +echo --- +echo Running core:crypto hash tests +echo --- +%PATH_TO_ODIN% run crypto %COMMON% \ No newline at end of file diff --git a/tests/core/crypto/test_core_crypto.odin b/tests/core/crypto/test_core_crypto.odin new file mode 100644 index 000000000..a50d67ddf --- /dev/null +++ b/tests/core/crypto/test_core_crypto.odin @@ -0,0 +1,1279 @@ +package test_core_crypto + +/* + Copyright 2021 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Test runner setup. + + Tests for the hashing algorithms within the crypto library. + Where possible, the official test vectors are used to validate the implementation. +*/ + +import "core:testing" +import "core:fmt" + +import "core:crypto/md2" +import "core:crypto/md4" +import "core:crypto/md5" +import "core:crypto/sha1" +import "core:crypto/sha2" +import "core:crypto/sha3" +import "core:crypto/keccak" +import "core:crypto/shake" +import "core:crypto/whirlpool" +import "core:crypto/ripemd" +import "core:crypto/blake" +import "core:crypto/blake2b" +import "core:crypto/blake2s" +import "core:crypto/tiger" +import "core:crypto/tiger2" +import "core:crypto/gost" +import "core:crypto/streebog" +import "core:crypto/sm3" +import "core:crypto/skein" +import "core:crypto/jh" +import "core:crypto/groestl" +import "core:crypto/haval" + +TEST_count := 0 +TEST_fail := 0 + +when ODIN_TEST { + expect :: testing.expect + log :: testing.log +} else { + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + fmt.printf("[%v] ", loc) + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.println(message) + return + } + fmt.println(" PASS") + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } +} + +main :: proc() { + t := testing.T{} + test_md2(&t) + test_md4(&t) + test_md5(&t) + test_sha1(&t) + test_sha224(&t) + test_sha256(&t) + test_sha384(&t) + test_sha512(&t) + test_sha3_224(&t) + test_sha3_256(&t) + test_sha3_384(&t) + test_sha3_512(&t) + test_shake_128(&t) + test_shake_256(&t) + test_keccak_224(&t) + test_keccak_256(&t) + test_keccak_384(&t) + test_keccak_512(&t) + test_whirlpool(&t) + test_gost(&t) + test_streebog_256(&t) + test_streebog_512(&t) + test_blake_224(&t) + test_blake_256(&t) + test_blake_384(&t) + test_blake_512(&t) + test_blake2b(&t) + test_blake2s(&t) + test_ripemd_128(&t) + test_ripemd_160(&t) + test_ripemd_256(&t) + test_ripemd_320(&t) + test_tiger_128(&t) + test_tiger_160(&t) + test_tiger_192(&t) + test_tiger2_128(&t) + test_tiger2_160(&t) + test_tiger2_192(&t) + test_sm3(&t) + test_skein512(&t) + test_jh_224(&t) + test_jh_256(&t) + test_jh_384(&t) + test_jh_512(&t) + test_groestl_224(&t) + test_groestl_256(&t) + test_groestl_384(&t) + test_groestl_512(&t) + test_haval_128(&t) + test_haval_160(&t) + test_haval_192(&t) + test_haval_224(&t) + test_haval_256(&t) + + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) +} + +TestHash :: struct { + hash: string, + str: string, +} + +hex_string :: proc(bytes: []byte, allocator := context.temp_allocator) -> string { + lut: [16]byte = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'} + buf := make([]byte, len(bytes) * 2, allocator) + for i := 0; i < len(bytes); i += 1 { + buf[i * 2 + 0] = lut[bytes[i] >> 4 & 0xf] + buf[i * 2 + 1] = lut[bytes[i] & 0xf] + } + return string(buf) +} + +@(test) +test_md2 :: proc(t: ^testing.T) { + // Official test vectors from https://datatracker.ietf.org/doc/html/rfc1319 + test_vectors := [?]TestHash { + TestHash{"8350e5a3e24c153df2275c9f80692773", ""}, + TestHash{"32ec01ec4a6dac72c0ab96fb34c0b5d1", "a"}, + TestHash{"da853b0d3f88d99b30283a69e6ded6bb", "abc"}, + TestHash{"ab4f496bfb2a530b219ff33031fe06b0", "message digest"}, + TestHash{"4e8ddff3650292ab5a4108c3aa47940b", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"da33def2a42df13975352846c30338cd", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + TestHash{"d5976f79d83d3a0dc9806c3c66f3efd8", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := md2.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_md4 :: proc(t: ^testing.T) { + // Official test vectors from https://datatracker.ietf.org/doc/html/rfc1320 + test_vectors := [?]TestHash { + TestHash{"31d6cfe0d16ae931b73c59d7e0c089c0", ""}, + TestHash{"bde52cb31de33e46245e05fbdbd6fb24", "a"}, + TestHash{"a448017aaf21d8525fc10ae87aa6729d", "abc"}, + TestHash{"d9130a8164549fe818874806e1c7014b", "message digest"}, + TestHash{"d79e1c308aa5bbcdeea8ed63df412da9", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"043f8582f241db351ce627e153e7f0e4", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + TestHash{"e33b4ddc9c38f2199c3e7b164fcc0536", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := md4.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + md4.use_botan() + for v, _ in test_vectors { + computed := md4.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + +} + +@(test) +test_md5 :: proc(t: ^testing.T) { + // Official test vectors from https://datatracker.ietf.org/doc/html/rfc1321 + test_vectors := [?]TestHash { + TestHash{"d41d8cd98f00b204e9800998ecf8427e", ""}, + TestHash{"0cc175b9c0f1b6a831c399e269772661", "a"}, + TestHash{"900150983cd24fb0d6963f7d28e17f72", "abc"}, + TestHash{"f96b697d7cb7938d525a2f31aaf161d0", "message digest"}, + TestHash{"c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"d174ab98d277d9f5a5611c2c9f419d9f", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + TestHash{"57edf4a22be3c955ac49da2e2107b67a", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := md5.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + md5.use_botan() + for v, _ in test_vectors { + computed := md5.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_sha1 :: proc(t: ^testing.T) { + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"da39a3ee5e6b4b0d3255bfef95601890afd80709", ""}, + TestHash{"a9993e364706816aba3e25717850c26c9cd0d89d", "abc"}, + TestHash{"f9537c23893d2014f365adf8ffe33b8eb0297ed1", "abcdbcdecdefdefgefghfghighijhi"}, + TestHash{"346fb528a24b48f563cb061470bcfd23740427ad", "jkijkljklmklmnlmnomnopnopq"}, + TestHash{"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "a"}, + TestHash{"c729c8996ee0a6f74f4f3248e8957edf704fb624", "01234567012345670123456701234567"}, + TestHash{"84983e441c3bd26ebaae4aa1f95129e5e54670f1", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"a49b2446a02c645bf419f995b67091253a04a259", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha1.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + sha1.use_botan() + for v, _ in test_vectors { + computed := sha1.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + + +@(test) +test_sha224 :: proc(t: ^testing.T) { + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""}, + TestHash{"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc"}, + TestHash{"75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha2.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + sha2.use_botan() + for v, _ in test_vectors { + computed := sha2.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_sha256 :: proc(t: ^testing.T) { + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""}, + TestHash{"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc"}, + TestHash{"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha2.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + sha2.use_botan() + for v, _ in test_vectors { + computed := sha2.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + +} + +@(test) +test_sha384 :: proc(t: ^testing.T) { + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", ""}, + TestHash{"cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7", "abc"}, + TestHash{"3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha2.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + sha2.use_botan() + for v, _ in test_vectors { + computed := sha2.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + +} + +@(test) +test_sha512 :: proc(t: ^testing.T) { + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", ""}, + TestHash{"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", "abc"}, + TestHash{"204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha2.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + sha2.use_botan() + for v, _ in test_vectors { + computed := sha2.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + + +@(test) +test_sha3_224 :: proc(t: ^testing.T) { + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", ""}, + TestHash{"e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc"}, + TestHash{"10241ac5187380bd501192e4e56b5280908727dd8fe0d10d4e5ad91e", "abcdbcdecdefdefgefghfghighijhi"}, + TestHash{"fd645fe07d814c397e85e85f92fe58b949f55efa4d3468b2468da45a", "jkijkljklmklmnlmnomnopnopq"}, + TestHash{"9e86ff69557ca95f405f081269685b38e3a819b309ee942f482b6a8b", "a"}, + TestHash{"6961f694b2ff3ed6f0c830d2c66da0c5e7ca9445f7c0dca679171112", "01234567012345670123456701234567"}, + TestHash{"8a24108b154ada21c9fd5574494479ba5c7e7ab76ef264ead0fcce33", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"543e6868e1666c1a643630df77367ae5a62a85070a51c14cbf665cbc", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha3.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + sha3.use_botan() + for v, _ in test_vectors { + computed := sha3.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_sha3_256 :: proc(t: ^testing.T) { + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", ""}, + TestHash{"3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", "abc"}, + TestHash{"565ada1ced21278cfaffdde00dea0107964121ac25e4e978abc59412be74550a", "abcdbcdecdefdefgefghfghighijhi"}, + TestHash{"8cc1709d520f495ce972ece48b0d2e1f74ec80d53bc5c47457142158fae15d98", "jkijkljklmklmnlmnomnopnopq"}, + TestHash{"80084bf2fba02475726feb2cab2d8215eab14bc6bdd8bfb2c8151257032ecd8b", "a"}, + TestHash{"e4786de5f88f7d374b7288f225ea9f2f7654da200bab5d417e1fb52d49202767", "01234567012345670123456701234567"}, + TestHash{"41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha3.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + sha3.use_botan() + for v, _ in test_vectors { + computed := sha3.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_sha3_384 :: proc(t: ^testing.T) { + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004", ""}, + TestHash{"ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25", "abc"}, + TestHash{"9aa92dbb716ebb573def0d5e3cdd28d6add38ada310b602b8916e690a3257b7144e5ddd3d0dbbc559c48480d34d57a9a", "abcdbcdecdefdefgefghfghighijhi"}, + TestHash{"77c90323d7392bcdee8a3e7f74f19f47b7d1b1a825ac6a2d8d882a72317879cc26597035f1fc24fe65090b125a691282", "jkijkljklmklmnlmnomnopnopq"}, + TestHash{"1815f774f320491b48569efec794d249eeb59aae46d22bf77dafe25c5edc28d7ea44f93ee1234aa88f61c91912a4ccd9", "a"}, + TestHash{"51072590ad4c51b27ff8265590d74f92de7cc55284168e414ca960087c693285b08a283c6b19d77632994cb9eb93f1be", "01234567012345670123456701234567"}, + TestHash{"991c665755eb3a4b6bbdfb75c78a492e8c56a22c5c4d7e429bfdbc32b9d4ad5aa04a1f076e62fea19eef51acd0657c22", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"79407d3b5916b59c3e30b09822974791c313fb9ecc849e406f23592d04f625dc8c709b98b43b3852b337216179aa7fc7", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha3.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + sha3.use_botan() + for v, _ in test_vectors { + computed := sha3.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_sha3_512 :: proc(t: ^testing.T) { + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26", ""}, + TestHash{"b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0", "abc"}, + TestHash{"9f9a327944a35988d67effc4fa748b3c07744f736ac70b479d8e12a3d10d6884d00a7ef593690305462e9e9030a67c51636fd346fd8fa0ee28a5ac2aee103d2e", "abcdbcdecdefdefgefghfghighijhi"}, + TestHash{"dbb124a0deda966eb4d199d0844fa0beb0770ea1ccddabcd335a7939a931ac6fb4fa6aebc6573f462ced2e4e7178277803be0d24d8bc2864626d9603109b7891", "jkijkljklmklmnlmnomnopnopq"}, + TestHash{"697f2d856172cb8309d6b8b97dac4de344b549d4dee61edfb4962d8698b7fa803f4f93ff24393586e28b5b957ac3d1d369420ce53332712f997bd336d09ab02a", "a"}, + TestHash{"5679e353bc8eeea3e801ca60448b249bcfd3ac4a6c3abe429a807bcbd4c9cd12da87a5a9dc74fde64c0d44718632cae966b078397c6f9ec155c6a238f2347cf1", "01234567012345670123456701234567"}, + TestHash{"04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee691fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha3.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + sha3.use_botan() + for v, _ in test_vectors { + computed := sha3.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_shake_128 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"7f9c2ba4e88f827d616045507605853e", ""}, + TestHash{"f4202e3c5852f9182a0430fd8144f0a7", "The quick brown fox jumps over the lazy dog"}, + TestHash{"853f4538be0db9621a6cea659a06c110", "The quick brown fox jumps over the lazy dof"}, + } + for v, _ in test_vectors { + computed := shake.hash_128(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + sha3.use_botan() + for v, _ in test_vectors { + computed := shake.hash_128(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_shake_256 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f", ""}, + TestHash{"2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca", "The quick brown fox jumps over the lazy dog"}, + TestHash{"46b1ebb2e142c38b9ac9081bef72877fe4723959640fa57119b366ce6899d401", "The quick brown fox jumps over the lazy dof"}, + } + for v, _ in test_vectors { + computed := shake.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + sha3.use_botan() + for v, _ in test_vectors { + computed := shake.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_keccak_224 :: proc(t: ^testing.T) { + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd", ""}, + TestHash{"c30411768506ebe1c2871b1ee2e87d38df342317300a9b97a95ec6a8", "abc"}, + } + for v, _ in test_vectors { + computed := keccak.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + keccak.use_botan() + for v, _ in test_vectors { + computed := keccak.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_keccak_256 :: proc(t: ^testing.T) { + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", ""}, + TestHash{"4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc"}, + } + for v, _ in test_vectors { + computed := keccak.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + keccak.use_botan() + for v, _ in test_vectors { + computed := keccak.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_keccak_384 :: proc(t: ^testing.T) { + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff", ""}, + TestHash{"f7df1165f033337be098e7d288ad6a2f74409d7a60b49c36642218de161b1f99f8c681e4afaf31a34db29fb763e3c28e", "abc"}, + } + for v, _ in test_vectors { + computed := keccak.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + keccak.use_botan() + for v, _ in test_vectors { + computed := keccak.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_keccak_512 :: proc(t: ^testing.T) { + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", ""}, + TestHash{"18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc"}, + } + for v, _ in test_vectors { + computed := keccak.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + keccak.use_botan() + for v, _ in test_vectors { + computed := keccak.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_whirlpool :: proc(t: ^testing.T) { + // Test vectors from + // https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html + test_vectors := [?]TestHash { + TestHash{"19fa61d75522a4669b44e39c1d2e1726c530232130d407f89afee0964997f7a73e83be698b288febcf88e3e03c4f0757ea8964e59b63d93708b138cc42a66eb3", ""}, + TestHash{"8aca2602792aec6f11a67206531fb7d7f0dff59413145e6973c45001d0087b42d11bc645413aeff63a42391a39145a591a92200d560195e53b478584fdae231a", "a"}, + TestHash{"33e24e6cbebf168016942df8a7174048f9cebc45cbd829c3b94b401a498acb11c5abcca7f2a1238aaf534371e87a4e4b19758965d5a35a7cad87cf5517043d97", "ab"}, + TestHash{"4e2448a4c6f486bb16b6562c73b4020bf3043e3a731bce721ae1b303d97e6d4c7181eebdb6c57e277d0e34957114cbd6c797fc9d95d8b582d225292076d4eef5", "abc"}, + TestHash{"bda164f0b930c43a1bacb5df880b205d15ac847add35145bf25d991ae74f0b72b1ac794f8aacda5fcb3c47038c954742b1857b5856519de4d1e54bfa2fa4eac5", "abcd"}, + TestHash{"5d745e26ccb20fe655d39c9e7f69455758fbae541cb892b3581e4869244ab35b4fd6078f5d28b1f1a217452a67d9801033d92724a221255a5e377fe9e9e5f0b2", "abcde"}, + TestHash{"a73e425459567308ba5f9eb2ae23570d0d0575eb1357ecf6ac88d4e0358b0ac3ea2371261f5d4c070211784b525911b9eec0ad968429bb7c7891d341cff4e811", "abcdef"}, + TestHash{"08b388f68fd3eb51906ac3d3c699b8e9c3ac65d7ceb49d2e34f8a482cbc3082bc401cead90e85a97b8647c948bf35e448740b79659f3bee42145f0bd653d1f25", "abcdefg"}, + TestHash{"1f1a84d30612820243afe2022712f9dac6d07c4c8bb41b40eacab0184c8d82275da5bcadbb35c7ca1960ff21c90acbae8c14e48d9309e4819027900e882c7ad9", "abcdefgh"}, + TestHash{"11882bc9a31ac1cf1c41dcd9fd6fdd3ccdb9b017fc7f4582680134f314d7bb49af4c71f5a920bc0a6a3c1ff9a00021bf361d9867fe636b0bc1da1552e4237de4", "abcdefghi"}, + TestHash{"717163de24809ffcf7ff6d5aba72b8d67c2129721953c252a4ddfb107614be857cbd76a9d5927de14633d6bdc9ddf335160b919db5c6f12cb2e6549181912eef", "abcdefghij"}, + TestHash{"b97de512e91e3828b40d2b0fdce9ceb3c4a71f9bea8d88e75c4fa854df36725fd2b52eb6544edcacd6f8beddfea403cb55ae31f03ad62a5ef54e42ee82c3fb35", "The quick brown fox jumps over the lazy dog"}, + TestHash{"c27ba124205f72e6847f3e19834f925cc666d0974167af915bb462420ed40cc50900d85a1f923219d832357750492d5c143011a76988344c2635e69d06f2d38c", "The quick brown fox jumps over the lazy eog"}, + } + for v, _ in test_vectors { + computed := whirlpool.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + whirlpool.use_botan() + for v, _ in test_vectors { + computed := whirlpool.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_gost :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"981e5f3ca30c841487830f84fb433e13ac1101569b9c13584ac483234cd656c0", ""}, + TestHash{"e74c52dd282183bf37af0079c9f78055715a103f17e3133ceff1aacf2f403011", "a"}, + TestHash{"b285056dbf18d7392d7677369524dd14747459ed8143997e163b2986f92fd42c", "abc"}, + TestHash{"bc6041dd2aa401ebfa6e9886734174febdb4729aa972d60f549ac39b29721ba0", "message digest"}, + TestHash{"9004294a361a508c586fe53d1f1b02746765e71b765472786e4770d565830a76", "The quick brown fox jumps over the lazy dog"}, + TestHash{"73b70a39497de53a6e08c67b6d4db853540f03e9389299d9b0156ef7e85d0f61", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + TestHash{"6bc7b38989b28cf93ae8842bf9d752905910a7528a61e5bce0782de43e610c90", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + TestHash{"2cefc2f7b7bdc514e18ea57fa74ff357e7fa17d652c75f69cb1be7893ede48eb", "This is message, length=32 bytes"}, + TestHash{"c3730c5cbccacf915ac292676f21e8bd4ef75331d9405e5f1a61dc3130a65011", "Suppose the original message has length = 50 bytes"}, + } + for v, _ in test_vectors { + computed := gost.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + gost.use_botan() + for v, _ in test_vectors { + computed := gost.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_streebog_256 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"3f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb", ""}, + TestHash{"3e7dea7f2384b6c5a3d0e24aaa29c05e89ddd762145030ec22c71a6db8b2c1f4", "The quick brown fox jumps over the lazy dog"}, + TestHash{"36816a824dcbe7d6171aa58500741f2ea2757ae2e1784ab72c5c3c6c198d71da", "The quick brown fox jumps over the lazy dog."}, + } + for v, _ in test_vectors { + computed := streebog.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + streebog.use_botan() + for v, _ in test_vectors { + computed := streebog.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_streebog_512 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"8e945da209aa869f0455928529bcae4679e9873ab707b55315f56ceb98bef0a7362f715528356ee83cda5f2aac4c6ad2ba3a715c1bcd81cb8e9f90bf4c1c1a8a", ""}, + TestHash{"d2b793a0bb6cb5904828b5b6dcfb443bb8f33efc06ad09368878ae4cdc8245b97e60802469bed1e7c21a64ff0b179a6a1e0bb74d92965450a0adab69162c00fe", "The quick brown fox jumps over the lazy dog"}, + TestHash{"fe0c42f267d921f940faa72bd9fcf84f9f1bd7e9d055e9816e4c2ace1ec83be82d2957cd59b86e123d8f5adee80b3ca08a017599a9fc1a14d940cf87c77df070", "The quick brown fox jumps over the lazy dog."}, + } + for v, _ in test_vectors { + computed := streebog.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + streebog.use_botan() + for v, _ in test_vectors { + computed := streebog.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_blake_224 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"7dc5313b1c04512a174bd6503b89607aecbee0903d40a8a569c94eed", ""}, + TestHash{"304c27fdbf308aea06955e331adc6814223a21fccd24c09fde9eda7b", "ube"}, + TestHash{"cfb6848add73e1cb47994c4765df33b8f973702705a30a71fe4747a3", "BLAKE"}, + } + for v, _ in test_vectors { + computed := blake.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_blake_256 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"716f6e863f744b9ac22c97ec7b76ea5f5908bc5b2f67c61510bfc4751384ea7a", ""}, + TestHash{"e802fe2a73fbe5853408f051d040aeb3a76a4d7a0fc5c3415d1af090f76a2c81", "ube"}, + TestHash{"07663e00cf96fbc136cf7b1ee099c95346ba3920893d18cc8851f22ee2e36aa6", "BLAKE"}, + } + for v, _ in test_vectors { + computed := blake.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_blake_384 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"c6cbd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706", ""}, + TestHash{"8f22f120b2b99dd4fd32b98c8c83bd87abd6413f7317be936b1997511247fc68ae781c6f42113224ccbc1567b0e88593", "ube"}, + TestHash{"f28742f7243990875d07e6afcff962edabdf7e9d19ddea6eae31d094c7fa6d9b00c8213a02ddf1e2d9894f3162345d85", "BLAKE"}, + } + for v, _ in test_vectors { + computed := blake.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_blake_512 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"a8cfbbd73726062df0c6864dda65defe58ef0cc52a5625090fa17601e1eecd1b628e94f396ae402a00acc9eab77b4d4c2e852aaaa25a636d80af3fc7913ef5b8", ""}, + TestHash{"49a24ca8f230936f938c19484d46b58f13ea4448ddadafecdf01419b1e1dd922680be2de84069187973ab61b10574da2ee50cbeaade68ea9391c8ec041b76be0", "ube"}, + TestHash{"7bf805d0d8de36802b882e65d0515aa7682a2be97a9d9ec1399f4be2eff7de07684d7099124c8ac81c1c7c200d24ba68c6222e75062e04feb0e9dd589aa6e3b7", "BLAKE"}, + } + for v, _ in test_vectors { + computed := blake.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_blake2b :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce", ""}, + TestHash{"a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918", "The quick brown fox jumps over the lazy dog"}, + } + for v, _ in test_vectors { + computed := blake2b.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + blake2b.use_botan() + for v, _ in test_vectors { + computed := blake2b.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_blake2s :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9", ""}, + TestHash{"606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812", "The quick brown fox jumps over the lazy dog"}, + } + for v, _ in test_vectors { + computed := blake2s.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_ripemd_128 :: proc(t: ^testing.T) { + // Test vectors from + // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html + test_vectors := [?]TestHash { + TestHash{"cdf26213a150dc3ecb610f18f6b38b46", ""}, + TestHash{"86be7afa339d0fc7cfc785e72f578d33", "a"}, + TestHash{"c14a12199c66e4ba84636b0f69144c77", "abc"}, + TestHash{"9e327b3d6e523062afc1132d7df9d1b8", "message digest"}, + TestHash{"fd2aa607f71dc8f510714922b371834e", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"a1aa0689d0fafa2ddc22e88b49133a06", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"d1e959eb179c911faea4624c60c5c702", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + } + for v, _ in test_vectors { + computed := ripemd.hash_128(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_ripemd_160 :: proc(t: ^testing.T) { + // Test vectors from + // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html + test_vectors := [?]TestHash { + TestHash{"9c1185a5c5e9fc54612808977ee8f548b2258d31", ""}, + TestHash{"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", "a"}, + TestHash{"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", "abc"}, + TestHash{"5d0689ef49d2fae572b881b123a85ffa21595f36", "message digest"}, + TestHash{"f71c27109c692c1b56bbdceb5b9d2865b3708dbc", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"12a053384a9c0c88e405a06c27dcf49ada62eb2b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"b0e20b6e3116640286ed3a87a5713079b21f5189", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + } + for v, _ in test_vectors { + computed := ripemd.hash_160(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + ripemd.use_botan() + for v, _ in test_vectors { + computed := ripemd.hash_160(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_ripemd_256 :: proc(t: ^testing.T) { + // Test vectors from + // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html + test_vectors := [?]TestHash { + TestHash{"02ba4c4e5f8ecd1877fc52d64d30e37a2d9774fb1e5d026380ae0168e3c5522d", ""}, + TestHash{"f9333e45d857f5d90a91bab70a1eba0cfb1be4b0783c9acfcd883a9134692925", "a"}, + TestHash{"afbd6e228b9d8cbbcef5ca2d03e6dba10ac0bc7dcbe4680e1e42d2e975459b65", "abc"}, + TestHash{"87e971759a1ce47a514d5c914c392c9018c7c46bc14465554afcdf54a5070c0e", "message digest"}, + TestHash{"649d3034751ea216776bf9a18acc81bc7896118a5197968782dd1fd97d8d5133", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"3843045583aac6c8c8d9128573e7a9809afb2a0f34ccc36ea9e72f16f6368e3f", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"5740a408ac16b720b84424ae931cbb1fe363d1d0bf4017f1a89f7ea6de77a0b8", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + } + for v, _ in test_vectors { + computed := ripemd.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_ripemd_320 :: proc(t: ^testing.T) { + // Test vectors from + // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html + test_vectors := [?]TestHash { + TestHash{"22d65d5661536cdc75c1fdf5c6de7b41b9f27325ebc61e8557177d705a0ec880151c3a32a00899b8", ""}, + TestHash{"ce78850638f92658a5a585097579926dda667a5716562cfcf6fbe77f63542f99b04705d6970dff5d", "a"}, + TestHash{"de4c01b3054f8930a79d09ae738e92301e5a17085beffdc1b8d116713e74f82fa942d64cdbc4682d", "abc"}, + TestHash{"3a8e28502ed45d422f68844f9dd316e7b98533fa3f2a91d29f84d425c88d6b4eff727df66a7c0197", "message digest"}, + TestHash{"cabdb1810b92470a2093aa6bce05952c28348cf43ff60841975166bb40ed234004b8824463e6b009", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"d034a7950cf722021ba4b84df769a5de2060e259df4c9bb4a4268c0e935bbc7470a969c9d072a1ac", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"ed544940c86d67f250d232c30b7b3e5770e0c60c8cb9a4cafe3b11388af9920e1b99230b843c86a4", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + } + for v, _ in test_vectors { + computed := ripemd.hash_320(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_tiger_128 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"3293ac630c13f0245f92bbb1766e1616", ""}, + TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc", "a"}, + TestHash{"2aab1484e8c158f2bfb8c5ff41b57a52", "abc"}, + TestHash{"d981f8cb78201a950dcf3048751e441c", "message digest"}, + TestHash{"1714a472eee57d30040412bfcc55032a", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"8dcea680a17583ee502ba38a3c368651", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + TestHash{"1c14795529fd9f207a958f84c52f11e8", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + TestHash{"6d12a41e72e644f017b6f0e2f7b44c62", "The quick brown fox jumps over the lazy dog"}, + } + for v, _ in test_vectors { + computed := tiger.hash_128(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + tiger.use_botan() + for v, _ in test_vectors { + computed := tiger.hash_128(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_tiger_160 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"3293ac630c13f0245f92bbb1766e16167a4e5849", ""}, + TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc613e247f", "a"}, + TestHash{"2aab1484e8c158f2bfb8c5ff41b57a525129131c", "abc"}, + TestHash{"d981f8cb78201a950dcf3048751e441c517fca1a", "message digest"}, + TestHash{"1714a472eee57d30040412bfcc55032a0b11602f", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3a71c631e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"8dcea680a17583ee502ba38a3c368651890ffbcc", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + TestHash{"1c14795529fd9f207a958f84c52f11e887fa0cab", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + TestHash{"6d12a41e72e644f017b6f0e2f7b44c6285f06dd5", "The quick brown fox jumps over the lazy dog"}, + } + for v, _ in test_vectors { + computed := tiger.hash_160(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + tiger.use_botan() + for v, _ in test_vectors { + computed := tiger.hash_160(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_tiger_192 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"3293ac630c13f0245f92bbb1766e16167a4e58492dde73f3", ""}, + TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc613e247f5f247809", "a"}, + TestHash{"2aab1484e8c158f2bfb8c5ff41b57a525129131c957b5f93", "abc"}, + TestHash{"d981f8cb78201a950dcf3048751e441c517fca1aa55a29f6", "message digest"}, + TestHash{"1714a472eee57d30040412bfcc55032a0b11602ff37beee9", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3a71c631e7b53f78e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"8dcea680a17583ee502ba38a3c368651890ffbccdc49a8cc", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + TestHash{"1c14795529fd9f207a958f84c52f11e887fa0cabdfd91bfd", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + TestHash{"6d12a41e72e644f017b6f0e2f7b44c6285f06dd5d2c5b075", "The quick brown fox jumps over the lazy dog"}, + } + for v, _ in test_vectors { + computed := tiger.hash_192(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + tiger.use_botan() + for v, _ in test_vectors { + computed := tiger.hash_192(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_tiger2_128 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"4441be75f6018773c206c22745374b92", ""}, + TestHash{"976abff8062a2e9dcea3a1ace966ed9c", "The quick brown fox jumps over the lazy dog"}, + TestHash{"09c11330283a27efb51930aa7dc1ec62", "The quick brown fox jumps over the lazy cog"}, + } + for v, _ in test_vectors { + computed := tiger2.hash_128(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_tiger2_160 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"4441be75f6018773c206c22745374b924aa8313f", ""}, + TestHash{"976abff8062a2e9dcea3a1ace966ed9c19cb8555", "The quick brown fox jumps over the lazy dog"}, + TestHash{"09c11330283a27efb51930aa7dc1ec624ff738a8", "The quick brown fox jumps over the lazy cog"}, + } + for v, _ in test_vectors { + computed := tiger2.hash_160(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_tiger2_192 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"4441be75f6018773c206c22745374b924aa8313fef919f41", ""}, + TestHash{"976abff8062a2e9dcea3a1ace966ed9c19cb85558b4976d8", "The quick brown fox jumps over the lazy dog"}, + TestHash{"09c11330283a27efb51930aa7dc1ec624ff738a8d9bdd3df", "The quick brown fox jumps over the lazy cog"}, + } + for v, _ in test_vectors { + computed := tiger2.hash_192(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_sm3 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b", ""}, + TestHash{"66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0", "abc"}, + TestHash{"debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732", "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"}, + TestHash{"5fdfe814b8573ca021983970fc79b2218c9570369b4859684e2e4c3fc76cb8ea", "The quick brown fox jumps over the lazy dog"}, + TestHash{"ca27d14a42fc04c1e5ecf574a95a8c2d70ecb5805e9b429026ccac8f28b20098", "The quick brown fox jumps over the lazy cog"}, + } + for v, _ in test_vectors { + computed := sm3.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + sm3.use_botan() + for v, _ in test_vectors { + computed := sm3.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_skein512 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"bc5b4c50925519c290cc634277ae3d6257212395cba733bbad37a4af0fa06af41fca7903d06564fea7a2d3730dbdb80c1f85562dfcc070334ea4d1d9e72cba7a", ""}, + TestHash{"94c2ae036dba8783d0b3f7d6cc111ff810702f5c77707999be7e1c9486ff238a7044de734293147359b4ac7e1d09cd247c351d69826b78dcddd951f0ef912713", "The quick brown fox jumps over the lazy dog"}, + } + skein.use_botan() + for v, _ in test_vectors { + computed := skein.hash_skein512(v.str, 64) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_jh_224 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"2c99df889b019309051c60fecc2bd285a774940e43175b76b2626630", ""}, + TestHash{"e715f969fb61b203a97e494aab92d91a9cec52f0933436b0d63bf722", "a"}, + TestHash{"c2b1967e635bd55b6a4d36f863ac4a877be302251d68692873007281", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := jh.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_jh_256 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"46e64619c18bb0a92a5e87185a47eef83ca747b8fcc8e1412921357e326df434", ""}, + TestHash{"d52c0c130a1bc0ae5136375637a52773e150c71efe1c968df8956f6745b05386", "a"}, + TestHash{"fc4214867025a8af94c614353b3553b10e561ae749fc18c40e5fd44a7a4ecd1b", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := jh.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_jh_384 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"2fe5f71b1b3290d3c017fb3c1a4d02a5cbeb03a0476481e25082434a881994b0ff99e078d2c16b105ad069b569315328", ""}, + TestHash{"77de897ca4fd5dadfbcbd1d8d4ea3c3c1426855e38661325853e92b069f3fe156729f6bbb9a5892c7c18a77f1cb9d0bb", "a"}, + TestHash{"6f73d9b9b8ed362f8180fb26020725b40bd6ca75b3b947405f26c4c37a885ce028876dc42e379d2faf6146fed3ea0e42", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := jh.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_jh_512 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"90ecf2f76f9d2c8017d979ad5ab96b87d58fc8fc4b83060f3f900774faa2c8fabe69c5f4ff1ec2b61d6b316941cedee117fb04b1f4c5bc1b919ae841c50eec4f", ""}, + TestHash{"f12c87e986daff17c481c81a99a39b603ca6bafcd320c5735523b97cb9a26f7681bad62ffad9aad0e21160a05f773fb0d1434ca4cbcb0483f480a171ada1561b", "a"}, + TestHash{"bafb8e710b35eabeb1a48220c4b0987c2c985b6e73b7b31d164bfb9d67c94d99d7bc43b474a25e647cd6cc36334b6a00a5f2a85fae74907fd2885c6168132fe7", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := jh.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_groestl_224 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"f2e180fb5947be964cd584e22e496242c6a329c577fc4ce8c36d34c3", ""}, + TestHash{"2dfa5bd326c23c451b1202d99e6cee98a98c45927e1a31077f538712", "a"}, + TestHash{"c8a3e7274d599900ae673419683c3626a2e49ed57308ed2687508bef", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := groestl.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_groestl_256 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"1a52d11d550039be16107f9c58db9ebcc417f16f736adb2502567119f0083467", ""}, + TestHash{"3645c245bb31223ad93c80885b719aa40b4bed0a9d9d6e7c11fe99e59ca350b5", "a"}, + TestHash{"2679d98913bee62e57fdbdde97ddb328373548c6b24fc587cc3d08f2a02a529c", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := groestl.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_groestl_384 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"ac353c1095ace21439251007862d6c62f829ddbe6de4f78e68d310a9205a736d8b11d99bffe448f57a1cfa2934f044a5", ""}, + TestHash{"13fce7bd9fc69b67cc12c77e765a0a97794c585f89df39fbff32408e060d7d9225c7e80fd87da647686888bda896c342", "a"}, + TestHash{"1c446cd70a6de52c9db386f5305aae029fe5a4120bc6230b7cd3a5e1ef1949cc8e6d2548c24cd7347b5ba512628a62f6", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := groestl.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_groestl_512 :: proc(t: ^testing.T) { + test_vectors := [?]TestHash { + TestHash{"6d3ad29d279110eef3adbd66de2a0345a77baede1557f5d099fce0c03d6dc2ba8e6d4a6633dfbd66053c20faa87d1a11f39a7fbe4a6c2f009801370308fc4ad8", ""}, + TestHash{"9ef345a835ee35d6d0d462ce45f722d84b5ca41fde9c81a98a22cfb4f7425720511b03a258cdc055bf8e9179dc9bdb5d88bed906c71125d4cf0cd39d3d7bebc7", "a"}, + TestHash{"862849fd911852cd54beefa88759db4cead0ef8e36aaf15398303c5c4cbc016d9b4c42b32081cbdcba710d2693e7663d244fae116ec29ffb40168baf44f944e7", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := groestl.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_haval_128 :: proc(t: ^testing.T) { + test_vectors_3 := [?]TestHash { + TestHash{"c68f39913f901f3ddf44c707357a7d70", ""}, + TestHash{"0cd40739683e15f01ca5dbceef4059f1", "a"}, + TestHash{"9e40ed883fb63e985d299b40cda2b8f2", "abc"}, + TestHash{"3caf4a79e81adcd6d1716bcc1cef4573", "message digest"}, + TestHash{"dc502247fb3eb8376109eda32d361d82", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"44068770868768964d1f2c3bff4aa3d8", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"de5eb3f7d9eb08fae7a07d68e3047ec6", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + } + for v, _ in test_vectors_3 { + computed := haval.hash_128_3(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_4 := [?]TestHash { + TestHash{"ee6bbf4d6a46a679b3a856c88538bb98", ""}, + TestHash{"5cd07f03330c3b5020b29ba75911e17d", "a"}, + } + for v, _ in test_vectors_4 { + computed := haval.hash_128_4(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_5 := [?]TestHash { + TestHash{"184b8482a0c050dca54b59c7f05bf5dd", ""}, + TestHash{"f23fbe704be8494bfa7a7fb4f8ab09e5", "a"}, + } + for v, _ in test_vectors_5 { + computed := haval.hash_128_5(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_haval_160 :: proc(t: ^testing.T) { + test_vectors_3 := [?]TestHash { + TestHash{"d353c3ae22a25401d257643836d7231a9a95f953", ""}, + TestHash{"4da08f514a7275dbc4cece4a347385983983a830", "a"}, + TestHash{"b21e876c4d391e2a897661149d83576b5530a089", "abc"}, + TestHash{"43a47f6f1c016207f08be8115c0977bf155346da", "message digest"}, + TestHash{"eba9fa6050f24c07c29d1834a60900ea4e32e61b", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"c30bce448cf8cfe957c141e90c0a063497cdfeeb", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"97dc988d97caae757be7523c4e8d4ea63007a4b9", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + } + for v, _ in test_vectors_3 { + computed := haval.hash_160_3(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_4 := [?]TestHash { + TestHash{"1d33aae1be4146dbaaca0b6e70d7a11f10801525", ""}, + TestHash{"e0a5be29627332034d4dd8a910a1a0e6fe04084d", "a"}, + } + for v, _ in test_vectors_4 { + computed := haval.hash_160_4(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_5 := [?]TestHash { + TestHash{"255158cfc1eed1a7be7c55ddd64d9790415b933b", ""}, + TestHash{"f5147df7abc5e3c81b031268927c2b5761b5a2b5", "a"}, + } + for v, _ in test_vectors_5 { + computed := haval.hash_160_5(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_haval_192 :: proc(t: ^testing.T) { + test_vectors_3 := [?]TestHash { + TestHash{"e9c48d7903eaf2a91c5b350151efcb175c0fc82de2289a4e", ""}, + TestHash{"b359c8835647f5697472431c142731ff6e2cddcacc4f6e08", "a"}, + } + for v, _ in test_vectors_3 { + computed := haval.hash_192_3(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_4 := [?]TestHash { + TestHash{"4a8372945afa55c7dead800311272523ca19d42ea47b72da", ""}, + TestHash{"856c19f86214ea9a8a2f0c4b758b973cce72a2d8ff55505c", "a"}, + } + for v, _ in test_vectors_4 { + computed := haval.hash_192_4(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_5 := [?]TestHash { + TestHash{"4839d0626f95935e17ee2fc4509387bbe2cc46cb382ffe85", ""}, + TestHash{"5ffa3b3548a6e2cfc06b7908ceb5263595df67cf9c4b9341", "a"}, + } + for v, _ in test_vectors_5 { + computed := haval.hash_192_5(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_haval_224 :: proc(t: ^testing.T) { + test_vectors_3 := [?]TestHash { + TestHash{"c5aae9d47bffcaaf84a8c6e7ccacd60a0dd1932be7b1a192b9214b6d", ""}, + TestHash{"731814ba5605c59b673e4caae4ad28eeb515b3abc2b198336794e17b", "a"}, + } + for v, _ in test_vectors_3 { + computed := haval.hash_224_3(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_4 := [?]TestHash { + TestHash{"3e56243275b3b81561750550e36fcd676ad2f5dd9e15f2e89e6ed78e", ""}, + TestHash{"742f1dbeeaf17f74960558b44f08aa98bdc7d967e6c0ab8f799b3ac1", "a"}, + } + for v, _ in test_vectors_4 { + computed := haval.hash_224_4(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_5 := [?]TestHash { + TestHash{"4a0513c032754f5582a758d35917ac9adf3854219b39e3ac77d1837e", ""}, + TestHash{"67b3cb8d4068e3641fa4f156e03b52978b421947328bfb9168c7655d", "a"}, + } + for v, _ in test_vectors_5 { + computed := haval.hash_224_5(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_haval_256 :: proc(t: ^testing.T) { + test_vectors_3 := [?]TestHash { + TestHash{"4f6938531f0bc8991f62da7bbd6f7de3fad44562b8c6f4ebf146d5b4e46f7c17", ""}, + TestHash{"47c838fbb4081d9525a0ff9b1e2c05a98f625714e72db289010374e27db021d8", "a"}, + } + for v, _ in test_vectors_3 { + computed := haval.hash_256_3(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_4 := [?]TestHash { + TestHash{"c92b2e23091e80e375dadce26982482d197b1a2521be82da819f8ca2c579b99b", ""}, + TestHash{"e686d2394a49b44d306ece295cf9021553221db132b36cc0ff5b593d39295899", "a"}, + } + for v, _ in test_vectors_4 { + computed := haval.hash_256_4(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_5 := [?]TestHash { + TestHash{"be417bb4dd5cfb76c7126f4f8eeb1553a449039307b1a3cd451dbfdc0fbbe330", ""}, + TestHash{"de8fd5ee72a5e4265af0a756f4e1a1f65c9b2b2f47cf17ecf0d1b88679a3e22f", "a"}, + } + for v, _ in test_vectors_5 { + computed := haval.hash_256_5(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +}