This commit is contained in:
gingerBill
2021-11-09 16:36:01 +00:00
34 changed files with 3124 additions and 7329 deletions
+35 -43
View File
@@ -2,48 +2,43 @@
A crypto library for the Odin language A crypto library for the Odin language
## Supported ## Supported
This library offers various algorithms available in either native Odin or via bindings to the [Botan](https://botan.randombit.net/) crypto library. This library offers various algorithms implemented in Odin.
Please see the chart below for the options. 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 ## Hashing algorithms
| Algorithm | Odin | Botan | | Algorithm | |
|:-------------------------------------------------------------------------------------------------------------|:-----------------|:---------------------| |:-------------------------------------------------------------------------------------------------------------|:-----------------|
| [BLAKE](https://web.archive.org/web/20190915215948/https://131002.net/blake) | ✔️ | | | [BLAKE](https://web.archive.org/web/20190915215948/https://131002.net/blake) | ✔️ |
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ | ✔️ | | [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ |
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ | | | [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ |
| [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | ✔️ | ✔️ | | [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | ✔️ |
| [Grøstl](http://www.groestl.info/Groestl.zip) | ✔️ | | | [Grøstl](http://www.groestl.info/Groestl.zip) | ✔️ |
| [HAVAL](https://web.archive.org/web/20150111210116/http://labs.calyptix.com/haval.php) | ✔️ | | | [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) | ✔️ | | | [JH](https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html) | ✔️ |
| [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ | ✔️ | | [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
| [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | ✔️ | | | [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | ✔️ |
| [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | ✔️ | ✔️ | | [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | ✔️ |
| [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | ✔️ | ✔️ | | [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | ✔️ |
| [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | ✔️ | ✔️\* | | [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | ✔️ |
| [SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | ✔️ | ✔️ | | [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-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) | ✔️ | ✔️ | | [SHA-3](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
| [SHAKE](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) | ✔️ |
| [SM3](https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02) | ✔️ | ✔️ | | [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | ✔️ |
| [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | ✔️ | ✔️ | | [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ |
| [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ | ✔️ | | [Tiger2](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) | ✔️ |
| [Whirlpool](https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html) | ✔️ | ✔️ |
\* Only `RIPEMD-160`
\*\* Only `SKEIN-512`
#### High level API #### High level API
Each hash algorithm contains a procedure group named `hash`, or if the algorithm provides more than one digest size `hash_<size>`\*\*\*. Each hash algorithm contains a procedure group named `hash`, or if the algorithm provides more than one digest size `hash_<size>`\*.
Included in these groups are four procedures. Included in these groups are four procedures.
* `hash_string` - Hash a given string and return the computed hash. Just calls `hash_bytes` internally * `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_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_stream` - Takes a stream from io.Stream and returns the computed hash from it
* `hash_file` - Takes a file handle and returns the computed hash from it. A second optional boolean parameter controls if the file is streamed (this is the default) or read at once (set to true) * `hash_file` - Takes a file handle and returns the computed hash from it. A second optional boolean parameter controls if the file is streamed (this is the default) 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. \* 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. 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(...)`. Computing a 256-bit hash with 3 rounds is therefore achieved by calling `haval.hash_256_3(...)`.
@@ -51,13 +46,6 @@ Computing a 256-bit hash with 3 rounds is therefore achieved by calling `haval.h
The above mentioned procedures internally call three procedures: `init`, `update` and `final`. The above mentioned procedures internally call three procedures: `init`, `update` and `final`.
You may also directly call them, if you wish. 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 #### Example
```odin ```odin
package crypto_example package crypto_example
@@ -67,12 +55,16 @@ import "core:crypto/md4"
main :: proc() { main :: proc() {
input := "foo" input := "foo"
// Compute the hash via Odin implementation
// Compute the hash, using the high level API
computed_hash := md4.hash(input) computed_hash := md4.hash(input)
// Switch to Botan
md4.use_botan() // Compute the hash, using the low level API
// Compute the hash via Botan bindings ctx: md4.Md4_Context
computed_hash_botan := md4.hash(input) computed_hash_low: [16]byte
md4.init(&ctx)
md4.update(&ctx, transmute([]byte)input)
md4.final(&ctx, computed_hash_low[:])
} }
``` ```
For example uses of all available algorithms, please see the tests within `tests/core/crypto`. For example uses of all available algorithms, please see the tests within `tests/core/crypto`.
+22 -14
View File
@@ -6,7 +6,6 @@ package _blake2
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://datatracker.ietf.org/doc/html/rfc7693> and <https://www.blake2.net/> Implementation of the BLAKE2 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/rfc7693> and <https://www.blake2.net/>
*/ */
@@ -76,7 +75,7 @@ BLAKE2B_IV := [8]u64 {
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
} }
init_odin :: proc(ctx: ^$T) { init :: proc(ctx: ^$T) {
when T == Blake2s_Context { when T == Blake2s_Context {
block_size :: BLAKE2S_BLOCK_SIZE block_size :: BLAKE2S_BLOCK_SIZE
} else when T == Blake2b_Context { } else when T == Blake2b_Context {
@@ -139,17 +138,17 @@ init_odin :: proc(ctx: ^$T) {
} }
if len(ctx.cfg.key) > 0 { if len(ctx.cfg.key) > 0 {
copy(ctx.padded_key[:], ctx.cfg.key) copy(ctx.padded_key[:], ctx.cfg.key)
update_odin(ctx, ctx.padded_key[:]) update(ctx, ctx.padded_key[:])
ctx.is_keyed = true ctx.is_keyed = true
} }
copy(ctx.ih[:], ctx.h[:]) copy(ctx.ih[:], ctx.h[:])
copy(ctx.h[:], ctx.ih[:]) copy(ctx.h[:], ctx.ih[:])
if ctx.is_keyed { if ctx.is_keyed {
update_odin(ctx, ctx.padded_key[:]) update(ctx, ctx.padded_key[:])
} }
} }
update_odin :: proc(ctx: ^$T, p: []byte) { update :: proc "contextless" (ctx: ^$T, p: []byte) {
p := p p := p
when T == Blake2s_Context { when T == Blake2s_Context {
block_size :: BLAKE2S_BLOCK_SIZE block_size :: BLAKE2S_BLOCK_SIZE
@@ -161,7 +160,7 @@ update_odin :: proc(ctx: ^$T, p: []byte) {
if len(p) > left { if len(p) > left {
copy(ctx.x[ctx.nx:], p[:left]) copy(ctx.x[ctx.nx:], p[:left])
p = p[left:] p = p[left:]
blake2_blocks(ctx, ctx.x[:]) blocks(ctx, ctx.x[:])
ctx.nx = 0 ctx.nx = 0
} }
if len(p) > block_size { if len(p) > block_size {
@@ -169,13 +168,22 @@ update_odin :: proc(ctx: ^$T, p: []byte) {
if n == len(p) { if n == len(p) {
n -= block_size n -= block_size
} }
blake2_blocks(ctx, p[:n]) blocks(ctx, p[:n])
p = p[n:] p = p[n:]
} }
ctx.nx += copy(ctx.x[ctx.nx:], p) ctx.nx += copy(ctx.x[ctx.nx:], p)
} }
blake2s_final_odin :: proc(ctx: $T, hash: []byte) { final :: proc "contextless" (ctx: ^$T, hash: []byte) {
when T == Blake2s_Context {
blake2s_final(ctx, hash)
}
when T == Blake2b_Context {
blake2b_final(ctx, hash)
}
}
blake2s_final :: proc "contextless" (ctx: ^Blake2s_Context, hash: []byte) {
if ctx.is_keyed { if ctx.is_keyed {
for i := 0; i < len(ctx.padded_key); i += 1 { for i := 0; i < len(ctx.padded_key); i += 1 {
ctx.padded_key[i] = 0 ctx.padded_key[i] = 0
@@ -193,7 +201,7 @@ blake2s_final_odin :: proc(ctx: $T, hash: []byte) {
ctx.f[1] = 0xffffffff ctx.f[1] = 0xffffffff
} }
blake2_blocks(ctx, ctx.x[:]) blocks(ctx, ctx.x[:])
j := 0 j := 0
for s, _ in ctx.h[:(ctx.size - 1) / 4 + 1] { for s, _ in ctx.h[:(ctx.size - 1) / 4 + 1] {
@@ -205,7 +213,7 @@ blake2s_final_odin :: proc(ctx: $T, hash: []byte) {
} }
} }
blake2b_final_odin :: proc(ctx: $T, hash: []byte) { blake2b_final :: proc "contextless" (ctx: ^Blake2b_Context, hash: []byte) {
if ctx.is_keyed { if ctx.is_keyed {
for i := 0; i < len(ctx.padded_key); i += 1 { for i := 0; i < len(ctx.padded_key); i += 1 {
ctx.padded_key[i] = 0 ctx.padded_key[i] = 0
@@ -223,7 +231,7 @@ blake2b_final_odin :: proc(ctx: $T, hash: []byte) {
ctx.f[1] = 0xffffffffffffffff ctx.f[1] = 0xffffffffffffffff
} }
blake2_blocks(ctx, ctx.x[:]) blocks(ctx, ctx.x[:])
j := 0 j := 0
for s, _ in ctx.h[:(ctx.size - 1) / 8 + 1] { for s, _ in ctx.h[:(ctx.size - 1) / 8 + 1] {
@@ -239,7 +247,7 @@ blake2b_final_odin :: proc(ctx: $T, hash: []byte) {
} }
} }
blake2_blocks :: proc(ctx: ^$T, p: []byte) { blocks :: proc "contextless" (ctx: ^$T, p: []byte) {
when T == Blake2s_Context { when T == Blake2s_Context {
blake2s_blocks(ctx, p) blake2s_blocks(ctx, p)
} }
@@ -248,7 +256,7 @@ blake2_blocks :: proc(ctx: ^$T, p: []byte) {
} }
} }
blake2s_blocks :: #force_inline proc "contextless"(ctx: ^Blake2s_Context, p: []byte) { 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] 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 p := p
for len(p) >= BLAKE2S_BLOCK_SIZE { for len(p) >= BLAKE2S_BLOCK_SIZE {
@@ -1404,7 +1412,7 @@ blake2s_blocks :: #force_inline proc "contextless"(ctx: ^Blake2s_Context, p: []b
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 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) { 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] 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 p := p
for len(p) >= BLAKE2B_BLOCK_SIZE { for len(p) >= BLAKE2B_BLOCK_SIZE {
-79
View File
@@ -1,79 +0,0 @@
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"
import "core:os"
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, hd: os.Handle, load_at_once := false) -> ([16]byte, bool),
hash_file_20 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool),
hash_file_24 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool),
hash_file_28 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool),
hash_file_32 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool),
hash_file_40 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([40]byte, bool),
hash_file_48 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool),
hash_file_64 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool),
hash_file_128 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([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, hd: os.Handle, out_size: int, load_at_once := false, 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
}
+5 -6
View File
@@ -6,7 +6,6 @@ package _sha3
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf> Implementation of the Keccak hashing algorithm, standardized as SHA3 in <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf>
To use the original Keccak padding, set the is_keccak bool to true, otherwise it will use SHA3 padding. To use the original Keccak padding, set the is_keccak bool to true, otherwise it will use SHA3 padding.
@@ -115,14 +114,14 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
} }
} }
init_odin :: proc "contextless" (c: ^Sha3_Context) { init :: proc "contextless" (c: ^Sha3_Context) {
for i := 0; i < 25; i += 1 { for i := 0; i < 25; i += 1 {
c.st.q[i] = 0 c.st.q[i] = 0
} }
c.rsiz = 200 - 2 * c.mdlen c.rsiz = 200 - 2 * c.mdlen
} }
update_odin :: proc "contextless" (c: ^Sha3_Context, data: []byte) { update :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
j := c.pt j := c.pt
for i := 0; i < len(data); i += 1 { for i := 0; i < len(data); i += 1 {
c.st.b[j] ~= data[i] c.st.b[j] ~= data[i]
@@ -135,7 +134,7 @@ update_odin :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
c.pt = j c.pt = j
} }
final_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) { final :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
if c.is_keccak { if c.is_keccak {
c.st.b[c.pt] ~= 0x01 c.st.b[c.pt] ~= 0x01
} else { } else {
@@ -149,14 +148,14 @@ final_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
} }
} }
shake_xof_odin :: proc "contextless" (c: ^Sha3_Context) { shake_xof :: proc "contextless" (c: ^Sha3_Context) {
c.st.b[c.pt] ~= 0x1F c.st.b[c.pt] ~= 0x1F
c.st.b[c.rsiz - 1] ~= 0x80 c.st.b[c.rsiz - 1] ~= 0x80
keccakf(&c.st.q) keccakf(&c.st.q)
c.pt = 0 c.pt = 0
} }
shake_out_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) { shake_out :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
j := c.pt j := c.pt
for i := 0; i < len(hash); i += 1 { for i := 0; i < len(hash); i += 1 {
if j >= c.rsiz { if j >= c.rsiz {
+10 -11
View File
@@ -6,7 +6,6 @@ package _tiger
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://www.cs.technion.ac.il/~biham/Reports/Tiger/> Implementation of the Tiger hashing algorithm, as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
*/ */
@@ -291,7 +290,7 @@ Tiger_Context :: struct {
ver: int, ver: int,
} }
round :: #force_inline proc "contextless"(a, b, c, x, mul: u64) -> (u64, u64, u64) { round :: #force_inline proc "contextless" (a, b, c, x, mul: u64) -> (u64, u64, u64) {
a, b, c := a, b, c a, b, c := a, b, c
c ~= x c ~= x
a -= T1[c & 0xff] ~ T2[(c >> 16) & 0xff] ~ T3[(c >> 32) & 0xff] ~ T4[(c >> 48) & 0xff] a -= T1[c & 0xff] ~ T2[(c >> 16) & 0xff] ~ T3[(c >> 32) & 0xff] ~ T4[(c >> 48) & 0xff]
@@ -300,7 +299,7 @@ round :: #force_inline proc "contextless"(a, b, c, x, mul: u64) -> (u64, u64, u6
return a, b, c return a, b, c
} }
pass :: #force_inline proc "contextless"(a, b, c: u64, d: []u64, mul: u64) -> (x, y, z: u64) { 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) x, y, z = round(a, b, c, d[0], mul)
y, z, x = round(y, z, x, d[1], mul) y, z, x = round(y, z, x, d[1], mul)
z, x, y = round(z, x, y, d[2], mul) z, x, y = round(z, x, y, d[2], mul)
@@ -312,7 +311,7 @@ pass :: #force_inline proc "contextless"(a, b, c: u64, d: []u64, mul: u64) -> (x
return return
} }
key_schedule :: #force_inline proc "contextless"(x: []u64) { key_schedule :: #force_inline proc "contextless" (x: []u64) {
x[0] -= x[7] ~ 0xa5a5a5a5a5a5a5a5 x[0] -= x[7] ~ 0xa5a5a5a5a5a5a5a5
x[1] ~= x[0] x[1] ~= x[0]
x[2] += x[1] x[2] += x[1]
@@ -331,7 +330,7 @@ key_schedule :: #force_inline proc "contextless"(x: []u64) {
x[7] -= x[6] ~ 0x0123456789abcdef x[7] -= x[6] ~ 0x0123456789abcdef
} }
compress :: #force_inline proc "contextless"(ctx: ^Tiger_Context, data: []byte) { compress :: #force_inline proc "contextless" (ctx: ^Tiger_Context, data: []byte) {
a := ctx.a a := ctx.a
b := ctx.b b := ctx.b
c := ctx.c c := ctx.c
@@ -346,13 +345,13 @@ compress :: #force_inline proc "contextless"(ctx: ^Tiger_Context, data: []byte)
ctx.c += c ctx.c += c
} }
init_odin :: proc(ctx: ^Tiger_Context) { init :: proc "contextless" (ctx: ^Tiger_Context) {
ctx.a = 0x0123456789abcdef ctx.a = 0x0123456789abcdef
ctx.b = 0xfedcba9876543210 ctx.b = 0xfedcba9876543210
ctx.c = 0xf096a5b4c3b2e187 ctx.c = 0xf096a5b4c3b2e187
} }
update_odin :: proc(ctx: ^Tiger_Context, input: []byte) { update :: proc(ctx: ^Tiger_Context, input: []byte) {
p := make([]byte, len(input)) p := make([]byte, len(input))
copy(p, input) copy(p, input)
@@ -380,7 +379,7 @@ update_odin :: proc(ctx: ^Tiger_Context, input: []byte) {
} }
} }
final_odin :: proc(ctx: ^Tiger_Context, hash: []byte) { final :: proc(ctx: ^Tiger_Context, hash: []byte) {
length := ctx.length length := ctx.length
tmp: [64]byte tmp: [64]byte
if ctx.ver == 1 { if ctx.ver == 1 {
@@ -391,16 +390,16 @@ final_odin :: proc(ctx: ^Tiger_Context, hash: []byte) {
size := length & 0x3f size := length & 0x3f
if size < 56 { if size < 56 {
update_odin(ctx, tmp[:56 - size]) update(ctx, tmp[:56 - size])
} else { } else {
update_odin(ctx, tmp[:64 + 56 - size]) update(ctx, tmp[:64 + 56 - size])
} }
length <<= 3 length <<= 3
for i := uint(0); i < 8; i += 1 { for i := uint(0); i < 8; i += 1 {
tmp[i] = byte(length >> (8 * i)) tmp[i] = byte(length >> (8 * i))
} }
update_odin(ctx, tmp[:8]) update(ctx, tmp[:8])
for i := uint(0); i < 8; i += 1 { for i := uint(0); i < 8; i += 1 {
tmp[i] = byte(ctx.a >> (8 * i)) tmp[i] = byte(ctx.a >> (8 * i))
+297 -494
View File
@@ -6,7 +6,6 @@ package blake
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://web.archive.org/web/20190915215948/https://131002.net/blake> Implementation of the BLAKE hashing algorithm, as defined in <https://web.archive.org/web/20190915215948/https://131002.net/blake>
*/ */
@@ -14,102 +13,59 @@ package blake
import "core:os" import "core:os"
import "core:io" 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 High level API
*/ */
// hash_string_224 will hash the given input and return the // hash_string_224 will hash the given input and return the
// computed hash // computed hash
hash_string_224 :: proc(data: string) -> [28]byte { hash_string_224 :: proc "contextless" (data: string) -> [28]byte {
return hash_bytes_224(transmute([]byte)(data)) return hash_bytes_224(transmute([]byte)(data))
} }
// hash_bytes_224 will hash the given input and return the // hash_bytes_224 will hash the given input and return the
// computed hash // computed hash
hash_bytes_224 :: proc(data: []byte) -> [28]byte { hash_bytes_224 :: proc "contextless" (data: []byte) -> [28]byte {
_create_blake256_ctx(true, ._28) hash: [28]byte
return _hash_impl->hash_bytes_28(data) ctx: Blake256_Context
ctx.is224 = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_224 will read the stream in chunks and compute a // hash_stream_224 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) { hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
_create_blake256_ctx(true, ._28) hash: [28]byte
return _hash_impl->hash_stream_28(s) ctx: Blake256_Context
ctx.is224 = true
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_224 will read the file provided by the given handle // hash_file_224 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) { hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
_create_blake256_ctx(true, ._28) if !load_at_once {
return _hash_impl->hash_file_28(hd, load_at_once) return hash_stream_224(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_224(buf[:]), ok
}
}
return [28]byte{}, false
} }
hash_224 :: proc { hash_224 :: proc {
@@ -121,29 +77,53 @@ hash_224 :: proc {
// hash_string_256 will hash the given input and return the // hash_string_256 will hash the given input and return the
// computed hash // computed hash
hash_string_256 :: proc(data: string) -> [32]byte { hash_string_256 :: proc "contextless" (data: string) -> [32]byte {
return hash_bytes_256(transmute([]byte)(data)) return hash_bytes_256(transmute([]byte)(data))
} }
// hash_bytes_256 will hash the given input and return the // hash_bytes_256 will hash the given input and return the
// computed hash // computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte { hash_bytes_256 :: proc "contextless" (data: []byte) -> [32]byte {
_create_blake256_ctx(false, ._32) hash: [32]byte
return _hash_impl->hash_bytes_32(data) ctx: Blake256_Context
ctx.is224 = false
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_256 will read the stream in chunks and compute a // hash_stream_256 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_blake256_ctx(false, ._32) hash: [32]byte
return _hash_impl->hash_stream_32(s) ctx: Blake256_Context
ctx.is224 = false
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_256 will read the file provided by the given handle // hash_file_256 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_blake256_ctx(false, ._32) if !load_at_once {
return _hash_impl->hash_file_32(hd, load_at_once) return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [32]byte{}, false
} }
hash_256 :: proc { hash_256 :: proc {
@@ -155,29 +135,53 @@ hash_256 :: proc {
// hash_string_384 will hash the given input and return the // hash_string_384 will hash the given input and return the
// computed hash // computed hash
hash_string_384 :: proc(data: string) -> [48]byte { hash_string_384 :: proc "contextless" (data: string) -> [48]byte {
return hash_bytes_384(transmute([]byte)(data)) return hash_bytes_384(transmute([]byte)(data))
} }
// hash_bytes_384 will hash the given input and return the // hash_bytes_384 will hash the given input and return the
// computed hash // computed hash
hash_bytes_384 :: proc(data: []byte) -> [48]byte { hash_bytes_384 :: proc "contextless" (data: []byte) -> [48]byte {
_create_blake512_ctx(true, ._48) hash: [48]byte
return _hash_impl->hash_bytes_48(data) ctx: Blake512_Context
ctx.is384 = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_384 will read the stream in chunks and compute a // hash_stream_384 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) { hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
_create_blake512_ctx(true, ._48) hash: [48]byte
return _hash_impl->hash_stream_48(s) ctx: Blake512_Context
ctx.is384 = true
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_384 will read the file provided by the given handle // hash_file_384 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) { hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
_create_blake512_ctx(true, ._48) if !load_at_once {
return _hash_impl->hash_file_48(hd, load_at_once) return hash_stream_384(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_384(buf[:]), ok
}
}
return [48]byte{}, false
} }
hash_384 :: proc { hash_384 :: proc {
@@ -189,29 +193,53 @@ hash_384 :: proc {
// hash_string_512 will hash the given input and return the // hash_string_512 will hash the given input and return the
// computed hash // computed hash
hash_string_512 :: proc(data: string) -> [64]byte { hash_string_512 :: proc "contextless" (data: string) -> [64]byte {
return hash_bytes_512(transmute([]byte)(data)) return hash_bytes_512(transmute([]byte)(data))
} }
// hash_bytes_512 will hash the given input and return the // hash_bytes_512 will hash the given input and return the
// computed hash // computed hash
hash_bytes_512 :: proc(data: []byte) -> [64]byte { hash_bytes_512 :: proc "contextless" (data: []byte) -> [64]byte {
_create_blake512_ctx(false, ._64) hash: [64]byte
return _hash_impl->hash_bytes_64(data) ctx: Blake512_Context
ctx.is384 = false
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_512 will read the stream in chunks and compute a // hash_stream_512 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) { hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_blake512_ctx(false, ._64) hash: [64]byte
return _hash_impl->hash_stream_64(s) ctx: Blake512_Context
ctx.is384 = false
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_512 will read the file provided by the given handle // hash_file_512 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) { hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_blake512_ctx(false, ._64) if !load_at_once {
return _hash_impl->hash_file_64(hd, load_at_once) return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [64]byte{}, false
} }
hash_512 :: proc { hash_512 :: proc {
@@ -225,231 +253,188 @@ hash_512 :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { init :: proc "contextless" (ctx: ^$T) {
_hash_impl->init() when T == Blake256_Context {
} if ctx.is224 {
ctx.h[0] = 0xc1059ed8
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { ctx.h[1] = 0x367cd507
_hash_impl->update(data) ctx.h[2] = 0x3070dd17
} ctx.h[3] = 0xf70e5939
ctx.h[4] = 0xffc00b31
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { ctx.h[5] = 0x68581511
_hash_impl->final(hash) ctx.h[6] = 0x64f98fa7
} ctx.h[7] = 0xbefa4fa4
} else {
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte { ctx.h[0] = 0x6a09e667
hash: [28]byte ctx.h[1] = 0xbb67ae85
if c, ok := ctx.internal_ctx.(Blake256_Context); ok { ctx.h[2] = 0x3c6ef372
init_odin(&c) ctx.h[3] = 0xa54ff53a
update_odin(&c, data) ctx.h[4] = 0x510e527f
final_odin(&c, hash[:]) 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
}
} }
return hash
} }
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) { update :: proc "contextless" (ctx: ^$T, data: []byte) {
hash: [28]byte data := data
if c, ok := ctx.internal_ctx.(Blake256_Context); ok { when T == Blake256_Context {
init_odin(&c) if ctx.nx > 0 {
buf := make([]byte, 512) n := copy(ctx.x[ctx.nx:], data)
defer delete(buf) ctx.nx += n
read := 1 if ctx.nx == BLOCKSIZE_256 {
for read > 0 { block256(ctx, ctx.x[:])
read, _ = fs->impl_read(buf) ctx.nx = 0
if read > 0 { }
update_odin(&c, buf[:read]) 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 :: proc "contextless" (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})
} }
} }
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) { for i : uint = 0; i < 8; i += 1 {
if !load_at_once { tmp[i] = byte(length >> (56 - 8 * i))
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_28(ctx, buf[:]), ok
} }
} write_additional(ctx, tmp[0:8])
return [28]byte{}, false
}
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { h := ctx.h[:]
hash: [32]byte if ctx.is224 {
if c, ok := ctx.internal_ctx.(Blake256_Context); ok { h = h[0:7]
init_odin(&c) }
update_odin(&c, data) for s, i in h {
final_odin(&c, hash[:]) hash[i * 4] = byte(s >> 24)
} hash[i * 4 + 1] = byte(s >> 16)
return hash hash[i * 4 + 2] = byte(s >> 8)
} hash[i * 4 + 3] = byte(s)
}
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { } else when T == Blake512_Context {
hash: [32]byte if nx == 111 {
if c, ok := ctx.internal_ctx.(Blake256_Context); ok { if ctx.is384 {
init_odin(&c) write_additional(ctx, {0x80})
buf := make([]byte, 512) } else {
defer delete(buf) write_additional(ctx, {0x81})
read := 1 }
for read > 0 { } else {
read, _ = fs->impl_read(buf) if nx < 111 {
if read > 0 { if nx == 0 {
update_odin(&c, buf[:read]) 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})
} }
} }
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { for i : uint = 0; i < 16; i += 1 {
if !load_at_once { tmp[i] = byte(length >> (120 - 8 * i))
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), ok
} }
} write_additional(ctx, tmp[0:16])
return [32]byte{}, false
}
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte { h := ctx.h[:]
hash: [48]byte if ctx.is384 {
if c, ok := ctx.internal_ctx.(Blake512_Context); ok { h = h[0:6]
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[:]) for s, i in h {
return hash, true hash[i * 8] = byte(s >> 56)
} else { hash[i * 8 + 1] = byte(s >> 48)
return hash, false 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_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) { hash[i * 8 + 6] = byte(s >> 8)
if !load_at_once { hash[i * 8 + 7] = byte(s)
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_48(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_64(ctx, buf[:]), 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_224 :: 28
SIZE_256 :: 32 SIZE_256 :: 32
SIZE_384 :: 48 SIZE_384 :: 48
@@ -542,8 +527,8 @@ G512 :: #force_inline proc "contextless" (a, b, c, d: u64, m: [16]u64, i, j: int
return a, b, c, d return a, b, c, d
} }
block256 :: proc "contextless" (ctx: ^Blake256_Context, p: []byte) { block256 :: proc "contextless" (ctx: ^Blake256_Context, p: []byte) #no_bounds_check {
i, j: int = ---, --- i, j: int = ---, ---
v, m: [16]u32 = ---, --- v, m: [16]u32 = ---, ---
p := p p := p
for len(p) >= BLOCKSIZE_256 { for len(p) >= BLOCKSIZE_256 {
@@ -595,7 +580,7 @@ block256 :: proc "contextless" (ctx: ^Blake256_Context, p: []byte) {
} }
block512 :: proc "contextless" (ctx: ^Blake512_Context, p: []byte) #no_bounds_check { block512 :: proc "contextless" (ctx: ^Blake512_Context, p: []byte) #no_bounds_check {
i, j: int = ---, --- i, j: int = ---, ---
v, m: [16]u64 = ---, --- v, m: [16]u64 = ---, ---
p := p p := p
for len(p) >= BLOCKSIZE_512 { for len(p) >= BLOCKSIZE_512 {
@@ -646,189 +631,7 @@ block512 :: proc "contextless" (ctx: ^Blake512_Context, p: []byte) #no_bounds_ch
} }
} }
init_odin :: proc(ctx: ^$T) { write_additional :: proc "contextless" (ctx: ^$T, data: []byte) {
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 ctx.t -= u64(len(data)) << 3
update_odin(ctx, data) update(ctx, data)
} }
+42 -127
View File
@@ -6,7 +6,6 @@ package blake2b
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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. Interface for the BLAKE2B hashing algorithm.
BLAKE2B and BLAKE2B share the implementation in the _blake2 package. BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
@@ -15,49 +14,8 @@ package blake2b
import "core:os" import "core:os"
import "core:io" import "core:io"
import "../botan"
import "../_ctx"
import "../_blake2" 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 High level API
*/ */
@@ -71,22 +29,50 @@ hash_string :: proc(data: string) -> [64]byte {
// hash_bytes will hash the given input and return the // hash_bytes will hash the given input and return the
// computed hash // computed hash
hash_bytes :: proc(data: []byte) -> [64]byte { hash_bytes :: proc(data: []byte) -> [64]byte {
_create_blake2b_ctx() hash: [64]byte
return _hash_impl->hash_bytes_64(data) ctx: _blake2.Blake2b_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2B_SIZE
ctx.cfg = cfg
_blake2.init(&ctx)
_blake2.update(&ctx, data)
_blake2.final(&ctx, hash[:])
return hash
} }
// hash_stream will read the stream in chunks and compute a // hash_stream will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) { hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_blake2b_ctx() hash: [64]byte
return _hash_impl->hash_stream_64(s) ctx: _blake2.Blake2b_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2B_SIZE
ctx.cfg = cfg
_blake2.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_blake2.update(&ctx, buf[:read])
}
}
_blake2.final(&ctx, hash[:])
return hash, true
} }
// hash_file will read the file provided by the given handle // hash_file will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) { hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_blake2b_ctx() if !load_at_once {
return _hash_impl->hash_file_64(hd, load_at_once) return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [64]byte{}, false
} }
hash :: proc { hash :: proc {
@@ -100,87 +86,16 @@ hash :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { Blake2b_Context :: _blake2.Blake2b_Context
_hash_impl->init()
init :: proc(ctx: ^_blake2.Blake2b_Context) {
_blake2.init(ctx)
} }
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { update :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, data: []byte) {
_hash_impl->update(data) _blake2.update(ctx, data)
} }
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { final :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, hash: []byte) {
_hash_impl->final(hash) _blake2.final(ctx, 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, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
}
}
return [64]byte{}, false
}
@(private)
_create_blake2b_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_blake2b_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)
}
} }
+42 -127
View File
@@ -6,7 +6,6 @@ package blake2s
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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. Interface for the BLAKE2S hashing algorithm.
BLAKE2B and BLAKE2B share the implementation in the _blake2 package. BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
@@ -15,49 +14,8 @@ package blake2s
import "core:os" import "core:os"
import "core:io" import "core:io"
import "../_ctx"
import "../_blake2" 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 High level API
*/ */
@@ -71,22 +29,50 @@ hash_string :: proc(data: string) -> [32]byte {
// hash_bytes will hash the given input and return the // hash_bytes will hash the given input and return the
// computed hash // computed hash
hash_bytes :: proc(data: []byte) -> [32]byte { hash_bytes :: proc(data: []byte) -> [32]byte {
_create_blake2s_ctx() hash: [32]byte
return _hash_impl->hash_bytes_32(data) ctx: _blake2.Blake2s_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2S_SIZE
ctx.cfg = cfg
_blake2.init(&ctx)
_blake2.update(&ctx, data)
_blake2.final(&ctx, hash[:])
return hash
} }
// hash_stream will read the stream in chunks and compute a // hash_stream will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) { hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_blake2s_ctx() hash: [32]byte
return _hash_impl->hash_stream_32(s) ctx: _blake2.Blake2s_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2S_SIZE
ctx.cfg = cfg
_blake2.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_blake2.update(&ctx, buf[:read])
}
}
_blake2.final(&ctx, hash[:])
return hash, true
} }
// hash_file will read the file provided by the given handle // hash_file will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_blake2s_ctx() if !load_at_once {
return _hash_impl->hash_file_32(hd, load_at_once) return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [32]byte{}, false
} }
hash :: proc { hash :: proc {
@@ -100,87 +86,16 @@ hash :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { Blake2s_Context :: _blake2.Blake2b_Context
_hash_impl->init()
init :: proc(ctx: ^_blake2.Blake2s_Context) {
_blake2.init(ctx)
} }
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { update :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, data: []byte) {
_hash_impl->update(data) _blake2.update(ctx, data)
} }
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { final :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, hash: []byte) {
_hash_impl->final(hash) _blake2.final(ctx, 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, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
@(private)
_create_blake2s_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_blake2s_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)
}
} }
Binary file not shown.
-480
View File
@@ -1,480 +0,0 @@
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 ---
}
-498
View File
@@ -1,498 +0,0 @@
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, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
if !load_at_once {
return hash_stream_16(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_16(ctx, buf[:]), ok
}
}
return [16]byte{}, false
}
hash_file_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
if !load_at_once {
return hash_stream_20(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_20(ctx, buf[:]), ok
}
}
return [20]byte{}, false
}
hash_file_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
if !load_at_once {
return hash_stream_24(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_24(ctx, buf[:]), ok
}
}
return [24]byte{}, false
}
hash_file_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
if !load_at_once {
return hash_stream_28(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_28(ctx, buf[:]), ok
}
}
return [28]byte{}, false
}
hash_file_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_32(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
hash_file_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
if !load_at_once {
return hash_stream_48(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_48(ctx, buf[:]), ok
}
}
return [48]byte{}, false
}
hash_file_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_64(ctx, buf[:]), ok
}
}
return [64]byte{}, false
}
hash_file_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([128]byte, bool) {
if !load_at_once {
return hash_stream_128(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_128(ctx, buf[:]), ok
}
}
return [128]byte{}, false
}
hash_file_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
if !load_at_once {
return hash_stream_slice(ctx, os.stream_from_handle(hd), bit_size, allocator)
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_slice(ctx, buf[:], bit_size, allocator), ok
}
}
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
}
+113 -210
View File
@@ -6,7 +6,6 @@ package gost
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://datatracker.ietf.org/doc/html/rfc5831> Implementation of the GOST hashing algorithm, as defined in RFC 5831 <https://datatracker.ietf.org/doc/html/rfc5831>
*/ */
@@ -15,42 +14,6 @@ import "core:mem"
import "core:os" import "core:os"
import "core:io" 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 High level API
*/ */
@@ -64,22 +27,44 @@ hash_string :: proc(data: string) -> [32]byte {
// hash_bytes will hash the given input and return the // hash_bytes will hash the given input and return the
// computed hash // computed hash
hash_bytes :: proc(data: []byte) -> [32]byte { hash_bytes :: proc(data: []byte) -> [32]byte {
_create_gost_ctx() hash: [32]byte
return _hash_impl->hash_bytes_32(data) ctx: Gost_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream will read the stream in chunks and compute a // hash_stream will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) { hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_gost_ctx() hash: [32]byte
return _hash_impl->hash_stream_32(s) ctx: Gost_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file will read the file provided by the given handle // hash_file will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_gost_ctx() if !load_at_once {
return _hash_impl->hash_file_32(hd, load_at_once) return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [32]byte{}, false
} }
hash :: proc { hash :: proc {
@@ -93,85 +78,77 @@ hash :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { init :: proc "contextless" (ctx: ^Gost_Context) {
_hash_impl->init() 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 },
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { { 7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13 },
_hash_impl->update(data) { 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 },
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { { 13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11 },
_hash_impl->final(hash) { 1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12 },
}
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) { i := 0
hash: [32]byte for a := 0; a < 16; a += 1 {
if c, ok := ctx.internal_ctx.(Gost_Context); ok { ax := sbox[1][a] << 15
init_odin(&c) bx := sbox[3][a] << 23
buf := make([]byte, 512) cx := sbox[5][a]
defer delete(buf) cx = (cx >> 1) | (cx << 31)
read := 1 dx := sbox[7][a] << 7
for read > 0 { for b := 0; b < 16; b, i = b + 1, i + 1 {
read, _ = fs->impl_read(buf) SBOX_1[i] = ax | (sbox[0][b] << 11)
if read > 0 { SBOX_2[i] = bx | (sbox[2][b] << 19)
update_odin(&c, buf[:read]) SBOX_3[i] = cx | (sbox[4][b] << 27)
} SBOX_4[i] = dx | (sbox[6][b] << 3)
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
} }
} }
return [32]byte{}, false
} }
@(private) update :: proc(ctx: ^Gost_Context, data: []byte) {
_create_gost_ctx :: #force_inline proc() { length := byte(len(data))
ctx: Gost_Context j: byte
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._32
}
@(private) i := ctx.partial_bytes
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { for i < 32 && j < length {
_create_gost_ctx() ctx.partial[i] = data[j]
if c, ok := ctx.internal_ctx.(Gost_Context); ok { i, j = i + 1, j + 1
init_odin(&c)
} }
}
@(private) if i < 32 {
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { ctx.partial_bytes = i
if c, ok := ctx.internal_ctx.(Gost_Context); ok { return
update_odin(&c, data)
} }
bytes(ctx, ctx.partial[:], 256)
for (j + 32) < length {
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
} }
@(private) final :: proc(ctx: ^Gost_Context, hash: []byte) {
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { if ctx.partial_bytes > 0 {
if c, ok := ctx.internal_ctx.(Gost_Context); ok { mem.set(&ctx.partial[ctx.partial_bytes], 0, 32 - int(ctx.partial_bytes))
final_odin(&c, hash) bytes(ctx, ctx.partial[:], u32(ctx.partial_bytes) << 3)
}
compress(ctx.hash[:], ctx.len[:])
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)
} }
} }
@@ -187,12 +164,12 @@ Gost_Context :: struct {
partial_bytes: byte, partial_bytes: byte,
} }
SBOX_1 : [256]u32 SBOX_1: [256]u32
SBOX_2 : [256]u32 SBOX_2: [256]u32
SBOX_3 : [256]u32 SBOX_3: [256]u32
SBOX_4 : [256]u32 SBOX_4: [256]u32
GOST_ENCRYPT_ROUND :: #force_inline proc "contextless"(l, r, t, k1, k2: u32) -> (u32, u32, u32) { ENCRYPT_ROUND :: #force_inline proc "contextless" (l, r, t, k1, k2: u32) -> (u32, u32, u32) {
l, r, t := l, r, t l, r, t := l, r, t
t = (k1) + r t = (k1) + r
l ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24] l ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24]
@@ -201,30 +178,30 @@ GOST_ENCRYPT_ROUND :: #force_inline proc "contextless"(l, r, t, k1, k2: u32) ->
return l, r, t return l, r, t
} }
GOST_ENCRYPT :: #force_inline proc "contextless"(a, b, c: u32, key: []u32) -> (l, r, t: u32) { 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = ENCRYPT_ROUND(l, r, t, key[3], key[2])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[1], key[0]) l, r, t = ENCRYPT_ROUND(l, r, t, key[1], key[0])
t = r t = r
r = l r = l
l = t l = t
return return
} }
gost_bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) { bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
a, c: u32 a, c: u32
m: [8]u32 m: [8]u32
@@ -237,14 +214,14 @@ gost_bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
c = c < a ? 1 : 0 c = c < a ? 1 : 0
} }
gost_compress(ctx.hash[:], m[:]) compress(ctx.hash[:], m[:])
ctx.len[0] += bits ctx.len[0] += bits
if ctx.len[0] < bits { if ctx.len[0] < bits {
ctx.len[1] += 1 ctx.len[1] += 1
} }
} }
gost_compress :: proc(h, m: []u32) { compress :: proc(h, m: []u32) {
key, u, v, w, s: [8]u32 key, u, v, w, s: [8]u32
copy(u[:], h) copy(u[:], h)
@@ -272,7 +249,7 @@ gost_compress :: proc(h, m: []u32) {
r := h[i] r := h[i]
l := h[i + 1] l := h[i + 1]
t: u32 t: u32
l, r, t = GOST_ENCRYPT(l, r, 0, key[:]) l, r, t = ENCRYPT(l, r, 0, key[:])
s[i] = r s[i] = r
s[i + 1] = l s[i + 1] = l
@@ -381,77 +358,3 @@ gost_compress :: proc(h, m: []u32) {
(v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ v[4] ~ (v[5] >> 16) ~ v[5] ~ (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] (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)
}
}
+198 -364
View File
@@ -6,7 +6,6 @@ package groestl
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <http://www.groestl.info/Groestl.zip> Implementation of the GROESTL hashing algorithm, as defined in <http://www.groestl.info/Groestl.zip>
*/ */
@@ -14,70 +13,6 @@ package groestl
import "core:os" import "core:os"
import "core:io" 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
#partial switch size {
case ._28: ctx.hashbitlen = 224
case ._32: ctx.hashbitlen = 256
case ._48: ctx.hashbitlen = 384
case ._64: ctx.hashbitlen = 512
}
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = size
}
/* /*
High level API High level API
*/ */
@@ -91,22 +26,46 @@ hash_string_224 :: proc(data: string) -> [28]byte {
// hash_bytes_224 will hash the given input and return the // hash_bytes_224 will hash the given input and return the
// computed hash // computed hash
hash_bytes_224 :: proc(data: []byte) -> [28]byte { hash_bytes_224 :: proc(data: []byte) -> [28]byte {
_create_groestl_ctx(._28) hash: [28]byte
return _hash_impl->hash_bytes_28(data) ctx: Groestl_Context
ctx.hashbitlen = 224
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_224 will read the stream in chunks and compute a // hash_stream_224 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) { hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
_create_groestl_ctx(._28) hash: [28]byte
return _hash_impl->hash_stream_28(s) ctx: Groestl_Context
ctx.hashbitlen = 224
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_224 will read the file provided by the given handle // hash_file_224 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) { hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
_create_groestl_ctx(._28) if !load_at_once {
return _hash_impl->hash_file_28(hd, load_at_once) return hash_stream_224(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_224(buf[:]), ok
}
}
return [28]byte{}, false
} }
hash_224 :: proc { hash_224 :: proc {
@@ -125,22 +84,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
// hash_bytes_256 will hash the given input and return the // hash_bytes_256 will hash the given input and return the
// computed hash // computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte { hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_groestl_ctx(._32) hash: [32]byte
return _hash_impl->hash_bytes_32(data) ctx: Groestl_Context
ctx.hashbitlen = 256
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_256 will read the stream in chunks and compute a // hash_stream_256 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_groestl_ctx(._32) hash: [32]byte
return _hash_impl->hash_stream_32(s) ctx: Groestl_Context
ctx.hashbitlen = 256
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_256 will read the file provided by the given handle // hash_file_256 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_groestl_ctx(._32) if !load_at_once {
return _hash_impl->hash_file_32(hd, load_at_once) return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [32]byte{}, false
} }
hash_256 :: proc { hash_256 :: proc {
@@ -159,22 +142,46 @@ hash_string_384 :: proc(data: string) -> [48]byte {
// hash_bytes_384 will hash the given input and return the // hash_bytes_384 will hash the given input and return the
// computed hash // computed hash
hash_bytes_384 :: proc(data: []byte) -> [48]byte { hash_bytes_384 :: proc(data: []byte) -> [48]byte {
_create_groestl_ctx(._48) hash: [48]byte
return _hash_impl->hash_bytes_48(data) ctx: Groestl_Context
ctx.hashbitlen = 384
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_384 will read the stream in chunks and compute a // hash_stream_384 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) { hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
_create_groestl_ctx(._48) hash: [48]byte
return _hash_impl->hash_stream_48(s) ctx: Groestl_Context
ctx.hashbitlen = 384
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_384 will read the file provided by the given handle // hash_file_384 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) { hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
_create_groestl_ctx(._48) if !load_at_once {
return _hash_impl->hash_file_48(hd, load_at_once) return hash_stream_384(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_384(buf[:]), ok
}
}
return [48]byte{}, false
} }
hash_384 :: proc { hash_384 :: proc {
@@ -193,22 +200,46 @@ hash_string_512 :: proc(data: string) -> [64]byte {
// hash_bytes_512 will hash the given input and return the // hash_bytes_512 will hash the given input and return the
// computed hash // computed hash
hash_bytes_512 :: proc(data: []byte) -> [64]byte { hash_bytes_512 :: proc(data: []byte) -> [64]byte {
_create_groestl_ctx(._64) hash: [64]byte
return _hash_impl->hash_bytes_64(data) ctx: Groestl_Context
ctx.hashbitlen = 512
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_512 will read the stream in chunks and compute a // hash_stream_512 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) { hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_groestl_ctx(._64) hash: [64]byte
return _hash_impl->hash_stream_64(s) ctx: Groestl_Context
ctx.hashbitlen = 512
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_512 will read the file provided by the given handle // hash_file_512 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) { hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_groestl_ctx(._64) if !load_at_once {
return _hash_impl->hash_file_64(hd, load_at_once) return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [64]byte{}, false
} }
hash_512 :: proc { hash_512 :: proc {
@@ -222,201 +253,101 @@ hash_512 :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { init :: proc(ctx: ^Groestl_Context) {
_hash_impl->init() assert(ctx.hashbitlen == 224 || ctx.hashbitlen == 256 || ctx.hashbitlen == 384 || ctx.hashbitlen == 512, "hashbitlen must be set to 224, 256, 384 or 512")
} if ctx.hashbitlen <= 256 {
ctx.rounds = 10
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { ctx.columns = 8
_hash_impl->update(data) ctx.statesize = 64
} } else {
ctx.rounds = 14
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { ctx.columns = 16
_hash_impl->final(hash) ctx.statesize = 128
} }
for i := 8 - size_of(i32); i < 8; i += 1 {
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte { ctx.chaining[i][ctx.columns - 1] = byte(ctx.hashbitlen >> (8 * (7 - uint(i))))
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) { update :: proc(ctx: ^Groestl_Context, data: []byte) {
hash: [28]byte databitlen := len(data) * 8
if c, ok := ctx.internal_ctx.(Groestl_Context); ok { msglen := databitlen / 8
init_odin(&c) rem := databitlen % 8
buf := make([]byte, 512)
defer delete(buf) i: int
read := 1 assert(ctx.bits_in_last_byte == 0)
for read > 0 {
read, _ = fs->impl_read(buf) if ctx.buf_ptr != 0 {
if read > 0 { for i = 0; ctx.buf_ptr < ctx.statesize && i < msglen; i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1 {
update_odin(&c, buf[:read]) 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
} }
final_odin(&c, hash[:])
return hash, true ctx.buf_ptr = 0
} else { transform(ctx, ctx.buffer[:], u32(ctx.statesize))
return hash, false }
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
} }
} }
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) { final :: proc(ctx: ^Groestl_Context, hash: []byte) {
if !load_at_once { hashbytelen := ctx.hashbitlen / 8
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
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 { } else {
if buf, ok := os.read_entire_file(hd); ok { ctx.buffer[ctx.buf_ptr] = 0x80
return hash_bytes_odin_28(ctx, buf[:]), ok 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
} }
return [28]byte{}, false
}
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { for ctx.buf_ptr < ctx.statesize - 8 {
hash: [32]byte ctx.buffer[ctx.buf_ptr] = 0
if c, ok := ctx.internal_ctx.(Groestl_Context); ok { ctx.buf_ptr += 1
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) { ctx.block_counter += 1
hash: [32]byte ctx.buf_ptr = ctx.statesize
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
init_odin(&c) for ctx.buf_ptr > ctx.statesize - 8 {
buf := make([]byte, 512) ctx.buf_ptr -= 1
defer delete(buf) ctx.buffer[ctx.buf_ptr] = byte(ctx.block_counter)
read := 1 ctx.block_counter >>= 8
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, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { transform(ctx, ctx.buffer[:], u32(ctx.statesize))
if !load_at_once { output_transformation(ctx)
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte { for i, j := ctx.statesize - hashbytelen , 0; i < ctx.statesize; i, j = i + 1, j + 1 {
hash: [48]byte hash[j] = ctx.chaining[i % 8][i / 8]
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, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
if !load_at_once {
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_48(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_64(ctx, buf[:]), 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)
} }
} }
@@ -631,100 +562,3 @@ add_roundconstant :: proc(x: [][16]byte, columns: int, round: byte, v: Groestl_V
} }
} }
} }
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]
}
}
File diff suppressed because it is too large Load Diff
+217 -383
View File
@@ -6,7 +6,6 @@ package jh
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html> Implementation of the JH hashing algorithm, as defined in <https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html>
*/ */
@@ -14,70 +13,6 @@ package jh
import "core:os" import "core:os"
import "core:io" 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
#partial switch size {
case ._28: ctx.hashbitlen = 224
case ._32: ctx.hashbitlen = 256
case ._48: ctx.hashbitlen = 384
case ._64: ctx.hashbitlen = 512
}
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = size
}
/* /*
High level API High level API
*/ */
@@ -91,22 +26,46 @@ hash_string_224 :: proc(data: string) -> [28]byte {
// hash_bytes_224 will hash the given input and return the // hash_bytes_224 will hash the given input and return the
// computed hash // computed hash
hash_bytes_224 :: proc(data: []byte) -> [28]byte { hash_bytes_224 :: proc(data: []byte) -> [28]byte {
_create_jh_ctx(._28) hash: [28]byte
return _hash_impl->hash_bytes_28(data) ctx: Jh_Context
ctx.hashbitlen = 224
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_224 will read the stream in chunks and compute a // hash_stream_224 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) { hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
_create_jh_ctx(._28) hash: [28]byte
return _hash_impl->hash_stream_28(s) ctx: Jh_Context
ctx.hashbitlen = 224
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_224 will read the file provided by the given handle // hash_file_224 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) { hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
_create_jh_ctx(._28) if !load_at_once {
return _hash_impl->hash_file_28(hd, load_at_once) return hash_stream_224(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_224(buf[:]), ok
}
}
return [28]byte{}, false
} }
hash_224 :: proc { hash_224 :: proc {
@@ -125,22 +84,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
// hash_bytes_256 will hash the given input and return the // hash_bytes_256 will hash the given input and return the
// computed hash // computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte { hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_jh_ctx(._32) hash: [32]byte
return _hash_impl->hash_bytes_32(data) ctx: Jh_Context
ctx.hashbitlen = 256
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_256 will read the stream in chunks and compute a // hash_stream_256 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_jh_ctx(._32) hash: [32]byte
return _hash_impl->hash_stream_32(s) ctx: Jh_Context
ctx.hashbitlen = 256
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_256 will read the file provided by the given handle // hash_file_256 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_jh_ctx(._32) if !load_at_once {
return _hash_impl->hash_file_32(hd, load_at_once) return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [32]byte{}, false
} }
hash_256 :: proc { hash_256 :: proc {
@@ -159,22 +142,46 @@ hash_string_384 :: proc(data: string) -> [48]byte {
// hash_bytes_384 will hash the given input and return the // hash_bytes_384 will hash the given input and return the
// computed hash // computed hash
hash_bytes_384 :: proc(data: []byte) -> [48]byte { hash_bytes_384 :: proc(data: []byte) -> [48]byte {
_create_jh_ctx(._48) hash: [48]byte
return _hash_impl->hash_bytes_48(data) ctx: Jh_Context
ctx.hashbitlen = 384
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_384 will read the stream in chunks and compute a // hash_stream_384 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) { hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
_create_jh_ctx(._48) hash: [48]byte
return _hash_impl->hash_stream_48(s) ctx: Jh_Context
ctx.hashbitlen = 384
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_384 will read the file provided by the given handle // hash_file_384 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) { hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
_create_jh_ctx(._48) if !load_at_once {
return _hash_impl->hash_file_48(hd, load_at_once) return hash_stream_384(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_384(buf[:]), ok
}
}
return [48]byte{}, false
} }
hash_384 :: proc { hash_384 :: proc {
@@ -193,22 +200,46 @@ hash_string_512 :: proc(data: string) -> [64]byte {
// hash_bytes_512 will hash the given input and return the // hash_bytes_512 will hash the given input and return the
// computed hash // computed hash
hash_bytes_512 :: proc(data: []byte) -> [64]byte { hash_bytes_512 :: proc(data: []byte) -> [64]byte {
_create_jh_ctx(._64) hash: [64]byte
return _hash_impl->hash_bytes_64(data) ctx: Jh_Context
ctx.hashbitlen = 512
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_512 will read the stream in chunks and compute a // hash_stream_512 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) { hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_jh_ctx(._64) hash: [64]byte
return _hash_impl->hash_stream_64(s) ctx: Jh_Context
ctx.hashbitlen = 512
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_512 will read the file provided by the given handle // hash_file_512 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) { hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_jh_ctx(._64) if !load_at_once {
return _hash_impl->hash_file_64(hd, load_at_once) return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [64]byte{}, false
} }
hash_512 :: proc { hash_512 :: proc {
@@ -222,201 +253,98 @@ hash_512 :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { init :: proc(ctx: ^Jh_Context) {
_hash_impl->init() assert(ctx.hashbitlen == 224 || ctx.hashbitlen == 256 || ctx.hashbitlen == 384 || ctx.hashbitlen == 512, "hashbitlen must be set to 224, 256, 384 or 512")
ctx.H[1] = byte(ctx.hashbitlen) & 0xff
ctx.H[0] = byte(ctx.hashbitlen >> 8) & 0xff
F8(ctx)
} }
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { update :: proc(ctx: ^Jh_Context, data: []byte) {
_hash_impl->update(data) databitlen := u64(len(data)) * 8
} ctx.databitlen += databitlen
i := u64(0)
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { if (ctx.buffer_size > 0) && ((ctx.buffer_size + databitlen) < 512) {
_hash_impl->final(hash) 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
}
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte { if (ctx.buffer_size > 0 ) && ((ctx.buffer_size + databitlen) >= 512) {
hash: [28]byte copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
if c, ok := ctx.internal_ctx.(Jh_Context); ok { i = 64 - (ctx.buffer_size >> 3)
init_odin(&c) databitlen = databitlen - (512 - ctx.buffer_size)
update_odin(&c, data) F8(ctx)
final_odin(&c, hash[:]) ctx.buffer_size = 0
}
for databitlen >= 512 {
copy(ctx.buffer[:], data[i:i + 64])
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
} }
return hash
} }
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) { final :: proc(ctx: ^Jh_Context, hash: []byte) {
hash: [28]byte if ctx.databitlen & 0x1ff == 0 {
if c, ok := ctx.internal_ctx.(Jh_Context); ok { for i := 0; i < 64; i += 1 {
init_odin(&c) ctx.buffer[i] = 0
buf := make([]byte, 512) }
defer delete(buf) ctx.buffer[0] = 0x80
read := 1 ctx.buffer[63] = byte(ctx.databitlen) & 0xff
for read > 0 { ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
read, _ = fs->impl_read(buf) ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
if read > 0 { ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
update_odin(&c, buf[:read]) 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
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
} }
} }
final_odin(&c, hash[:]) ctx.buffer[(ctx.databitlen & 0x1ff) >> 3] |= 1 << (7 - (ctx.databitlen & 7))
return hash, true F8(ctx)
} else { for i := 0; i < 64; i += 1 {
return hash, false ctx.buffer[i] = 0
}
}
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
if !load_at_once {
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_28(ctx, buf[:]), ok
} }
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
F8(ctx)
} }
return [28]byte{}, false switch ctx.hashbitlen {
} case 224: copy(hash[:], ctx.H[100:128])
case 256: copy(hash[:], ctx.H[96:128])
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { case 384: copy(hash[:], ctx.H[80:128])
hash: [32]byte case 512: copy(hash[:], ctx.H[64:128])
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, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
if !load_at_once {
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_48(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_64(ctx, buf[:]), 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)
} }
} }
@@ -424,7 +352,7 @@ _final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
JH implementation JH implementation
*/ */
JH_ROUNDCONSTANT_ZERO := [64]byte { ROUNDCONSTANT_ZERO := [64]byte {
0x6, 0xa, 0x0, 0x9, 0xe, 0x6, 0x6, 0x7, 0x6, 0xa, 0x0, 0x9, 0xe, 0x6, 0x6, 0x7,
0xf, 0x3, 0xb, 0xc, 0xc, 0x9, 0x0, 0x8, 0xf, 0x3, 0xb, 0xc, 0xc, 0x9, 0x0, 0x8,
0xb, 0x2, 0xf, 0xb, 0x1, 0x3, 0x6, 0x6, 0xb, 0x2, 0xf, 0xb, 0x1, 0x3, 0x6, 0x6,
@@ -435,7 +363,7 @@ JH_ROUNDCONSTANT_ZERO := [64]byte {
0x0, 0x6, 0x6, 0x7, 0x3, 0x2, 0x2, 0xa, 0x0, 0x6, 0x6, 0x7, 0x3, 0x2, 0x2, 0xa,
} }
JH_S := [2][16]byte { SBOX := [2][16]byte {
{9, 0, 4, 11, 13, 12, 3, 15, 1, 10, 2, 6, 7, 5, 8, 14}, {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}, {3, 12, 6, 13, 5, 7, 1, 9, 15, 2, 0, 4, 11, 10, 14, 8},
} }
@@ -450,7 +378,7 @@ Jh_Context :: struct {
buffer: [64]byte, buffer: [64]byte,
} }
JH_E8_finaldegroup :: proc(ctx: ^Jh_Context) { E8_finaldegroup :: proc(ctx: ^Jh_Context) {
t0,t1,t2,t3: byte t0,t1,t2,t3: byte
tem: [256]byte tem: [256]byte
for i := 0; i < 128; i += 1 { for i := 0; i < 128; i += 1 {
@@ -473,11 +401,11 @@ JH_E8_finaldegroup :: proc(ctx: ^Jh_Context) {
} }
} }
jh_update_roundconstant :: proc(ctx: ^Jh_Context) { update_roundconstant :: proc(ctx: ^Jh_Context) {
tem: [64]byte tem: [64]byte
t: byte t: byte
for i := 0; i < 64; i += 1 { for i := 0; i < 64; i += 1 {
tem[i] = JH_S[0][ctx.roundconstant[i]] tem[i] = SBOX[0][ctx.roundconstant[i]]
} }
for i := 0; i < 64; i += 2 { for i := 0; i < 64; i += 2 {
tem[i + 1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf tem[i + 1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf
@@ -499,14 +427,14 @@ jh_update_roundconstant :: proc(ctx: ^Jh_Context) {
} }
} }
JH_R8 :: proc(ctx: ^Jh_Context) { R8 :: proc(ctx: ^Jh_Context) {
t: byte t: byte
tem, roundconstant_expanded: [256]byte tem, roundconstant_expanded: [256]byte
for i := u32(0); i < 256; i += 1 { for i := u32(0); i < 256; i += 1 {
roundconstant_expanded[i] = (ctx.roundconstant[i >> 2] >> (3 - (i & 3)) ) & 1 roundconstant_expanded[i] = (ctx.roundconstant[i >> 2] >> (3 - (i & 3)) ) & 1
} }
for i := 0; i < 256; i += 1 { for i := 0; i < 256; i += 1 {
tem[i] = JH_S[roundconstant_expanded[i]][ctx.A[i]] tem[i] = SBOX[roundconstant_expanded[i]][ctx.A[i]]
} }
for i := 0; i < 256; i += 2 { for i := 0; i < 256; i += 2 {
tem[i+1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf tem[i+1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf
@@ -528,7 +456,7 @@ JH_R8 :: proc(ctx: ^Jh_Context) {
} }
} }
JH_E8_initialgroup :: proc(ctx: ^Jh_Context) { E8_initialgroup :: proc(ctx: ^Jh_Context) {
t0, t1, t2, t3: byte t0, t1, t2, t3: byte
tem: [256]byte tem: [256]byte
for i := u32(0); i < 256; i += 1 { for i := u32(0); i < 256; i += 1 {
@@ -544,118 +472,24 @@ JH_E8_initialgroup :: proc(ctx: ^Jh_Context) {
} }
} }
JH_E8 :: proc(ctx: ^Jh_Context) { E8 :: proc(ctx: ^Jh_Context) {
for i := 0; i < 64; i += 1 { for i := 0; i < 64; i += 1 {
ctx.roundconstant[i] = JH_ROUNDCONSTANT_ZERO[i] ctx.roundconstant[i] = ROUNDCONSTANT_ZERO[i]
} }
JH_E8_initialgroup(ctx) E8_initialgroup(ctx)
for i := 0; i < 42; i += 1 { for i := 0; i < 42; i += 1 {
JH_R8(ctx) R8(ctx)
jh_update_roundconstant(ctx) update_roundconstant(ctx)
} }
JH_E8_finaldegroup(ctx) E8_finaldegroup(ctx)
} }
JH_F8 :: proc(ctx: ^Jh_Context) { F8 :: proc(ctx: ^Jh_Context) {
for i := 0; i < 64; i += 1 { for i := 0; i < 64; i += 1 {
ctx.H[i] ~= ctx.buffer[i] ctx.H[i] ~= ctx.buffer[i]
} }
JH_E8(ctx) E8(ctx)
for i := 0; i < 64; i += 1 { for i := 0; i < 64; i += 1 {
ctx.H[i + 64] ~= ctx.buffer[i] 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])
}
}
+137 -285
View File
@@ -6,7 +6,6 @@ package keccak
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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. 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. This is done because the padding in the SHA3 standard was changed by the NIST, resulting in a different output.
@@ -15,57 +14,8 @@ package keccak
import "core:os" import "core:os"
import "core:io" import "core:io"
import "../botan"
import "../_ctx"
import "../_sha3" 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 High level API
@@ -80,22 +30,48 @@ hash_string_224 :: proc(data: string) -> [28]byte {
// hash_bytes_224 will hash the given input and return the // hash_bytes_224 will hash the given input and return the
// computed hash // computed hash
hash_bytes_224 :: proc(data: []byte) -> [28]byte { hash_bytes_224 :: proc(data: []byte) -> [28]byte {
_create_keccak_ctx(28) hash: [28]byte
return _hash_impl->hash_bytes_28(data) ctx: _sha3.Sha3_Context
ctx.mdlen = 28
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
} }
// hash_stream_224 will read the stream in chunks and compute a // hash_stream_224 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) { hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
_create_keccak_ctx(28) hash: [28]byte
return _hash_impl->hash_stream_28(s) ctx: _sha3.Sha3_Context
ctx.mdlen = 28
ctx.is_keccak = true
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
} }
// hash_file_224 will read the file provided by the given handle // hash_file_224 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) { hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
_create_keccak_ctx(28) if !load_at_once {
return _hash_impl->hash_file_28(hd, load_at_once) return hash_stream_224(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_224(buf[:]), ok
}
}
return [28]byte{}, false
} }
hash_224 :: proc { hash_224 :: proc {
@@ -114,22 +90,48 @@ hash_string_256 :: proc(data: string) -> [32]byte {
// hash_bytes_256 will hash the given input and return the // hash_bytes_256 will hash the given input and return the
// computed hash // computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte { hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_keccak_ctx(32) hash: [32]byte
return _hash_impl->hash_bytes_32(data) ctx: _sha3.Sha3_Context
ctx.mdlen = 32
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
} }
// hash_stream_256 will read the stream in chunks and compute a // hash_stream_256 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_keccak_ctx(32) hash: [32]byte
return _hash_impl->hash_stream_32(s) ctx: _sha3.Sha3_Context
ctx.mdlen = 32
ctx.is_keccak = true
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
} }
// hash_file_256 will read the file provided by the given handle // hash_file_256 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_keccak_ctx(32) if !load_at_once {
return _hash_impl->hash_file_32(hd, load_at_once) return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [32]byte{}, false
} }
hash_256 :: proc { hash_256 :: proc {
@@ -148,22 +150,48 @@ hash_string_384 :: proc(data: string) -> [48]byte {
// hash_bytes_384 will hash the given input and return the // hash_bytes_384 will hash the given input and return the
// computed hash // computed hash
hash_bytes_384 :: proc(data: []byte) -> [48]byte { hash_bytes_384 :: proc(data: []byte) -> [48]byte {
_create_keccak_ctx(48) hash: [48]byte
return _hash_impl->hash_bytes_48(data) ctx: _sha3.Sha3_Context
ctx.mdlen = 48
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
} }
// hash_stream_384 will read the stream in chunks and compute a // hash_stream_384 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) { hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
_create_keccak_ctx(48) hash: [48]byte
return _hash_impl->hash_stream_48(s) ctx: _sha3.Sha3_Context
ctx.mdlen = 48
ctx.is_keccak = true
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
} }
// hash_file_384 will read the file provided by the given handle // hash_file_384 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) { hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
_create_keccak_ctx(48) if !load_at_once {
return _hash_impl->hash_file_48(hd, load_at_once) return hash_stream_384(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_384(buf[:]), ok
}
}
return [48]byte{}, false
} }
hash_384 :: proc { hash_384 :: proc {
@@ -182,22 +210,48 @@ hash_string_512 :: proc(data: string) -> [64]byte {
// hash_bytes_512 will hash the given input and return the // hash_bytes_512 will hash the given input and return the
// computed hash // computed hash
hash_bytes_512 :: proc(data: []byte) -> [64]byte { hash_bytes_512 :: proc(data: []byte) -> [64]byte {
_create_keccak_ctx(64) hash: [64]byte
return _hash_impl->hash_bytes_64(data) ctx: _sha3.Sha3_Context
ctx.mdlen = 64
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
} }
// hash_stream_512 will read the stream in chunks and compute a // hash_stream_512 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) { hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_keccak_ctx(64) hash: [64]byte
return _hash_impl->hash_stream_64(s) ctx: _sha3.Sha3_Context
ctx.mdlen = 64
ctx.is_keccak = true
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
} }
// hash_file_512 will read the file provided by the given handle // hash_file_512 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) { hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_keccak_ctx(64) if !load_at_once {
return _hash_impl->hash_file_64(hd, load_at_once) return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [64]byte{}, false
} }
hash_512 :: proc { hash_512 :: proc {
@@ -211,219 +265,17 @@ hash_512 :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { Sha3_Context :: _sha3.Sha3_Context
_hash_impl->init()
init :: proc(ctx: ^_sha3.Sha3_Context) {
ctx.is_keccak = true
_sha3.init(ctx)
} }
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
_hash_impl->update(data) _sha3.update(ctx, data)
} }
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
_hash_impl->final(hash) _sha3.final(ctx, 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, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
if !load_at_once {
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_28(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
if !load_at_once {
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_48(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_64(ctx, buf[:]), ok
}
}
return [64]byte{}, false
}
@(private)
_create_keccak_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_keccak_ctx(28)
case ._32: _create_keccak_ctx(32)
case ._48: _create_keccak_ctx(48)
case ._64: _create_keccak_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)
}
} }
+49 -151
View File
@@ -6,7 +6,6 @@ package md2
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://datatracker.ietf.org/doc/html/rfc1319> Implementation of the MD2 hashing algorithm, as defined in RFC 1319 <https://datatracker.ietf.org/doc/html/rfc1319>
*/ */
@@ -14,48 +13,6 @@ package md2
import "core:os" import "core:os"
import "core:io" 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 High level API
*/ */
@@ -69,22 +26,44 @@ hash_string :: proc(data: string) -> [16]byte {
// hash_bytes will hash the given input and return the // hash_bytes will hash the given input and return the
// computed hash // computed hash
hash_bytes :: proc(data: []byte) -> [16]byte { hash_bytes :: proc(data: []byte) -> [16]byte {
_create_md2_ctx() hash: [16]byte
return _hash_impl->hash_bytes_16(data) ctx: Md2_Context
// init(&ctx) No-op
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream will read the stream in chunks and compute a // hash_stream will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) { hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
_create_md2_ctx() hash: [16]byte
return _hash_impl->hash_stream_16(s) ctx: Md2_Context
// init(&ctx) No-op
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file will read the file provided by the given handle // hash_file will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) { hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
_create_md2_ctx() if !load_at_once {
return _hash_impl->hash_file_16(hd, load_at_once) return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [16]byte{}, false
} }
hash :: proc { hash :: proc {
@@ -98,85 +77,32 @@ hash :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { @(warning="Init is a no-op for MD2")
_hash_impl->init() init :: proc(ctx: ^Md2_Context) {
// No action needed here
} }
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { update :: proc(ctx: ^Md2_Context, data: []byte) {
_hash_impl->update(data) 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 :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { final :: proc(ctx: ^Md2_Context, hash: []byte) {
_hash_impl->final(hash) to_pad := byte(16 - ctx.datalen)
} for ctx.datalen < 16 {
ctx.data[ctx.datalen] = to_pad
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte { ctx.datalen += 1
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 transform(ctx, ctx.data[:])
} transform(ctx, ctx.checksum[:])
for i := 0; i < 16; i += 1 {
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) { hash[i] = ctx.state[i]
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, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), 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)
} }
} }
@@ -232,31 +158,3 @@ transform :: proc(ctx: ^Md2_Context, data: []byte) {
t = ctx.checksum[j] 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]
}
}
+74 -175
View File
@@ -16,47 +16,6 @@ import "core:os"
import "core:io" import "core:io"
import "../util" 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 High level API
@@ -71,22 +30,44 @@ hash_string :: proc(data: string) -> [16]byte {
// hash_bytes will hash the given input and return the // hash_bytes will hash the given input and return the
// computed hash // computed hash
hash_bytes :: proc(data: []byte) -> [16]byte { hash_bytes :: proc(data: []byte) -> [16]byte {
_create_md4_ctx() hash: [16]byte
return _hash_impl->hash_bytes_16(data) ctx: Md4_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream will read the stream in chunks and compute a // hash_stream will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) { hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
_create_md4_ctx() hash: [16]byte
return _hash_impl->hash_stream_16(s) ctx: Md4_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file will read the file provided by the given handle // hash_file will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) { hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
_create_md4_ctx() if !load_at_once {
return _hash_impl->hash_file_16(hd, load_at_once) return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [16]byte{}, false
} }
hash :: proc { hash :: proc {
@@ -100,85 +81,61 @@ hash :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { init :: proc(ctx: ^Md4_Context) {
_hash_impl->init() ctx.state[0] = 0x67452301
ctx.state[1] = 0xefcdab89
ctx.state[2] = 0x98badcfe
ctx.state[3] = 0x10325476
} }
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { update :: proc(ctx: ^Md4_Context, data: []byte) {
_hash_impl->update(data) for i := 0; i < len(data); i += 1 {
} ctx.data[ctx.datalen] = data[i]
ctx.datalen += 1
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { if(ctx.datalen == BLOCK_SIZE) {
_hash_impl->final(hash) transform(ctx, ctx.data[:])
} ctx.bitlen += 512
ctx.datalen = 0
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, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
} }
} }
return [16]byte{}, false
} }
@(private) final :: proc(ctx: ^Md4_Context, hash: []byte) {
_create_md4_ctx :: #force_inline proc() { i := ctx.datalen
ctx: Md4_Context if ctx.datalen < 56 {
_hash_impl.internal_ctx = ctx ctx.data[i] = 0x80
_hash_impl.hash_size = ._16 i += 1
} for i < 56 {
ctx.data[i] = 0x00
@(private) i += 1
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { }
_create_md4_ctx() } else if ctx.datalen >= 56 {
if c, ok := ctx.internal_ctx.(Md4_Context); ok { ctx.data[i] = 0x80
init_odin(&c) i += 1
for i < BLOCK_SIZE {
ctx.data[i] = 0x00
i += 1
}
transform(ctx, ctx.data[:])
mem.set(&ctx.data, 0, 56)
} }
}
@(private) ctx.bitlen += u64(ctx.datalen * 8)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { ctx.data[56] = byte(ctx.bitlen)
if c, ok := ctx.internal_ctx.(Md4_Context); ok { ctx.data[57] = byte(ctx.bitlen >> 8)
update_odin(&c, data) 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[:])
@(private) for i = 0; i < 4; i += 1 {
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
if c, ok := ctx.internal_ctx.(Md4_Context); ok { hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
final_odin(&c, hash) hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
} }
} }
@@ -282,61 +239,3 @@ transform :: proc(ctx: ^Md4_Context, data: []byte) {
ctx.state[2] += c ctx.state[2] += c
ctx.state[3] += d 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
}
}
+75 -177
View File
@@ -6,7 +6,6 @@ package md5
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://datatracker.ietf.org/doc/html/rfc1321> Implementation of the MD5 hashing algorithm, as defined in RFC 1321 <https://datatracker.ietf.org/doc/html/rfc1321>
*/ */
@@ -16,47 +15,6 @@ import "core:os"
import "core:io" import "core:io"
import "../util" 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 High level API
@@ -71,22 +29,44 @@ hash_string :: proc(data: string) -> [16]byte {
// hash_bytes will hash the given input and return the // hash_bytes will hash the given input and return the
// computed hash // computed hash
hash_bytes :: proc(data: []byte) -> [16]byte { hash_bytes :: proc(data: []byte) -> [16]byte {
_create_md5_ctx() hash: [16]byte
return _hash_impl->hash_bytes_16(data) ctx: Md5_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream will read the stream in chunks and compute a // hash_stream will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) { hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
_create_md5_ctx() hash: [16]byte
return _hash_impl->hash_stream_16(s) ctx: Md5_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file will read the file provided by the given handle // hash_file will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) { hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
_create_md5_ctx() if !load_at_once {
return _hash_impl->hash_file_16(hd, load_at_once) return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [16]byte{}, false
} }
hash :: proc { hash :: proc {
@@ -100,85 +80,63 @@ hash :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { init :: proc(ctx: ^Md5_Context) {
_hash_impl->init() ctx.state[0] = 0x67452301
ctx.state[1] = 0xefcdab89
ctx.state[2] = 0x98badcfe
ctx.state[3] = 0x10325476
} }
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { update :: proc(ctx: ^Md5_Context, data: []byte) {
_hash_impl->update(data) for i := 0; i < len(data); i += 1 {
} ctx.data[ctx.datalen] = data[i]
ctx.datalen += 1
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { if(ctx.datalen == BLOCK_SIZE) {
_hash_impl->final(hash) transform(ctx, ctx.data[:])
} ctx.bitlen += 512
ctx.datalen = 0
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, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
} }
} }
return [16]byte{}, false
} }
@(private) final :: proc(ctx: ^Md5_Context, hash: []byte){
_create_md5_ctx :: #force_inline proc() { i : u32
ctx: Md5_Context i = ctx.datalen
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._16
}
@(private) if ctx.datalen < 56 {
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { ctx.data[i] = 0x80
_create_md5_ctx() i += 1
if c, ok := ctx.internal_ctx.(Md5_Context); ok { for i < 56 {
init_odin(&c) 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)
} }
}
@(private) ctx.bitlen += u64(ctx.datalen * 8)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { ctx.data[56] = byte(ctx.bitlen)
if c, ok := ctx.internal_ctx.(Md5_Context); ok { ctx.data[57] = byte(ctx.bitlen >> 8)
update_odin(&c, data) 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[:])
@(private) for i = 0; i < 4; i += 1 {
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
if c, ok := ctx.internal_ctx.(Md5_Context); ok { hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
final_odin(&c, hash) hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
} }
} }
@@ -303,63 +261,3 @@ transform :: proc(ctx: ^Md5_Context, data: []byte) {
ctx.state[2] += c ctx.state[2] += c
ctx.state[3] += d 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
}
}
+214 -428
View File
@@ -6,7 +6,6 @@ package ripemd
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://homes.esat.kuleuven.be/~bosselae/ripemd160.html> Implementation for the RIPEMD hashing algorithm as defined in <https://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
*/ */
@@ -15,56 +14,6 @@ import "core:os"
import "core:io" import "core:io"
import "../util" 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 High level API
@@ -79,22 +28,44 @@ hash_string_128 :: proc(data: string) -> [16]byte {
// hash_bytes_128 will hash the given input and return the // hash_bytes_128 will hash the given input and return the
// computed hash // computed hash
hash_bytes_128 :: proc(data: []byte) -> [16]byte { hash_bytes_128 :: proc(data: []byte) -> [16]byte {
_create_ripemd_ctx(16) hash: [16]byte
return _hash_impl->hash_bytes_16(data) ctx: Ripemd128_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_128 will read the stream in chunks and compute a // hash_stream_128 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) { hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
_create_ripemd_ctx(16) hash: [16]byte
return _hash_impl->hash_stream_16(s) ctx: Ripemd128_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_128 will read the file provided by the given handle // hash_file_128 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) { hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
_create_ripemd_ctx(16) if !load_at_once {
return _hash_impl->hash_file_16(hd, load_at_once) return hash_stream_128(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_128(buf[:]), ok
}
}
return [16]byte{}, false
} }
hash_128 :: proc { hash_128 :: proc {
@@ -113,22 +84,44 @@ hash_string_160 :: proc(data: string) -> [20]byte {
// hash_bytes_160 will hash the given input and return the // hash_bytes_160 will hash the given input and return the
// computed hash // computed hash
hash_bytes_160 :: proc(data: []byte) -> [20]byte { hash_bytes_160 :: proc(data: []byte) -> [20]byte {
_create_ripemd_ctx(20) hash: [20]byte
return _hash_impl->hash_bytes_20(data) ctx: Ripemd160_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_160 will read the stream in chunks and compute a // hash_stream_160 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) { hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
_create_ripemd_ctx(20) hash: [20]byte
return _hash_impl->hash_stream_20(s) ctx: Ripemd160_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_160 will read the file provided by the given handle // hash_file_160 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) { hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
_create_ripemd_ctx(20) if !load_at_once {
return _hash_impl->hash_file_20(hd, load_at_once) return hash_stream_160(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_160(buf[:]), ok
}
}
return [20]byte{}, false
} }
hash_160 :: proc { hash_160 :: proc {
@@ -147,22 +140,44 @@ hash_string_256 :: proc(data: string) -> [32]byte {
// hash_bytes_256 will hash the given input and return the // hash_bytes_256 will hash the given input and return the
// computed hash // computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte { hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_ripemd_ctx(32) hash: [32]byte
return _hash_impl->hash_bytes_32(data) ctx: Ripemd256_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_256 will read the stream in chunks and compute a // hash_stream_256 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_ripemd_ctx(32) hash: [32]byte
return _hash_impl->hash_stream_32(s) ctx: Ripemd256_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_256 will read the file provided by the given handle // hash_file_256 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_ripemd_ctx(32) if !load_at_once {
return _hash_impl->hash_file_32(hd, load_at_once) return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [32]byte{}, false
} }
hash_256 :: proc { hash_256 :: proc {
@@ -181,22 +196,44 @@ hash_string_320 :: proc(data: string) -> [40]byte {
// hash_bytes_320 will hash the given input and return the // hash_bytes_320 will hash the given input and return the
// computed hash // computed hash
hash_bytes_320 :: proc(data: []byte) -> [40]byte { hash_bytes_320 :: proc(data: []byte) -> [40]byte {
_create_ripemd_ctx(40) hash: [40]byte
return _hash_impl->hash_bytes_40(data) ctx: Ripemd320_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_320 will read the stream in chunks and compute a // hash_stream_320 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_320 :: proc(s: io.Stream) -> ([40]byte, bool) { hash_stream_320 :: proc(s: io.Stream) -> ([40]byte, bool) {
_create_ripemd_ctx(40) hash: [40]byte
return _hash_impl->hash_stream_40(s) ctx: Ripemd320_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_320 will read the file provided by the given handle // hash_file_320 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_320 :: proc(hd: os.Handle, load_at_once := false) -> ([40]byte, bool) { hash_file_320 :: proc(hd: os.Handle, load_at_once := false) -> ([40]byte, bool) {
_create_ripemd_ctx(40) if !load_at_once {
return _hash_impl->hash_file_40(hd, load_at_once) return hash_stream_320(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_320(buf[:]), ok
}
}
return [40]byte{}, false
} }
hash_320 :: proc { hash_320 :: proc {
@@ -206,261 +243,122 @@ hash_320 :: proc {
hash_string_320, hash_string_320,
} }
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte { /*
hash: [16]byte Low level API
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok { */
init_odin(&c)
update_odin(&c, data) init :: proc(ctx: ^$T) {
final_odin(&c, hash[:]) 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
} }
return hash
} }
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) { update :: proc(ctx: ^$T, data: []byte) {
hash: [16]byte ctx.tc += u64(len(data))
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok { data := data
init_odin(&c) if ctx.nx > 0 {
buf := make([]byte, 512) n := len(data)
defer delete(buf)
read := 1 when T == Ripemd128_Context {
for read > 0 { if n > RIPEMD_128_BLOCK_SIZE - ctx.nx {
read, _ = fs->impl_read(buf) n = RIPEMD_128_BLOCK_SIZE - ctx.nx
if read > 0 { }
update_odin(&c, buf[:read]) } 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
} }
} }
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) { for i := 0; i < n; i += 1 {
if !load_at_once { ctx.x[ctx.nx + i] = data[i]
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_16(ctx, buf[:]), ok
} }
}
return [16]byte{}, false
}
hash_bytes_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte { ctx.nx += n
hash: [20]byte when T == Ripemd128_Context {
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok { if ctx.nx == RIPEMD_128_BLOCK_SIZE {
init_odin(&c) block(ctx, ctx.x[0:])
update_odin(&c, data) ctx.nx = 0
final_odin(&c, hash[:]) }
} } else when T == Ripemd160_Context {
return hash if ctx.nx == RIPEMD_160_BLOCK_SIZE {
} block(ctx, ctx.x[0:])
ctx.nx = 0
hash_stream_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) { }
hash: [20]byte } else when T == Ripemd256_Context{
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok { if ctx.nx == RIPEMD_256_BLOCK_SIZE {
init_odin(&c) block(ctx, ctx.x[0:])
buf := make([]byte, 512) ctx.nx = 0
defer delete(buf) }
read := 1 } else when T == Ripemd320_Context{
for read > 0 { if ctx.nx == RIPEMD_320_BLOCK_SIZE {
read, _ = fs->impl_read(buf) block(ctx, ctx.x[0:])
if read > 0 { ctx.nx = 0
update_odin(&c, buf[:read])
} }
} }
final_odin(&c, hash[:]) data = data[n:]
return hash, true }
n := block(ctx, data)
data = data[n:]
if len(data) > 0 {
ctx.nx = copy(ctx.x[:], data)
}
}
final :: proc(ctx: ^$T, hash: []byte) {
d := ctx
tc := d.tc
tmp: [64]byte
tmp[0] = 0x80
if tc % 64 < 56 {
update(d, tmp[0:56 - tc % 64])
} else { } else {
return hash, false update(d, tmp[0:64 + 56 - tc % 64])
} }
tc <<= 3
for i : u32 = 0; i < 8; i += 1 {
tmp[i] = byte(tc >> (8 * i))
}
update(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[:])
} }
hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
if !load_at_once {
return hash_stream_odin_20(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_20(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([40]byte, bool) {
if !load_at_once {
return hash_stream_odin_40(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_40(ctx, buf[:]), 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 RIPEMD implementation
@@ -574,20 +472,6 @@ RIPEMD_160_R1 := [80]uint {
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11, 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 { block :: #force_inline proc (ctx: ^$T, p: []byte) -> int {
when T == Ripemd128_Context { when T == Ripemd128_Context {
return ripemd_128_block(ctx, p) return ripemd_128_block(ctx, p)
@@ -948,101 +832,3 @@ ripemd_320_block :: proc(ctx: ^$T, p: []byte) -> int {
} }
return n 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[:])
}
+84 -187
View File
@@ -6,7 +6,6 @@ package sha1
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://datatracker.ietf.org/doc/html/rfc3174> Implementation of the SHA1 hashing algorithm, as defined in RFC 3174 <https://datatracker.ietf.org/doc/html/rfc3174>
*/ */
@@ -16,52 +15,10 @@ import "core:os"
import "core:io" import "core:io"
import "../util" 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 High level API
*/ */
// hash_string will hash the given input and return the // hash_string will hash the given input and return the
// computed hash // computed hash
hash_string :: proc(data: string) -> [20]byte { hash_string :: proc(data: string) -> [20]byte {
@@ -71,22 +28,44 @@ hash_string :: proc(data: string) -> [20]byte {
// hash_bytes will hash the given input and return the // hash_bytes will hash the given input and return the
// computed hash // computed hash
hash_bytes :: proc(data: []byte) -> [20]byte { hash_bytes :: proc(data: []byte) -> [20]byte {
_create_sha1_ctx() hash: [20]byte
return _hash_impl->hash_bytes_20(data) ctx: Sha1_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream will read the stream in chunks and compute a // hash_stream will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream :: proc(s: io.Stream) -> ([20]byte, bool) { hash_stream :: proc(s: io.Stream) -> ([20]byte, bool) {
_create_sha1_ctx() hash: [20]byte
return _hash_impl->hash_stream_20(s) ctx: Sha1_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file will read the file provided by the given handle // hash_file will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) { hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
_create_sha1_ctx() if !load_at_once {
return _hash_impl->hash_file_20(hd, load_at_once) return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [20]byte{}, false
} }
hash :: proc { hash :: proc {
@@ -100,86 +79,70 @@ hash :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { init :: proc(ctx: ^Sha1_Context) {
_hash_impl->init() 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 :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { update :: proc(ctx: ^Sha1_Context, data: []byte) {
_hash_impl->update(data) 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 :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { final :: proc(ctx: ^Sha1_Context, hash: []byte) {
_hash_impl->final(hash) i := ctx.datalen
}
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte { if ctx.datalen < 56 {
hash: [20]byte ctx.data[i] = 0x80
if c, ok := ctx.internal_ctx.(Sha1_Context); ok { i += 1
init_odin(&c) for i < 56 {
update_odin(&c, data) ctx.data[i] = 0x00
final_odin(&c, hash[:]) i += 1
}
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, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
} }
} }
return [20]byte{}, false 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)
}
@(private) ctx.bitlen += u64(ctx.datalen * 8)
_create_sha1_ctx :: #force_inline proc() { ctx.data[63] = u8(ctx.bitlen)
ctx: Sha1_Context ctx.data[62] = u8(ctx.bitlen >> 8)
_hash_impl.internal_ctx = ctx ctx.data[61] = u8(ctx.bitlen >> 16)
_hash_impl.hash_size = ._20 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[:])
@(private) for j: u32 = 0; j < 4; j += 1 {
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { hash[j] = u8(ctx.state[0] >> (24 - j * 8)) & 0x000000ff
_create_sha1_ctx() hash[j + 4] = u8(ctx.state[1] >> (24 - j * 8)) & 0x000000ff
if c, ok := ctx.internal_ctx.(Sha1_Context); ok { hash[j + 8] = u8(ctx.state[2] >> (24 - j * 8)) & 0x000000ff
init_odin(&c) hash[j + 12] = u8(ctx.state[3] >> (24 - j * 8)) & 0x000000ff
} hash[j + 16] = u8(ctx.state[4] >> (24 - j * 8)) & 0x000000ff
} }
@(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)
}
} }
/* /*
@@ -258,69 +221,3 @@ transform :: proc(ctx: ^Sha1_Context, data: []byte) {
ctx.state[3] += d ctx.state[3] += d
ctx.state[4] += e 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
}
}
+223 -415
View File
@@ -6,7 +6,6 @@ package sha2
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf> Implementation of the SHA2 hashing algorithm, as defined in <https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf>
and in RFC 3874 <https://datatracker.ietf.org/doc/html/rfc3874> and in RFC 3874 <https://datatracker.ietf.org/doc/html/rfc3874>
@@ -17,72 +16,6 @@ import "core:os"
import "core:io" import "core:io"
import "../util" 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 High level API
@@ -97,22 +30,46 @@ hash_string_224 :: proc(data: string) -> [28]byte {
// hash_bytes_224 will hash the given input and return the // hash_bytes_224 will hash the given input and return the
// computed hash // computed hash
hash_bytes_224 :: proc(data: []byte) -> [28]byte { hash_bytes_224 :: proc(data: []byte) -> [28]byte {
_create_sha256_ctx(true) hash: [28]byte
return _hash_impl->hash_bytes_28(data) ctx: Sha256_Context
ctx.is224 = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_224 will read the stream in chunks and compute a // hash_stream_224 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) { hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
_create_sha256_ctx(true) hash: [28]byte
return _hash_impl->hash_stream_28(s) ctx: Sha512_Context
ctx.is384 = false
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_224 will read the file provided by the given handle // hash_file_224 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) { hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
_create_sha256_ctx(true) if !load_at_once {
return _hash_impl->hash_file_28(hd, load_at_once) return hash_stream_224(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_224(buf[:]), ok
}
}
return [28]byte{}, false
} }
hash_224 :: proc { hash_224 :: proc {
@@ -131,22 +88,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
// hash_bytes_256 will hash the given input and return the // hash_bytes_256 will hash the given input and return the
// computed hash // computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte { hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_sha256_ctx(false) hash: [32]byte
return _hash_impl->hash_bytes_32(data) ctx: Sha256_Context
ctx.is224 = false
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_256 will read the stream in chunks and compute a // hash_stream_256 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_sha256_ctx(false) hash: [32]byte
return _hash_impl->hash_stream_32(s) ctx: Sha512_Context
ctx.is384 = false
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_256 will read the file provided by the given handle // hash_file_256 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_sha256_ctx(false) if !load_at_once {
return _hash_impl->hash_file_32(hd, load_at_once) return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [32]byte{}, false
} }
hash_256 :: proc { hash_256 :: proc {
@@ -165,22 +146,46 @@ hash_string_384 :: proc(data: string) -> [48]byte {
// hash_bytes_384 will hash the given input and return the // hash_bytes_384 will hash the given input and return the
// computed hash // computed hash
hash_bytes_384 :: proc(data: []byte) -> [48]byte { hash_bytes_384 :: proc(data: []byte) -> [48]byte {
_create_sha512_ctx(true) hash: [48]byte
return _hash_impl->hash_bytes_48(data) ctx: Sha512_Context
ctx.is384 = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_384 will read the stream in chunks and compute a // hash_stream_384 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) { hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
_create_sha512_ctx(true) hash: [48]byte
return _hash_impl->hash_stream_48(s) ctx: Sha512_Context
ctx.is384 = true
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_384 will read the file provided by the given handle // hash_file_384 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) { hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
_create_sha512_ctx(true) if !load_at_once {
return _hash_impl->hash_file_48(hd, load_at_once) return hash_stream_384(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_384(buf[:]), ok
}
}
return [48]byte{}, false
} }
hash_384 :: proc { hash_384 :: proc {
@@ -199,22 +204,46 @@ hash_string_512 :: proc(data: string) -> [64]byte {
// hash_bytes_512 will hash the given input and return the // hash_bytes_512 will hash the given input and return the
// computed hash // computed hash
hash_bytes_512 :: proc(data: []byte) -> [64]byte { hash_bytes_512 :: proc(data: []byte) -> [64]byte {
_create_sha512_ctx(false) hash: [64]byte
return _hash_impl->hash_bytes_64(data) ctx: Sha512_Context
ctx.is384 = false
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_512 will read the stream in chunks and compute a // hash_stream_512 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) { hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_sha512_ctx(false) hash: [64]byte
return _hash_impl->hash_stream_64(s) ctx: Sha512_Context
ctx.is384 = false
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_512 will read the file provided by the given handle // hash_file_512 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) { hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_sha512_ctx(false) if !load_at_once {
return _hash_impl->hash_file_64(hd, load_at_once) return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [64]byte{}, false
} }
hash_512 :: proc { hash_512 :: proc {
@@ -228,224 +257,120 @@ hash_512 :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { init :: proc(ctx: ^$T) {
_hash_impl->init() when T == Sha256_Context {
} if ctx.is224 {
ctx.h[0] = 0xc1059ed8
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { ctx.h[1] = 0x367cd507
_hash_impl->update(data) ctx.h[2] = 0x3070dd17
} ctx.h[3] = 0xf70e5939
ctx.h[4] = 0xffc00b31
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { ctx.h[5] = 0x68581511
_hash_impl->final(hash) ctx.h[6] = 0x64f98fa7
} ctx.h[7] = 0xbefa4fa4
} else {
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte { ctx.h[0] = 0x6a09e667
hash: [28]byte ctx.h[1] = 0xbb67ae85
if c, ok := ctx.internal_ctx.(Sha256_Context); ok { ctx.h[2] = 0x3c6ef372
init_odin(&c) ctx.h[3] = 0xa54ff53a
update_odin(&c, data) ctx.h[4] = 0x510e527f
final_odin(&c, hash[:]) ctx.h[5] = 0x9b05688c
} ctx.h[6] = 0x1f83d9ab
return hash ctx.h[7] = 0x5be0cd19
}
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[:]) } else when T == Sha512_Context {
return hash, true if ctx.is384 {
} else { ctx.h[0] = 0xcbbb9d5dc1059ed8
return hash, false ctx.h[1] = 0x629a292a367cd507
} ctx.h[2] = 0x9159015a3070dd17
} ctx.h[3] = 0x152fecd8f70e5939
ctx.h[4] = 0x67332667ffc00b31
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) { ctx.h[5] = 0x8eb44a8768581511
if !load_at_once { ctx.h[6] = 0xdb0c2e0d64f98fa7
return hash_stream_odin_28(ctx, os.stream_from_handle(hd)) ctx.h[7] = 0x47b5481dbefa4fa4
} else { } else {
if buf, ok := os.read_entire_file(hd); ok { ctx.h[0] = 0x6a09e667f3bcc908
return hash_bytes_odin_28(ctx, buf[:]), ok 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
} }
} }
return [28]byte{}, false
} }
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { update :: proc(ctx: ^$T, data: []byte) {
hash: [32]byte length := uint(len(data))
if c, ok := ctx.internal_ctx.(Sha256_Context); ok { block_nb: uint
init_odin(&c) new_len, rem_len, tmp_len: uint
update_odin(&c, data) shifted_message := make([]byte, length)
final_odin(&c, hash[:])
when T == Sha256_Context {
CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE
} else when T == Sha512_Context {
CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE
} }
return hash
}
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { tmp_len = CURR_BLOCK_SIZE - ctx.length
hash: [32]byte rem_len = length < tmp_len ? length : tmp_len
if c, ok := ctx.internal_ctx.(Sha256_Context); ok { copy(ctx.block[ctx.length:], data[:rem_len])
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, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { if ctx.length + length < CURR_BLOCK_SIZE {
if !load_at_once { ctx.length += length
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
if !load_at_once {
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_48(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_64(ctx, buf[:]), 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 return
} }
if ctx.hash_size == ._48 || ctx.hash_size == ._64 {
_create_sha512_ctx(ctx.hash_size == ._48) new_len = length - rem_len
if c, ok := ctx.internal_ctx.(Sha512_Context); ok { block_nb = new_len / CURR_BLOCK_SIZE
init_odin(&c) 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 :: 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])}
} }
}
}
@(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)
}
} }
} }
@@ -590,50 +515,6 @@ 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 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) { sha2_transf :: proc(ctx: ^$T, data: []byte, block_nb: uint) {
when T == Sha256_Context { when T == Sha256_Context {
w: [64]u32 w: [64]u32
@@ -710,76 +591,3 @@ sha2_transf :: proc(ctx: ^$T, data: []byte, block_nb: uint) {
} }
} }
} }
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])}
}
}
}
+128 -285
View File
@@ -6,7 +6,6 @@ package sha3
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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. 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. If you wish to compute a Keccak hash, you can use the keccak package, it will use the original padding.
@@ -15,58 +14,8 @@ package sha3
import "core:os" import "core:os"
import "core:io" import "core:io"
import "../botan"
import "../_ctx"
import "../_sha3" 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 High level API
*/ */
@@ -80,22 +29,46 @@ hash_string_224 :: proc(data: string) -> [28]byte {
// hash_bytes_224 will hash the given input and return the // hash_bytes_224 will hash the given input and return the
// computed hash // computed hash
hash_bytes_224 :: proc(data: []byte) -> [28]byte { hash_bytes_224 :: proc(data: []byte) -> [28]byte {
_create_sha3_ctx(28) hash: [28]byte
return _hash_impl->hash_bytes_28(data) ctx: _sha3.Sha3_Context
ctx.mdlen = 28
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
} }
// hash_stream_224 will read the stream in chunks and compute a // hash_stream_224 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) { hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
_create_sha3_ctx(28) hash: [28]byte
return _hash_impl->hash_stream_28(s) ctx: _sha3.Sha3_Context
ctx.mdlen = 28
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
} }
// hash_file_224 will read the file provided by the given handle // hash_file_224 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) { hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
_create_sha3_ctx(28) if !load_at_once {
return _hash_impl->hash_file_28(hd, load_at_once) return hash_stream_224(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_224(buf[:]), ok
}
}
return [28]byte{}, false
} }
hash_224 :: proc { hash_224 :: proc {
@@ -114,22 +87,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
// hash_bytes_256 will hash the given input and return the // hash_bytes_256 will hash the given input and return the
// computed hash // computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte { hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_sha3_ctx(32) hash: [32]byte
return _hash_impl->hash_bytes_32(data) ctx: _sha3.Sha3_Context
ctx.mdlen = 32
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
} }
// hash_stream_256 will read the stream in chunks and compute a // hash_stream_256 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_sha3_ctx(32) hash: [32]byte
return _hash_impl->hash_stream_32(s) ctx: _sha3.Sha3_Context
ctx.mdlen = 32
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
} }
// hash_file_256 will read the file provided by the given handle // hash_file_256 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_sha3_ctx(32) if !load_at_once {
return _hash_impl->hash_file_32(hd, load_at_once) return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [32]byte{}, false
} }
hash_256 :: proc { hash_256 :: proc {
@@ -148,22 +145,46 @@ hash_string_384 :: proc(data: string) -> [48]byte {
// hash_bytes_384 will hash the given input and return the // hash_bytes_384 will hash the given input and return the
// computed hash // computed hash
hash_bytes_384 :: proc(data: []byte) -> [48]byte { hash_bytes_384 :: proc(data: []byte) -> [48]byte {
_create_sha3_ctx(48) hash: [48]byte
return _hash_impl->hash_bytes_48(data) ctx: _sha3.Sha3_Context
ctx.mdlen = 48
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
} }
// hash_stream_384 will read the stream in chunks and compute a // hash_stream_384 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) { hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
_create_sha3_ctx(48) hash: [48]byte
return _hash_impl->hash_stream_48(s) ctx: _sha3.Sha3_Context
ctx.mdlen = 48
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
} }
// hash_file_384 will read the file provided by the given handle // hash_file_384 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) { hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
_create_sha3_ctx(48) if !load_at_once {
return _hash_impl->hash_file_48(hd, load_at_once) return hash_stream_384(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_384(buf[:]), ok
}
}
return [48]byte{}, false
} }
hash_384 :: proc { hash_384 :: proc {
@@ -182,22 +203,46 @@ hash_string_512 :: proc(data: string) -> [64]byte {
// hash_bytes_512 will hash the given input and return the // hash_bytes_512 will hash the given input and return the
// computed hash // computed hash
hash_bytes_512 :: proc(data: []byte) -> [64]byte { hash_bytes_512 :: proc(data: []byte) -> [64]byte {
_create_sha3_ctx(64) hash: [64]byte
return _hash_impl->hash_bytes_64(data) ctx: _sha3.Sha3_Context
ctx.mdlen = 64
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
} }
// hash_stream_512 will read the stream in chunks and compute a // hash_stream_512 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) { hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_sha3_ctx(64) hash: [64]byte
return _hash_impl->hash_stream_64(s) ctx: _sha3.Sha3_Context
ctx.mdlen = 64
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
} }
// hash_file_512 will read the file provided by the given handle // hash_file_512 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) { hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_sha3_ctx(64) if !load_at_once {
return _hash_impl->hash_file_64(hd, load_at_once) return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [64]byte{}, false
} }
hash_512 :: proc { hash_512 :: proc {
@@ -211,218 +256,16 @@ hash_512 :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { Sha3_Context :: _sha3.Sha3_Context
_hash_impl->init()
init :: proc(ctx: ^_sha3.Sha3_Context) {
_sha3.init(ctx)
} }
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
_hash_impl->update(data) _sha3.update(ctx, data)
} }
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
_hash_impl->final(hash) _sha3.final(ctx, 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, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
if !load_at_once {
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_28(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
if !load_at_once {
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_48(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_64(ctx, buf[:]), 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)
}
} }
+73 -186
View File
@@ -6,7 +6,6 @@ package shake
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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. Interface for the SHAKE hashing algorithm.
The SHA3 functionality can be found in package sha3. The SHA3 functionality can be found in package sha3.
@@ -15,52 +14,8 @@ package shake
import "core:os" import "core:os"
import "core:io" import "core:io"
import "../botan"
import "../_ctx"
import "../_sha3" 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 High level API
*/ */
@@ -74,22 +29,48 @@ hash_string_128 :: proc(data: string) -> [16]byte {
// hash_bytes_128 will hash the given input and return the // hash_bytes_128 will hash the given input and return the
// computed hash // computed hash
hash_bytes_128 :: proc(data: []byte) -> [16]byte { hash_bytes_128 :: proc(data: []byte) -> [16]byte {
_create_shake_ctx(16) hash: [16]byte
return _hash_impl->hash_bytes_16(data) ctx: _sha3.Sha3_Context
ctx.mdlen = 16
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.shake_xof(&ctx)
_sha3.shake_out(&ctx, hash[:])
return hash
} }
// hash_stream_128 will read the stream in chunks and compute a // hash_stream_128 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) { hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
_create_shake_ctx(16) hash: [16]byte
return _hash_impl->hash_stream_16(s) ctx: _sha3.Sha3_Context
ctx.mdlen = 16
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.shake_xof(&ctx)
_sha3.shake_out(&ctx, hash[:])
return hash, true
} }
// hash_file_128 will read the file provided by the given handle // hash_file_128 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) { hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
_create_shake_ctx(16) if !load_at_once {
return _hash_impl->hash_file_16(hd, load_at_once) return hash_stream_128(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_128(buf[:]), ok
}
}
return [16]byte{}, false
} }
hash_128 :: proc { hash_128 :: proc {
@@ -108,22 +89,48 @@ hash_string_256 :: proc(data: string) -> [32]byte {
// hash_bytes_256 will hash the given input and return the // hash_bytes_256 will hash the given input and return the
// computed hash // computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte { hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_shake_ctx(32) hash: [32]byte
return _hash_impl->hash_bytes_32(data) ctx: _sha3.Sha3_Context
ctx.mdlen = 32
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.shake_xof(&ctx)
_sha3.shake_out(&ctx, hash[:])
return hash
} }
// hash_stream_256 will read the stream in chunks and compute a // hash_stream_256 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_shake_ctx(32) hash: [32]byte
return _hash_impl->hash_stream_32(s) ctx: _sha3.Sha3_Context
ctx.mdlen = 32
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.shake_xof(&ctx)
_sha3.shake_out(&ctx, hash[:])
return hash, true
} }
// hash_file_256 will read the file provided by the given handle // hash_file_256 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_shake_ctx(32) if !load_at_once {
return _hash_impl->hash_file_32(hd, load_at_once) return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [32]byte{}, false
} }
hash_256 :: proc { hash_256 :: proc {
@@ -137,137 +144,17 @@ hash_256 :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { Sha3_Context :: _sha3.Sha3_Context
_hash_impl->init()
init :: proc(ctx: ^_sha3.Sha3_Context) {
_sha3.init(ctx)
} }
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
_hash_impl->update(data) _sha3.update(ctx, data)
} }
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
_hash_impl->final(hash) _sha3.shake_xof(ctx)
} _sha3.shake_out(ctx, 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, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
if !load_at_once {
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_16(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
@(private)
_create_shake_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_shake_ctx(16)
case ._32: _create_shake_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[:])
}
} }
-487
View File
@@ -1,487 +0,0 @@
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 <https://www.schneier.com/academic/skein/>
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 read the file provided by the given handle
// and compute a hash
hash_skein256_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
_create_skein256_ctx(bit_size)
return _hash_impl->hash_file_slice(hd, 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 read the file provided by the given handle
// and compute a hash
hash_skein512_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
_create_skein512_ctx(bit_size)
return _hash_impl->hash_file_slice(hd, 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 read the file provided by the given handle
// and compute a hash
hash_skein1024_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
_create_skein1024_ctx(bit_size)
return _hash_impl->hash_file_slice(hd, 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, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
if !load_at_once {
return hash_stream_skein256_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_skein256_odin(ctx, buf[:], bit_size, allocator), 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, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
if !load_at_once {
return hash_stream_skein512_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_skein512_odin(ctx, buf[:], bit_size, allocator), 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, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
if !load_at_once {
return hash_stream_skein512_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_skein512_odin(ctx, buf[:], bit_size, allocator), 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) {
}
+78 -184
View File
@@ -6,7 +6,6 @@ package sm3
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02> Implementation of the SM3 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02>
*/ */
@@ -15,51 +14,6 @@ import "core:os"
import "core:io" import "core:io"
import "../util" 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 // hash_string will hash the given input and return the
// computed hash // computed hash
@@ -70,22 +24,44 @@ hash_string :: proc(data: string) -> [32]byte {
// hash_bytes will hash the given input and return the // hash_bytes will hash the given input and return the
// computed hash // computed hash
hash_bytes :: proc(data: []byte) -> [32]byte { hash_bytes :: proc(data: []byte) -> [32]byte {
_create_sm3_ctx() hash: [32]byte
return _hash_impl->hash_bytes_32(data) ctx: Sm3_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream will read the stream in chunks and compute a // hash_stream will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) { hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_sm3_ctx() hash: [32]byte
return _hash_impl->hash_stream_32(s) ctx: Sm3_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file will read the file provided by the given handle // hash_file will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_sm3_ctx() if !load_at_once {
return _hash_impl->hash_file_32(hd, load_at_once) return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [32]byte{}, false
} }
hash :: proc { hash :: proc {
@@ -99,86 +75,64 @@ hash :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { init :: proc(ctx: ^Sm3_Context) {
_hash_impl->init() 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]
} }
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { update :: proc(ctx: ^Sm3_Context, data: []byte) {
_hash_impl->update(data) data := data
} ctx.length += u64(len(data))
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { if ctx.bitlength > 0 {
_hash_impl->final(hash) n := copy(ctx.x[ctx.bitlength:], data[:])
} ctx.bitlength += u64(n)
if ctx.bitlength == 64 {
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { block(ctx, ctx.x[:])
hash: [32]byte ctx.bitlength = 0
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[:]) data = data[n:]
return hash, true }
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 :: proc(ctx: ^Sm3_Context, hash: []byte) {
length := ctx.length
pad: [64]byte
pad[0] = 0x80
if length % 64 < 56 {
update(ctx, pad[0: 56 - length % 64])
} else { } else {
return hash, false update(ctx, pad[0: 64 + 56 - length % 64])
} }
}
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { length <<= 3
if !load_at_once { util.PUT_U64_BE(pad[:], length)
return hash_stream_odin(ctx, os.stream_from_handle(hd)) update(ctx, pad[0: 8])
} else { assert(ctx.bitlength == 0)
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
@(private) util.PUT_U32_BE(hash[0:], ctx.state[0])
_create_sm3_ctx :: #force_inline proc() { util.PUT_U32_BE(hash[4:], ctx.state[1])
ctx: Sm3_Context util.PUT_U32_BE(hash[8:], ctx.state[2])
_hash_impl.internal_ctx = ctx util.PUT_U32_BE(hash[12:], ctx.state[3])
_hash_impl.hash_size = ._32 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])
@(private) util.PUT_U32_BE(hash[28:], ctx.state[7])
_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)
}
} }
/* /*
@@ -200,17 +154,6 @@ IV := [8]u32 {
0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e, 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) { block :: proc "contextless" (ctx: ^Sm3_Context, buf: []byte) {
buf := buf buf := buf
@@ -282,52 +225,3 @@ block :: proc "contextless" (ctx: ^Sm3_Context, buf: []byte) {
ctx.state[0], ctx.state[1], ctx.state[2], ctx.state[3] = state0, state1, state2, state3 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 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])
}
+107 -230
View File
@@ -6,7 +6,6 @@ package streebog
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://datatracker.ietf.org/doc/html/rfc6986> Implementation of the Streebog hashing algorithm, standardized as GOST R 34.11-2012 in RFC 6986 <https://datatracker.ietf.org/doc/html/rfc6986>
*/ */
@@ -15,58 +14,6 @@ import "core:os"
import "core:io" import "core:io"
import "../util" 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 High level API
@@ -81,22 +28,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
// hash_bytes_256 will hash the given input and return the // hash_bytes_256 will hash the given input and return the
// computed hash // computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte { hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_streebog_ctx(true) hash: [32]byte
return _hash_impl->hash_bytes_32(data) ctx: Streebog_Context
ctx.is256 = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_256 will read the stream in chunks and compute a // hash_stream_256 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) { hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_streebog_ctx(true) hash: [32]byte
return _hash_impl->hash_stream_32(s) ctx: Streebog_Context
ctx.is256 = true
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_256 will read the file provided by the given handle // hash_file_256 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_streebog_ctx(true) if !load_at_once {
return _hash_impl->hash_file_32(hd, load_at_once) return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [32]byte{}, false
} }
hash_256 :: proc { hash_256 :: proc {
@@ -115,22 +86,44 @@ hash_string_512 :: proc(data: string) -> [64]byte {
// hash_bytes_512 will hash the given input and return the // hash_bytes_512 will hash the given input and return the
// computed hash // computed hash
hash_bytes_512 :: proc(data: []byte) -> [64]byte { hash_bytes_512 :: proc(data: []byte) -> [64]byte {
_create_streebog_ctx(false) hash: [64]byte
return _hash_impl->hash_bytes_64(data) ctx: Streebog_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream_512 will read the stream in chunks and compute a // hash_stream_512 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) { hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_streebog_ctx(false) hash: [64]byte
return _hash_impl->hash_stream_64(s) ctx: Streebog_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file_512 will read the file provided by the given handle // hash_file_512 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) { hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_streebog_ctx(false) if !load_at_once {
return _hash_impl->hash_file_64(hd, load_at_once) return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [64]byte{}, false
} }
hash_512 :: proc { hash_512 :: proc {
@@ -144,120 +137,64 @@ hash_512 :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { init :: proc(ctx: ^Streebog_Context) {
_hash_impl->init() 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 :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { update :: proc(ctx: ^Streebog_Context, data: []byte) {
_hash_impl->update(data) 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 :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { final :: proc(ctx: ^Streebog_Context, hash: []byte) {
_hash_impl->final(hash) t: [64]byte
} t[1] = byte((ctx.buf_size * 8) >> 8) & 0xff
t[0] = byte((ctx.buf_size) * 8) & 0xff
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { padding(ctx)
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) { G(ctx.h[:], ctx.n[:], ctx.buffer[:])
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, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { add_mod_512(ctx.n[:], t[:], ctx.n[:])
if !load_at_once { add_mod_512(ctx.sigma[:], ctx.buffer[:], ctx.sigma[:])
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte { G(ctx.h[:], ctx.v_0[:], ctx.n[:])
hash: [64]byte G(ctx.h[:], ctx.v_0[:], ctx.sigma[:])
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) { if ctx.is256 {
hash: [64]byte copy(hash[:], ctx.h[32:])
if c, ok := ctx.internal_ctx.(Streebog_Context); ok { } else {
init_odin(&c) copy(hash[:], ctx.h[:])
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, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_64(ctx, buf[:]), 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)
}
} }
/* /*
@@ -534,63 +471,3 @@ padding :: proc(ctx: ^Streebog_Context) {
copy(ctx.buffer[:], t[:]) 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[:])
}
}
+100 -218
View File
@@ -6,7 +6,6 @@ package tiger
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://www.cs.technion.ac.il/~biham/Reports/Tiger/> Interface for the Tiger1 variant of the Tiger hashing algorithm as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
*/ */
@@ -14,55 +13,8 @@ package tiger
import "core:os" import "core:os"
import "core:io" import "core:io"
import "../botan"
import "../_ctx"
import "../_tiger" 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 High level API
*/ */
@@ -76,22 +28,46 @@ hash_string_128 :: proc(data: string) -> [16]byte {
// hash_bytes_128 will hash the given input and return the // hash_bytes_128 will hash the given input and return the
// computed hash // computed hash
hash_bytes_128 :: proc(data: []byte) -> [16]byte { hash_bytes_128 :: proc(data: []byte) -> [16]byte {
_create_tiger_ctx(16) hash: [16]byte
return _hash_impl->hash_bytes_16(data) ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash[:])
return hash
} }
// hash_stream_128 will read the stream in chunks and compute a // hash_stream_128 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) { hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
_create_tiger_ctx(16) hash: [16]byte
return _hash_impl->hash_stream_16(s) ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
}
_tiger.final(&ctx, hash[:])
return hash, true
} }
// hash_file_128 will read the file provided by the given handle // hash_file_128 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) { hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
_create_tiger_ctx(16) if !load_at_once {
return _hash_impl->hash_file_16(hd, load_at_once) return hash_stream_128(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_128(buf[:]), ok
}
}
return [16]byte{}, false
} }
hash_128 :: proc { hash_128 :: proc {
@@ -110,22 +86,46 @@ hash_string_160 :: proc(data: string) -> [20]byte {
// hash_bytes_160 will hash the given input and return the // hash_bytes_160 will hash the given input and return the
// computed hash // computed hash
hash_bytes_160 :: proc(data: []byte) -> [20]byte { hash_bytes_160 :: proc(data: []byte) -> [20]byte {
_create_tiger_ctx(20) hash: [20]byte
return _hash_impl->hash_bytes_20(data) ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash[:])
return hash
} }
// hash_stream_160 will read the stream in chunks and compute a // hash_stream_160 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) { hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
_create_tiger_ctx(20) hash: [20]byte
return _hash_impl->hash_stream_20(s) ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
}
_tiger.final(&ctx, hash[:])
return hash, true
} }
// hash_file_160 will read the file provided by the given handle // hash_file_160 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) { hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
_create_tiger_ctx(20) if !load_at_once {
return _hash_impl->hash_file_20(hd, load_at_once) return hash_stream_160(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_160(buf[:]), ok
}
}
return [20]byte{}, false
} }
hash_160 :: proc { hash_160 :: proc {
@@ -144,22 +144,46 @@ hash_string_192 :: proc(data: string) -> [24]byte {
// hash_bytes_192 will hash the given input and return the // hash_bytes_192 will hash the given input and return the
// computed hash // computed hash
hash_bytes_192 :: proc(data: []byte) -> [24]byte { hash_bytes_192 :: proc(data: []byte) -> [24]byte {
_create_tiger_ctx(24) hash: [24]byte
return _hash_impl->hash_bytes_24(data) ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash[:])
return hash
} }
// hash_stream_192 will read the stream in chunks and compute a // hash_stream_192 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) { hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
_create_tiger_ctx(24) hash: [24]byte
return _hash_impl->hash_stream_24(s) ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
}
_tiger.final(&ctx, hash[:])
return hash, true
} }
// hash_file_192 will read the file provided by the given handle // hash_file_192 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) { hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
_create_tiger_ctx(24) if !load_at_once {
return _hash_impl->hash_file_24(hd, load_at_once) return hash_stream_192(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_192(buf[:]), ok
}
}
return [24]byte{}, false
} }
hash_192 :: proc { hash_192 :: proc {
@@ -169,163 +193,21 @@ hash_192 :: proc {
hash_string_192, hash_string_192,
} }
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte { /*
hash: [16]byte Low level API
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) { Tiger_Context :: _tiger.Tiger_Context
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, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) { init :: proc(ctx: ^_tiger.Tiger_Context) {
if !load_at_once {
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_16(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
if !load_at_once {
return hash_stream_odin_20(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_20(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
if !load_at_once {
return hash_stream_odin_24(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_24(ctx, buf[:]), ok
}
}
return [24]byte{}, false
}
@(private)
_create_tiger_ctx :: #force_inline proc(hash_size: int) {
ctx: _tiger.Tiger_Context
ctx.ver = 1 ctx.ver = 1
_hash_impl.internal_ctx = ctx _tiger.init(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) update :: proc(ctx: ^_tiger.Tiger_Context, data: []byte) {
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { _tiger.update(ctx, data)
#partial switch ctx.hash_size {
case ._16: _create_tiger_ctx(16)
case ._20: _create_tiger_ctx(20)
case ._24: _create_tiger_ctx(24)
}
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
}
} }
@(private) final :: proc(ctx: ^_tiger.Tiger_Context, hash: []byte) {
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { _tiger.final(ctx, hash)
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)
}
} }
+100 -218
View File
@@ -6,7 +6,6 @@ package tiger2
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://www.cs.technion.ac.il/~biham/Reports/Tiger/> Interface for the Tiger2 variant of the Tiger hashing algorithm as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
*/ */
@@ -14,55 +13,8 @@ package tiger2
import "core:os" import "core:os"
import "core:io" import "core:io"
import "../_ctx"
import "../_tiger" 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 High level API
*/ */
@@ -76,22 +28,46 @@ hash_string_128 :: proc(data: string) -> [16]byte {
// hash_bytes_128 will hash the given input and return the // hash_bytes_128 will hash the given input and return the
// computed hash // computed hash
hash_bytes_128 :: proc(data: []byte) -> [16]byte { hash_bytes_128 :: proc(data: []byte) -> [16]byte {
_create_tiger2_ctx(16) hash: [16]byte
return _hash_impl->hash_bytes_16(data) ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash[:])
return hash
} }
// hash_stream_128 will read the stream in chunks and compute a // hash_stream_128 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) { hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
_create_tiger2_ctx(16) hash: [16]byte
return _hash_impl->hash_stream_16(s) ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
}
_tiger.final(&ctx, hash[:])
return hash, true
} }
// hash_file_128 will read the file provided by the given handle // hash_file_128 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) { hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
_create_tiger2_ctx(16) if !load_at_once {
return _hash_impl->hash_file_16(hd, load_at_once) return hash_stream_128(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_128(buf[:]), ok
}
}
return [16]byte{}, false
} }
hash_128 :: proc { hash_128 :: proc {
@@ -110,22 +86,46 @@ hash_string_160 :: proc(data: string) -> [20]byte {
// hash_bytes_160 will hash the given input and return the // hash_bytes_160 will hash the given input and return the
// computed hash // computed hash
hash_bytes_160 :: proc(data: []byte) -> [20]byte { hash_bytes_160 :: proc(data: []byte) -> [20]byte {
_create_tiger2_ctx(20) hash: [20]byte
return _hash_impl->hash_bytes_20(data) ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash[:])
return hash
} }
// hash_stream_160 will read the stream in chunks and compute a // hash_stream_160 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) { hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
_create_tiger2_ctx(20) hash: [20]byte
return _hash_impl->hash_stream_20(s) ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
}
_tiger.final(&ctx, hash[:])
return hash, true
} }
// hash_file_160 will read the file provided by the given handle // hash_file_160 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) { hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
_create_tiger2_ctx(20) if !load_at_once {
return _hash_impl->hash_file_20(hd, load_at_once) return hash_stream_160(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_160(buf[:]), ok
}
}
return [20]byte{}, false
} }
hash_160 :: proc { hash_160 :: proc {
@@ -144,22 +144,46 @@ hash_string_192 :: proc(data: string) -> [24]byte {
// hash_bytes_192 will hash the given input and return the // hash_bytes_192 will hash the given input and return the
// computed hash // computed hash
hash_bytes_192 :: proc(data: []byte) -> [24]byte { hash_bytes_192 :: proc(data: []byte) -> [24]byte {
_create_tiger2_ctx(24) hash: [24]byte
return _hash_impl->hash_bytes_24(data) ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash[:])
return hash
} }
// hash_stream_192 will read the stream in chunks and compute a // hash_stream_192 will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) { hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
_create_tiger2_ctx(24) hash: [24]byte
return _hash_impl->hash_stream_24(s) ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
}
_tiger.final(&ctx, hash[:])
return hash, true
} }
// hash_file_192 will read the file provided by the given handle // hash_file_192 will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) { hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
_create_tiger2_ctx(24) if !load_at_once {
return _hash_impl->hash_file_24(hd, load_at_once) return hash_stream_192(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_192(buf[:]), ok
}
}
return [24]byte{}, false
} }
hash_192 :: proc { hash_192 :: proc {
@@ -169,163 +193,21 @@ hash_192 :: proc {
hash_string_192, hash_string_192,
} }
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte { /*
hash: [16]byte Low level API
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) { Tiger_Context :: _tiger.Tiger_Context
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, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) { init :: proc(ctx: ^_tiger.Tiger_Context) {
if !load_at_once {
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_16(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
if !load_at_once {
return hash_stream_odin_20(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_20(ctx, buf[:]), 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, hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
if !load_at_once {
return hash_stream_odin_24(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_24(ctx, buf[:]), ok
}
}
return [24]byte{}, false
}
@(private)
_create_tiger2_ctx :: #force_inline proc(hash_size: int) {
ctx: _tiger.Tiger_Context
ctx.ver = 2 ctx.ver = 2
_hash_impl.internal_ctx = ctx _tiger.init(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) update :: proc(ctx: ^_tiger.Tiger_Context, data: []byte) {
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { _tiger.update(ctx, data)
#partial switch ctx.hash_size {
case ._16: _create_tiger2_ctx(16)
case ._20: _create_tiger2_ctx(20)
case ._24: _create_tiger2_ctx(24)
}
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
}
} }
@(private) final :: proc(ctx: ^_tiger.Tiger_Context, hash: []byte) {
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { _tiger.final(ctx, hash)
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)
}
} }
+117 -203
View File
@@ -6,7 +6,6 @@ package whirlpool
List of contributors: List of contributors:
zhibog, dotbmp: Initial implementation. 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 <https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html> Implementation of the Whirlpool hashing algorithm, as defined in <https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html>
*/ */
@@ -14,48 +13,8 @@ package whirlpool
import "core:os" import "core:os"
import "core:io" import "core:io"
import "../botan"
import "../_ctx"
import "../util" 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 High level API
*/ */
@@ -69,22 +28,44 @@ hash_string :: proc(data: string) -> [64]byte {
// hash_bytes will hash the given input and return the // hash_bytes will hash the given input and return the
// computed hash // computed hash
hash_bytes :: proc(data: []byte) -> [64]byte { hash_bytes :: proc(data: []byte) -> [64]byte {
_create_whirlpool_ctx() hash: [64]byte
return _hash_impl->hash_bytes_64(data) ctx: Whirlpool_Context
// init(&ctx) No-op
update(&ctx, data)
final(&ctx, hash[:])
return hash
} }
// hash_stream will read the stream in chunks and compute a // hash_stream will read the stream in chunks and compute a
// hash from its contents // hash from its contents
hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) { hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_whirlpool_ctx() hash: [64]byte
return _hash_impl->hash_stream_64(s) ctx: Whirlpool_Context
// init(&ctx) No-op
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
} }
// hash_file will read the file provided by the given handle // hash_file will read the file provided by the given handle
// and compute a hash // and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) { hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_whirlpool_ctx() if !load_at_once {
return _hash_impl->hash_file_64(hd, load_at_once) return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [64]byte{}, false
} }
hash :: proc { hash :: proc {
@@ -98,76 +79,103 @@ hash :: proc {
Low level API Low level API
*/ */
init :: proc(ctx: ^_ctx.Hash_Context) { @(warning="Init is a no-op for Whirlpool")
_hash_impl->init() init :: proc(ctx: ^Whirlpool_Context) {
// No action needed here
} }
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { update :: proc(ctx: ^Whirlpool_Context, source: []byte) {
_hash_impl->update(data) 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 :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { final :: proc(ctx: ^Whirlpool_Context, hash: []byte) {
_hash_impl->final(hash) n := ctx
} n.buffer[n.buffer_pos] |= 0x80 >> (uint(n.buffer_bits) & 7)
n.buffer_pos += 1
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte { if n.buffer_pos > 64 - 32 {
hash: [64]byte if n.buffer_pos < 64 {
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok { for i := 0; i < 64 - n.buffer_pos; i += 1 {
update_odin(&c, data) n.buffer[n.buffer_pos + i] = 0
final_odin(&c, hash[:]) }
} }
return hash transform(ctx)
} n.buffer_pos = 0
}
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) { if n.buffer_pos < 64 - 32 {
hash: [64]byte for i := 0; i < (64 - 32) - n.buffer_pos; i += 1 {
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok { n.buffer[n.buffer_pos + i] = 0
buf := make([]byte, 512) }
defer delete(buf) }
read := 1 n.buffer_pos = 64 - 32
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, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) { for i := 0; i < 32; i += 1 {
if !load_at_once { n.buffer[n.buffer_pos + i] = n.bitlength[i]
return hash_stream_odin(ctx, os.stream_from_handle(hd)) }
} else { transform(ctx)
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
}
}
return [64]byte{}, false
}
@(private) for i := 0; i < 8; i += 1 {
_create_whirlpool_ctx :: #force_inline proc() { hash[i * 8] = byte(n.hash[i] >> 56)
ctx: Whirlpool_Context hash[i * 8 + 1] = byte(n.hash[i] >> 48)
_hash_impl.internal_ctx = ctx hash[i * 8 + 2] = byte(n.hash[i] >> 40)
_hash_impl.hash_size = ._64 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)
@(private) hash[i * 8 + 6] = byte(n.hash[i] >> 8)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { hash[i * 8 + 7] = byte(n.hash[i])
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)
}
} }
/* /*
@@ -774,97 +782,3 @@ transform :: proc (ctx: ^Whirlpool_Context) {
} }
for i := 0; i < 8; i += 1 {ctx.hash[i] ~= state[i] ~ block[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])
}
}
-1
View File
@@ -1,7 +1,6 @@
package os2 package os2
import "core:mem" import "core:mem"
import "core:io"
import "core:strconv" import "core:strconv"
import "core:unicode/utf8" import "core:unicode/utf8"
-1
View File
@@ -1,7 +1,6 @@
//+private //+private
package os2 package os2
import "core:runtime"
import "core:mem" import "core:mem"
import win32 "core:sys/windows" import win32 "core:sys/windows"
Binary file not shown.
-183
View File
@@ -33,7 +33,6 @@ import "core:crypto/tiger2"
import "core:crypto/gost" import "core:crypto/gost"
import "core:crypto/streebog" import "core:crypto/streebog"
import "core:crypto/sm3" import "core:crypto/sm3"
import "core:crypto/skein"
import "core:crypto/jh" import "core:crypto/jh"
import "core:crypto/groestl" import "core:crypto/groestl"
import "core:crypto/haval" import "core:crypto/haval"
@@ -102,7 +101,6 @@ main :: proc() {
test_tiger2_160(&t) test_tiger2_160(&t)
test_tiger2_192(&t) test_tiger2_192(&t)
test_sm3(&t) test_sm3(&t)
test_skein512(&t)
test_jh_224(&t) test_jh_224(&t)
test_jh_256(&t) test_jh_256(&t)
test_jh_384(&t) test_jh_384(&t)
@@ -171,13 +169,6 @@ test_md4 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -197,12 +188,6 @@ test_md5 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -225,15 +210,8 @@ test_sha1 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
test_sha224 :: proc(t: ^testing.T) { test_sha224 :: proc(t: ^testing.T) {
// Test vectors from // Test vectors from
@@ -250,12 +228,6 @@ test_sha224 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -274,13 +246,6 @@ test_sha256 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -299,13 +264,6 @@ test_sha384 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -324,15 +282,8 @@ test_sha512 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
test_sha3_224 :: proc(t: ^testing.T) { test_sha3_224 :: proc(t: ^testing.T) {
// Test vectors from // Test vectors from
@@ -353,12 +304,6 @@ test_sha3_224 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -381,12 +326,6 @@ test_sha3_256 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -409,12 +348,6 @@ test_sha3_384 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -437,12 +370,6 @@ test_sha3_512 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -457,12 +384,6 @@ test_shake_128 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -477,12 +398,6 @@ test_shake_256 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -499,12 +414,6 @@ test_keccak_224 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -521,12 +430,6 @@ test_keccak_256 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -543,12 +446,6 @@ test_keccak_384 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -565,12 +462,6 @@ test_keccak_512 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -597,12 +488,6 @@ test_whirlpool :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -623,12 +508,6 @@ test_gost :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -643,12 +522,6 @@ test_streebog_256 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -663,12 +536,6 @@ test_streebog_512 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -738,12 +605,6 @@ test_blake2b :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -797,12 +658,6 @@ test_ripemd_160 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -863,12 +718,6 @@ test_tiger_128 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -889,12 +738,6 @@ test_tiger_160 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -915,12 +758,6 @@ test_tiger_192 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)
@@ -979,26 +816,6 @@ test_sm3 :: proc(t: ^testing.T) {
computed_str := hex_string(computed[:]) 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)) 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)