Merge branch 'master' into target-js_wasm32

This commit is contained in:
gingerBill
2021-11-09 18:06:19 +00:00
60 changed files with 3685 additions and 7573 deletions
+42 -5
View File
@@ -69,13 +69,50 @@ set linker_settings=%libs% %linker_flags%
del *.pdb > NUL 2> NUL del *.pdb > NUL 2> NUL
del *.ilk > NUL 2> NUL del *.ilk > NUL 2> NUL
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name% rem cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
if %errorlevel% neq 0 goto end_of_build rem if %errorlevel% neq 0 goto end_of_build
call build_vendor.bat odin run examples/bug
if %errorlevel% neq 0 goto end_of_build rem odin build examples/demo -use-separate-modules
rem odin run examples/demo -o:minimal
rem odin run examples/demo -o:size
rem odin run examples/demo -o:speed -keep-temp-files
rem odin run examples/demo -o:speed -keep-temp-files
if %release_mode% EQU 0 odin run examples/demo rem set small_hellope_flags=-strict-style -no-bounds-check -default-to-nil-allocator -disable-assert -no-crt -o:size
rem set small_hellope_flags=-strict-style -no-bounds-check -default-to-nil-allocator -disable-assert -o:size
rem odin build examples/small_hellope %small_hellope_flags% -build-mode:asm
rem odin run examples/small_hellope %small_hellope_flags% -keep-temp-files
rem FOR /F "usebackq" %%A IN ('small_hellope.exe') DO echo %%~zA
rem cl /nologo examples\small_hellope\small_hellope.c /O1 /link Kernel32.lib -out:small_hellope_c.exe /NODEFAULTLIB /entry:mainCRTStartup /subsystem:console
rem small_hellope_c.exe
rem FOR /F "usebackq" %%A IN ('small_hellope_c.exe') DO echo %%~zA
rem odin run examples/demo
rem odin run examples/sdl2 -vet
rem odin check examples/all -vet
rem odin run examples/bug
rem odin check examples/wasm -strict-style -target:wasi_wasm32
rem odin check examples/demo -strict-style
rem odin build examples/wasm -strict-style -target:wasi_wasm32 -keep-temp-files
rem "C:\Program Files\WAVM\bin\wavm.exe" disassemble wasm.wasm > wasm.wast
rem "C:\Program Files\WAVM\bin\wavm.exe" run --abi=wasi --function=weird_add wasm.wasm 2 3
rem wasmer inspect wasm.wasm
rem wasmer run wasm.wasm
rem odin run examples/demo -strict-style
rem call build_vendor.bat
rem if %errorlevel% neq 0 goto end_of_build
rem if %release_mode% EQU 0 odin check examples/bug -strict-style
rem odin run examples/demo -strict-style
del *.obj > NUL 2> NUL del *.obj > NUL 2> NUL
+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))
+276 -473
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
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
hash: [28]byte
if c, ok := ctx.internal_ctx.(Blake256_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
hash: [28]byte
if c, ok := ctx.internal_ctx.(Blake256_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else { } else {
return hash, false 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 {
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) { ctx.h[0] = 0xcbbb9d5dc1059ed8
if !load_at_once { ctx.h[1] = 0x629a292a367cd507
return hash_stream_odin_28(ctx, os.stream_from_handle(hd)) 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 { } 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 "contextless" (ctx: ^$T, data: []byte) {
hash: [32]byte data := data
if c, ok := ctx.internal_ctx.(Blake256_Context); ok { when T == Blake256_Context {
init_odin(&c) if ctx.nx > 0 {
update_odin(&c, data) n := copy(ctx.x[ctx.nx:], data)
final_odin(&c, hash[:]) 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)
}
} }
return hash
} }
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { final :: proc "contextless" (ctx: ^$T, hash: []byte) {
hash: [32]byte when T == Blake256_Context {
if c, ok := ctx.internal_ctx.(Blake256_Context); ok { tmp: [65]byte
init_odin(&c) } else when T == Blake512_Context {
buf := make([]byte, 512) tmp: [129]byte
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
} }
} nx := u64(ctx.nx)
final_odin(&c, hash[:]) tmp[0] = 0x80
return hash, true length := (ctx.t + nx) << 3
when T == Blake256_Context {
if nx == 55 {
if ctx.is224 {
write_additional(ctx, {0x80})
} else { } else {
return hash, false write_additional(ctx, {0x81})
} }
}
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 { } else {
if buf, ok := os.read_entire_file(hd); ok { if nx < 55 {
return hash_bytes_odin_32(ctx, buf[:]), ok if nx == 0 {
ctx.nullt = true
} }
} write_additional(ctx, tmp[0 : 55 - nx])
return [32]byte{}, false
}
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
hash: [48]byte
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
hash: [48]byte
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else { } else {
return hash, false write_additional(ctx, tmp[0 : 64 - nx])
write_additional(ctx, tmp[1:56])
ctx.nullt = true
} }
} if ctx.is224 {
write_additional(ctx, {0x00})
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 { } else {
if buf, ok := os.read_entire_file(hd); ok { write_additional(ctx, {0x01})
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 { for i : uint = 0; i < 8; i += 1 {
hash: [64]byte tmp[i] = byte(length >> (56 - 8 * i))
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
} }
return hash write_additional(ctx, tmp[0:8])
}
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) { h := ctx.h[:]
hash: [64]byte if ctx.is224 {
if c, ok := ctx.internal_ctx.(Blake512_Context); ok { h = h[0:7]
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])
} }
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)
} }
final_odin(&c, hash[:]) } else when T == Blake512_Context {
return hash, true if nx == 111 {
if ctx.is384 {
write_additional(ctx, {0x80})
} else { } else {
return hash, false write_additional(ctx, {0x81})
} }
}
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 { } else {
if buf, ok := os.read_entire_file(hd); ok { if nx < 111 {
return hash_bytes_odin_64(ctx, buf[:]), ok 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})
} }
} }
return [64]byte{}, false
}
@(private) for i : uint = 0; i < 16; i += 1 {
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { tmp[i] = byte(length >> (120 - 8 * i))
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 write_additional(ctx, tmp[0:16])
h := ctx.h[:]
if ctx.is384 {
h = h[0:6]
} }
if ctx.hash_size == ._48 || ctx.hash_size == ._64 { for s, i in h {
_create_blake512_ctx(ctx.hash_size == ._48, ctx.hash_size) hash[i * 8] = byte(s >> 56)
if c, ok := ctx.internal_ctx.(Blake512_Context); ok { hash[i * 8 + 1] = byte(s >> 48)
init_odin(&c) 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)
} }
} }
} }
@(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,7 +527,7 @@ 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
@@ -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
}
+112 -209
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) { update :: proc(ctx: ^Gost_Context, data: []byte) {
if !load_at_once { length := byte(len(data))
return hash_stream_odin(ctx, os.stream_from_handle(hd)) j: byte
} else {
if buf, ok := os.read_entire_file(hd); ok { i := ctx.partial_bytes
return hash_bytes_odin(ctx, buf[:]), ok for i < 32 && j < length {
ctx.partial[i] = data[j]
i, j = i + 1, j + 1
} }
if i < 32 {
ctx.partial_bytes = i
return
} }
return [32]byte{}, false 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) {
_create_gost_ctx :: #force_inline proc() { if ctx.partial_bytes > 0 {
ctx: Gost_Context mem.set(&ctx.partial[ctx.partial_bytes], 0, 32 - int(ctx.partial_bytes))
_hash_impl.internal_ctx = ctx bytes(ctx, ctx.partial[:], u32(ctx.partial_bytes) << 3)
_hash_impl.hash_size = ._32
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_gost_ctx()
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
init_odin(&c)
} }
}
@(private) compress(ctx.hash[:], ctx.len[:])
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { compress(ctx.hash[:], ctx.sum[:])
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
update_odin(&c, data)
}
}
@(private) for i, j := 0, 0; i < 8; i, j = i + 1, j + 4 {
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { hash[j] = byte(ctx.hash[i])
if c, ok := ctx.internal_ctx.(Gost_Context); ok { hash[j + 1] = byte(ctx.hash[i] >> 8)
final_odin(&c, hash) 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)
}
}
+200 -366
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
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = size
#partial switch size {
case ._28: ctx.hashbitlen = 224
case ._32: ctx.hashbitlen = 256
case ._48: ctx.hashbitlen = 384
case ._64: ctx.hashbitlen = 512
}
}
/* /*
High level API 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
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
hash: [28]byte
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
hash: [28]byte
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else { } else {
return hash, false 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))))
} }
} }
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) { update :: proc(ctx: ^Groestl_Context, data: []byte) {
if !load_at_once { databitlen := len(data) * 8
return hash_stream_odin_28(ctx, os.stream_from_handle(hd)) 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 :: 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 { } 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
} }
}
return [28]byte{}, false
}
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { if ctx.buf_ptr > ctx.statesize - 8 {
hash: [32]byte for ctx.buf_ptr < ctx.statesize {
if c, ok := ctx.internal_ctx.(Groestl_Context); ok { ctx.buffer[ctx.buf_ptr] = 0
init_odin(&c) ctx.buf_ptr += 1
update_odin(&c, data) }
final_odin(&c, hash[:]) transform(ctx, ctx.buffer[:], u32(ctx.statesize))
ctx.buf_ptr = 0
} }
return hash
}
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { 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)
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) { ctx.block_counter += 1
if !load_at_once { ctx.buf_ptr = ctx.statesize
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 ctx.buf_ptr > ctx.statesize - 8 {
hash: [48]byte ctx.buf_ptr -= 1
if c, ok := ctx.internal_ctx.(Groestl_Context); ok { ctx.buffer[ctx.buf_ptr] = byte(ctx.block_counter)
init_odin(&c) ctx.block_counter >>= 8
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) { transform(ctx, ctx.buffer[:], u32(ctx.statesize))
hash: [48]byte output_transformation(ctx)
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) { for i, j := ctx.statesize - hashbytelen , 0; i < ctx.statesize; i, j = i + 1, j + 1 {
if !load_at_once { hash[j] = ctx.chaining[i % 8][i / 8]
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
+209 -375
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
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = size
#partial switch size {
case ._28: ctx.hashbitlen = 224
case ._32: ctx.hashbitlen = 256
case ._48: ctx.hashbitlen = 384
case ._64: ctx.hashbitlen = 512
}
}
/* /*
High level API 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)])
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
hash: [28]byte
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
hash: [28]byte
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else { } else {
return hash, false copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3) + 1])
}
ctx.buffer_size += databitlen
databitlen = 0
} }
}
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) { if (ctx.buffer_size > 0 ) && ((ctx.buffer_size + databitlen) >= 512) {
if !load_at_once { copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
return hash_stream_odin_28(ctx, os.stream_from_handle(hd)) i = 64 - (ctx.buffer_size >> 3)
databitlen = databitlen - (512 - ctx.buffer_size)
F8(ctx)
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 { } else {
if buf, ok := os.read_entire_file(hd); ok { copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3) + 1])
return hash_bytes_odin_28(ctx, buf[:]), ok
} }
ctx.buffer_size = databitlen
} }
return [28]byte{}, false
} }
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte { final :: proc(ctx: ^Jh_Context, hash: []byte) {
hash: [32]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
update_odin(&c, data)
final_odin(&c, hash[:])
} }
return hash ctx.buffer[0] = 0x80
} ctx.buffer[63] = byte(ctx.databitlen) & 0xff
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
hash: [32]byte ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
if c, ok := ctx.internal_ctx.(Jh_Context); ok { ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
init_odin(&c) ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
buf := make([]byte, 512) ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
defer delete(buf) ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
read := 1 F8(ctx)
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else { } else {
return hash, false if ctx.buffer_size & 7 == 0 {
for i := (ctx.databitlen & 0x1ff) >> 3; i < 64; i += 1 {
ctx.buffer[i] = 0
} }
}
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 { } else {
if buf, ok := os.read_entire_file(hd); ok { for i := ((ctx.databitlen & 0x1ff) >> 3) + 1; i < 64; i += 1 {
return hash_bytes_odin_32(ctx, buf[:]), ok ctx.buffer[i] = 0
} }
} }
return [32]byte{}, false ctx.buffer[(ctx.databitlen & 0x1ff) >> 3] |= 1 << (7 - (ctx.databitlen & 7))
} F8(ctx)
for i := 0; i < 64; i += 1 {
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte { ctx.buffer[i] = 0
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 ctx.buffer[63] = byte(ctx.databitlen) & 0xff
} ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) { ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
hash: [48]byte ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
if c, ok := ctx.internal_ctx.(Jh_Context); ok { ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
init_odin(&c) ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
buf := make([]byte, 512) ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
defer delete(buf) F8(ctx)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
} }
} switch ctx.hashbitlen {
final_odin(&c, hash[:]) case 224: copy(hash[:], ctx.H[100:128])
return hash, true case 256: copy(hash[:], ctx.H[96:128])
} else { case 384: copy(hash[:], ctx.H[80:128])
return hash, false case 512: copy(hash[:], ctx.H[64:128])
}
}
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])
}
}
+135 -283
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()
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { init :: proc(ctx: ^_sha3.Sha3_Context) {
_hash_impl->update(data)
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
hash: [28]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
_sha3.update_odin(&c, data)
_sha3.final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
hash: [28]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_sha3.update_odin(&c, buf[:read])
}
}
_sha3.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, 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 ctx.is_keccak = true
_hash_impl.internal_ctx = ctx _sha3.init(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) update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) { _sha3.update(ctx, data)
#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) final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { _sha3.final(ctx, hash)
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)
}
} }
+47 -149
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
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { if (ctx.datalen == 16) {
_hash_impl->final(hash) transform(ctx, ctx.data[:])
} 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.(Md2_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
hash: [16]byte
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
} }
} }
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
} }
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) { final :: proc(ctx: ^Md2_Context, hash: []byte) {
if !load_at_once { to_pad := byte(16 - ctx.datalen)
return hash_stream_odin(ctx, os.stream_from_handle(hd)) for ctx.datalen < 16 {
} else { ctx.data[ctx.datalen] = to_pad
if buf, ok := os.read_entire_file(hd); ok { ctx.datalen += 1
return hash_bytes_odin(ctx, buf[:]), ok
} }
} transform(ctx, ctx.data[:])
return [16]byte{}, false transform(ctx, ctx.checksum[:])
} for i := 0; i < 16; i += 1 {
hash[i] = ctx.state[i]
@(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]
}
}
+72 -173
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) { final :: proc(ctx: ^Md4_Context, hash: []byte) {
if !load_at_once { i := ctx.datalen
return hash_stream_odin(ctx, os.stream_from_handle(hd)) if ctx.datalen < 56 {
} else { ctx.data[i] = 0x80
if buf, ok := os.read_entire_file(hd); ok { i += 1
return hash_bytes_odin(ctx, buf[:]), ok 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
} }
return [16]byte{}, false transform(ctx, ctx.data[:])
} mem.set(&ctx.data, 0, 56)
@(private)
_create_md4_ctx :: #force_inline proc() {
ctx: Md4_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._16
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_md4_ctx()
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
init_odin(&c)
} }
}
@(private) 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) { final :: proc(ctx: ^Md5_Context, hash: []byte){
if !load_at_once { i : u32
return hash_stream_odin(ctx, os.stream_from_handle(hd)) i = ctx.datalen
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
}
}
return [16]byte{}, false
}
@(private) if ctx.datalen < 56 {
_create_md5_ctx :: #force_inline proc() { ctx.data[i] = 0x80
ctx: Md5_Context i += 1
_hash_impl.internal_ctx = ctx for i < 56 {
_hash_impl.hash_size = ._16 ctx.data[i] = 0x00
} i += 1
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_md5_ctx()
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
init_odin(&c)
} }
} } else if ctx.datalen >= 56 {
ctx.data[i] = 0x80
@(private) i += 1
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { for i < BLOCK_SIZE {
if c, ok := ctx.internal_ctx.(Md5_Context); ok { ctx.data[i] = 0x00
update_odin(&c, data) i += 1
}
transform(ctx, ctx.data[:])
mem.set(&ctx.data, 0, 56)
} }
}
@(private) ctx.bitlen += u64(ctx.datalen * 8)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { ctx.data[56] = byte(ctx.bitlen)
if c, ok := ctx.internal_ctx.(Md5_Context); ok { ctx.data[57] = byte(ctx.bitlen >> 8)
final_odin(&c, hash) 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
} }
} }
@@ -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
}
}
+219 -433
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 for i := 0; i < n; i += 1 {
ctx.x[ctx.nx + i] = data[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
}
}
data = data[n:]
}
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_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_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
hash: [20]byte
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) {
hash: [20]byte
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, 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[:])
}
+80 -183
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,85 +79,69 @@ 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
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) -> [20]byte {
hash: [20]byte
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) {
hash: [20]byte
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
} }
} }
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
} }
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) { final :: proc(ctx: ^Sha1_Context, hash: []byte) {
if !load_at_once { i := ctx.datalen
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else { if ctx.datalen < 56 {
if buf, ok := os.read_entire_file(hd); ok { ctx.data[i] = 0x80
return hash_bytes_odin(ctx, buf[:]), ok i += 1
for i < 56 {
ctx.data[i] = 0x00
i += 1
} }
} }
return [20]byte{}, false else {
} ctx.data[i] = 0x80
i += 1
@(private) for i < BLOCK_SIZE {
_create_sha1_ctx :: #force_inline proc() { ctx.data[i] = 0x00
ctx: Sha1_Context i += 1
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._20
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_sha1_ctx()
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
init_odin(&c)
} }
} transform(ctx, ctx.data[:])
mem.set(&ctx.data, 0, 56)
@(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) ctx.bitlen += u64(ctx.datalen * 8)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { ctx.data[63] = u8(ctx.bitlen)
if c, ok := ctx.internal_ctx.(Sha1_Context); ok { ctx.data[62] = u8(ctx.bitlen >> 8)
final_odin(&c, hash) 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
} }
} }
@@ -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
}
}
+216 -408
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,223 +257,119 @@ 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
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
hash: [28]byte
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
hash: [28]byte
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else { } else {
return hash, false 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 {
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) { ctx.h[0] = 0xcbbb9d5dc1059ed8
if !load_at_once { ctx.h[1] = 0x629a292a367cd507
return hash_stream_odin_28(ctx, os.stream_from_handle(hd)) 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 { } 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 {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
} }
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { update :: proc(ctx: ^$T, data: []byte) {
if !load_at_once { length := uint(len(data))
return hash_stream_odin_32(ctx, os.stream_from_handle(hd)) block_nb: uint
} else { new_len, rem_len, tmp_len: uint
if buf, ok := os.read_entire_file(hd); ok { shifted_message := make([]byte, length)
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 { when T == Sha256_Context {
hash: [48]byte CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE
if c, ok := ctx.internal_ctx.(Sha512_Context); ok { } else when T == Sha512_Context {
init_odin(&c) CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE
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) { tmp_len = CURR_BLOCK_SIZE - ctx.length
hash: [48]byte rem_len = length < tmp_len ? length : tmp_len
if c, ok := ctx.internal_ctx.(Sha512_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_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) { if ctx.length + length < CURR_BLOCK_SIZE {
if !load_at_once { ctx.length += length
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}
} }
@(private) final :: proc(ctx: ^$T, hash: []byte) {
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) { block_nb, pm_len, len_b: u32
#partial switch ctx.hash_size { i: i32
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) when T == Sha256_Context {CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE}
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) { else when T == Sha512_Context {CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE}
#partial switch ctx.hash_size {
case ._28, ._32: when T == Sha256_Context {block_nb = 1 + ((CURR_BLOCK_SIZE - 9) < (ctx.length % CURR_BLOCK_SIZE) ? 1 : 0)}
if c, ok := ctx.internal_ctx.(Sha256_Context); ok { else when T == Sha512_Context {block_nb = 1 + ((CURR_BLOCK_SIZE - 17) < (ctx.length % CURR_BLOCK_SIZE) ? 1 : 0)}
final_odin(&c, hash)
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])}
} }
case ._48, ._64: } else when T == Sha512_Context {
if c, ok := ctx.internal_ctx.(Sha512_Context); ok { if ctx.is384 {
final_odin(&c, hash) 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])}
} }
} }
} }
@@ -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) {
}
+76 -182
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) data = data[n:]
update_odin(&c, data) }
final_odin(&c, hash[:]) 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[:]))
} }
return hash
} }
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) { final :: proc(ctx: ^Sm3_Context, hash: []byte) {
hash: [32]byte length := ctx.length
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
init_odin(&c) pad: [64]byte
buf := make([]byte, 512) pad[0] = 0x80
defer delete(buf) if length % 64 < 56 {
read := 1 update(ctx, pad[0: 56 - length % 64])
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} 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])
}
+109 -232
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,119 +137,63 @@ 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 {
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) { ctx.h[i] = 0x01
_hash_impl->update(data)
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
} }
return hash
}
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else { } else {
return hash, false ctx.hash_size = 512
}
ctx.v_512[1] = 0x02
}
update :: 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
}
} }
} }
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) { final :: proc(ctx: ^Streebog_Context, hash: []byte) {
if !load_at_once { t: [64]byte
return hash_stream_odin_32(ctx, os.stream_from_handle(hd)) 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 { } else {
if buf, ok := os.read_entire_file(hd); ok { copy(hash[:], ctx.h[:])
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 {
hash: [64]byte
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
hash: [64]byte
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, 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)
}
} }
+109 -195
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,75 +79,102 @@ 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
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) { for i, carry, value := 31, u32(0), u32(source_bits); i >= 0 && (carry != 0 || value != 0); i -= 1 {
_hash_impl->final(hash) carry += u32(ctx.bitlength[i]) + (u32(value & 0xff))
} ctx.bitlength[i] = byte(carry)
carry >>= 8
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte { value >>= 8
hash: [64]byte
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
update_odin(&c, data)
final_odin(&c, hash[:])
} }
return hash
}
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) { for source_bits > 8 {
hash: [64]byte b = u32(u32((source[source_pos] << source_gap) & 0xff) | u32((source[source_pos+1] & 0xff) >> (8 - source_gap)))
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
buf := make([]byte, 512) ctx.buffer[ctx.buffer_pos] |= u8(b >> buffer_rem)
defer delete(buf) ctx.buffer_pos += 1
read := 1 ctx.buffer_bits += int(8 - buffer_rem)
for read > 0 {
read, _ = fs->impl_read(buf) if ctx.buffer_bits == 512 {
if read > 0 { transform(ctx)
update_odin(&c, buf[:read]) 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
} }
final_odin(&c, hash[:])
return hash, true 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 { } else {
return hash, false 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)
} }
} }
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) { final :: proc(ctx: ^Whirlpool_Context, hash: []byte) {
if !load_at_once { n := ctx
return hash_stream_odin(ctx, os.stream_from_handle(hd)) n.buffer[n.buffer_pos] |= 0x80 >> (uint(n.buffer_bits) & 7)
} else { n.buffer_pos += 1
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok 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
} }
} }
return [64]byte{}, false transform(ctx)
} n.buffer_pos = 0
@(private)
_create_whirlpool_ctx :: #force_inline proc() {
ctx: Whirlpool_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._64
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
update_odin(&c, data)
} }
}
@(private) if n.buffer_pos < 64 - 32 {
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []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
final_odin(&c, hash) }
}
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])
} }
} }
@@ -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])
}
}
+8 -8
View File
@@ -1929,12 +1929,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
if fi.hash { if fi.hash {
// Printed as it is written // Printed as it is written
io.write_byte(fi.writer, '\n') io.write_byte(fi.writer, '\n')
for col in 0..<info.column_count {
fmt_write_indent(fi)
for row in 0..<info.row_count { for row in 0..<info.row_count {
if row > 0 { io.write_string(fi.writer, ", ") } fmt_write_indent(fi)
for col in 0..<info.column_count {
if col > 0 { io.write_string(fi.writer, ", ") }
offset := (col + row*info.elem_stride)*info.elem_size offset := (row + col*info.elem_stride)*info.elem_size
data := uintptr(v.data) + uintptr(offset) data := uintptr(v.data) + uintptr(offset)
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
@@ -1943,12 +1943,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
} }
} else { } else {
// Printed in Row-Major layout to match text layout // Printed in Row-Major layout to match text layout
for col in 0..<info.column_count {
if col > 0 { io.write_string(fi.writer, "; ") }
for row in 0..<info.row_count { for row in 0..<info.row_count {
if row > 0 { io.write_string(fi.writer, ", ") } if row > 0 { io.write_string(fi.writer, "; ") }
for col in 0..<info.column_count {
if col > 0 { io.write_string(fi.writer, ", ") }
offset := (col + row*info.elem_stride)*info.elem_size offset := (row + col*info.elem_stride)*info.elem_size
data := uintptr(v.data) + uintptr(offset) data := uintptr(v.data) + uintptr(offset)
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
-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"
+17 -6
View File
@@ -18,6 +18,7 @@ enum TargetOsKind {
TargetOs_freebsd, TargetOs_freebsd,
TargetOs_wasi, TargetOs_wasi,
TargetOs_js,
TargetOs_freestanding, TargetOs_freestanding,
@@ -54,6 +55,7 @@ String target_os_names[TargetOs_COUNT] = {
str_lit("freebsd"), str_lit("freebsd"),
str_lit("wasi"), str_lit("wasi"),
str_lit("js"),
str_lit("freestanding"), str_lit("freestanding"),
}; };
@@ -344,12 +346,12 @@ gb_global TargetMetrics target_freestanding_wasm32 = {
str_lit(""), str_lit(""),
}; };
gb_global TargetMetrics target_freestanding_wasm64 = { gb_global TargetMetrics target_js_wasm32 = {
TargetOs_freestanding, TargetOs_js,
TargetArch_wasm64, TargetArch_wasm32,
4,
8, 8,
16, str_lit("wasm32-js-js"),
str_lit("wasm64-freestanding-js"),
str_lit(""), str_lit(""),
}; };
@@ -363,6 +365,14 @@ gb_global TargetMetrics target_wasi_wasm32 = {
}; };
// gb_global TargetMetrics target_freestanding_wasm64 = {
// TargetOs_freestanding,
// TargetArch_wasm64,
// 8,
// 16,
// str_lit("wasm64-freestanding-js"),
// str_lit(""),
// };
@@ -382,8 +392,9 @@ gb_global NamedTargetMetrics named_targets[] = {
{ str_lit("freebsd_386"), &target_freebsd_386 }, { str_lit("freebsd_386"), &target_freebsd_386 },
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 }, { str_lit("freebsd_amd64"), &target_freebsd_amd64 },
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 }, { str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
// { str_lit("freestanding_wasm64"), &target_freestanding_wasm64 },
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 }, { str_lit("wasi_wasm32"), &target_wasi_wasm32 },
{ str_lit("js_wasm32"), &target_js_wasm32 },
// { str_lit("freestanding_wasm64"), &target_freestanding_wasm64 },
}; };
NamedTargetMetrics *selected_target_metrics; NamedTargetMetrics *selected_target_metrics;
+4
View File
@@ -473,6 +473,10 @@ LLVMTypeRef lb_type_padding_filler(lbModule *m, i64 padding, i64 padding_align);
LLVMValueRef llvm_basic_shuffle(lbProcedure *p, LLVMValueRef vector, LLVMValueRef mask); LLVMValueRef llvm_basic_shuffle(lbProcedure *p, LLVMValueRef vector, LLVMValueRef mask);
void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" #define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info" #define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
#define LB_TYPE_INFO_DATA_NAME "__$type_info_data" #define LB_TYPE_INFO_DATA_NAME "__$type_info_data"
+6
View File
@@ -1010,6 +1010,11 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
if (op != Token_RangeHalf) { if (op != Token_RangeHalf) {
hi += 1; hi += 1;
} }
GB_ASSERT(0 <= lo && lo <= max_count);
GB_ASSERT(0 <= hi && hi <= max_count);
GB_ASSERT(lo <= hi);
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value; LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
for (i64 k = lo; k < hi; k++) { for (i64 k = lo; k < hi; k++) {
@@ -1021,6 +1026,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
TypeAndValue index_tav = fv->field->tav; TypeAndValue index_tav = fv->field->tav;
GB_ASSERT(index_tav.mode == Addressing_Constant); GB_ASSERT(index_tav.mode == Addressing_Constant);
i64 index = exact_value_to_i64(index_tav.value); i64 index = exact_value_to_i64(index_tav.value);
GB_ASSERT(index < max_count);
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value; LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
i64 offset = matrix_row_major_index_to_offset(type, index); i64 offset = matrix_row_major_index_to_offset(type, index);
+1 -10
View File
@@ -538,16 +538,7 @@ LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
} }
case Type_Basic: default:
case Type_Pointer:
case Type_Array:
case Type_EnumeratedArray:
case Type_Tuple:
case Type_Proc:
case Type_SimdVector:
case Type_RelativePointer:
case Type_RelativeSlice:
case Type_Matrix:
{ {
LLVMMetadataRef debug_bt = lb_debug_type(m, bt); LLVMMetadataRef debug_bt = lb_debug_type(m, bt);
LLVMMetadataRef final_decl = LLVMDIBuilderCreateTypedef(m->debug_builder, debug_bt, name_text, name_len, file, line, scope, align_in_bits); LLVMMetadataRef final_decl = LLVMDIBuilderCreateTypedef(m->debug_builder, debug_bt, name_text, name_len, file, line, scope, align_in_bits);
+19 -14
View File
@@ -490,15 +490,11 @@ bool lb_is_matrix_simdable(Type *t) {
} }
switch (build_context.metrics.arch) { switch (build_context.metrics.arch) {
default:
return false;
case TargetArch_amd64: case TargetArch_amd64:
case TargetArch_arm64: case TargetArch_arm64:
// possible
break; break;
case TargetArch_386:
case TargetArch_wasm32:
case TargetArch_wasm64:
// nope
return false;
} }
if (elem->kind == Type_Basic) { if (elem->kind == Type_Basic) {
@@ -2018,14 +2014,23 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
i64 src_count = src->Matrix.row_count*src->Matrix.column_count; i64 src_count = src->Matrix.row_count*src->Matrix.column_count;
GB_ASSERT(dst_count == src_count); GB_ASSERT(dst_count == src_count);
for (i64 j = 0; j < src->Matrix.column_count; j++) { lbValue pdst = v.addr;
for (i64 i = 0; i < src->Matrix.row_count; i++) { lbValue psrc = lb_address_from_load_or_generate_local(p, value);
lbValue s = lb_emit_matrix_ev(p, value, i, j);
i64 index = i + j*src->Matrix.row_count; bool same_elem_base_types = are_types_identical(
i64 dst_i = index%dst->Matrix.row_count; base_type(dst->Matrix.elem),
i64 dst_j = index/dst->Matrix.row_count; base_type(src->Matrix.elem)
lbValue d = lb_emit_matrix_epi(p, v.addr, dst_i, dst_j); );
lb_emit_store(p, d, s);
if (same_elem_base_types && type_size_of(dst) == type_size_of(src)) {
lb_mem_copy_overlapping(p, v.addr, psrc, lb_const_int(p->module, t_int, type_size_of(dst)));
} else {
for (i64 i = 0; i < src_count; i++) {
lbValue dp = lb_emit_array_epi(p, v.addr, matrix_column_major_index_to_offset(dst, i));
lbValue sp = lb_emit_array_epi(p, psrc, matrix_column_major_index_to_offset(src, i));
lbValue s = lb_emit_load(p, sp);
s = lb_emit_conv(p, s, dst->Matrix.elem);
lb_emit_store(p, dp, s);
} }
} }
} }
+2 -2
View File
@@ -1,4 +1,4 @@
void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false) { void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile) {
dst = lb_emit_conv(p, dst, t_rawptr); dst = lb_emit_conv(p, dst, t_rawptr);
src = lb_emit_conv(p, src, t_rawptr); src = lb_emit_conv(p, src, t_rawptr);
len = lb_emit_conv(p, len, t_int); len = lb_emit_conv(p, len, t_int);
@@ -27,7 +27,7 @@ void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue l
args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, is_volatile); args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, is_volatile);
LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
} }
void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false) { void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile) {
dst = lb_emit_conv(p, dst, t_rawptr); dst = lb_emit_conv(p, dst, t_rawptr);
src = lb_emit_conv(p, src, t_rawptr); src = lb_emit_conv(p, src, t_rawptr);
len = lb_emit_conv(p, len, t_int); len = lb_emit_conv(p, len, t_int);
+6
View File
@@ -138,10 +138,16 @@ i32 linker_stage(lbGenerator *gen) {
if (is_arch_wasm()) { if (is_arch_wasm()) {
timings_start_section(timings, str_lit("wasm-ld")); timings_start_section(timings, str_lit("wasm-ld"));
#if defined(GB_SYSTEM_WINDOWS)
result = system_exec_command_line_app("wasm-ld", result = system_exec_command_line_app("wasm-ld",
"\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s", "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
LIT(build_context.ODIN_ROOT), LIT(build_context.ODIN_ROOT),
LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#else
result = system_exec_command_line_app("wasm-ld",
"wasm-ld \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#endif
return result; return result;
} }
+10 -2
View File
@@ -1406,18 +1406,26 @@ i64 matrix_indices_to_offset(Type *t, i64 row_index, i64 column_index) {
GB_ASSERT(0 <= row_index && row_index < t->Matrix.row_count); GB_ASSERT(0 <= row_index && row_index < t->Matrix.row_count);
GB_ASSERT(0 <= column_index && column_index < t->Matrix.column_count); GB_ASSERT(0 <= column_index && column_index < t->Matrix.column_count);
i64 stride_elems = matrix_type_stride_in_elems(t); i64 stride_elems = matrix_type_stride_in_elems(t);
return stride_elems*column_index + row_index; // NOTE(bill): Column-major layout internally
return row_index + stride_elems*column_index;
} }
i64 matrix_row_major_index_to_offset(Type *t, i64 index) { i64 matrix_row_major_index_to_offset(Type *t, i64 index) {
t = base_type(t); t = base_type(t);
GB_ASSERT(t->kind == Type_Matrix); GB_ASSERT(t->kind == Type_Matrix);
i64 column_index = index%t->Matrix.column_count;
i64 row_index = index/t->Matrix.column_count; i64 row_index = index/t->Matrix.column_count;
i64 column_index = index%t->Matrix.column_count;
return matrix_indices_to_offset(t, row_index, column_index); return matrix_indices_to_offset(t, row_index, column_index);
} }
i64 matrix_column_major_index_to_offset(Type *t, i64 index) {
t = base_type(t);
GB_ASSERT(t->kind == Type_Matrix);
i64 row_index = index%t->Matrix.row_count;
i64 column_index = index/t->Matrix.row_count;
return matrix_indices_to_offset(t, row_index, column_index);
}
bool is_matrix_square(Type *t) { bool is_matrix_square(Type *t) {
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)
+109
View File
@@ -0,0 +1,109 @@
<img align="left" src="https://github.com/raysan5/raylib/blob/master/logo/raylib_logo_animation.gif" width="288px">
**raylib is a simple and easy-to-use library to enjoy videogames programming.**
raylib is highly inspired by Borland BGI graphics lib and by XNA framework and it's specially well suited for prototyping, tooling, graphical applications, embedded systems and education.
*NOTE for ADVENTURERS: raylib is a programming library to enjoy videogames programming; no fancy interface, no visual helpers, no debug button... just coding in the most pure spartan-programmers way.*
Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html)
---
<br>
[![GitHub contributors](https://img.shields.io/github/contributors/raysan5/raylib)](https://github.com/raysan5/raylib/graphs/contributors)
[![GitHub All Releases](https://img.shields.io/github/downloads/raysan5/raylib/total)](https://github.com/raysan5/raylib/releases)
[![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/4.0.0)](https://github.com/raysan5/raylib/commits/master)
[![License](https://img.shields.io/badge/license-zlib%2Flibpng-blue.svg)](LICENSE)
[![Chat on Discord](https://img.shields.io/discord/426912293134270465.svg?logo=discord)](https://discord.gg/raylib)
[![GitHub stars](https://img.shields.io/github/stars/raysan5/raylib?style=social)](https://github.com/raysan5/raylib/stargazers)
[![Twitter Follow](https://img.shields.io/twitter/follow/raysan5?style=social)](https://twitter.com/raysan5)
[![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/raylib?style=social)](https://www.reddit.com/r/raylib/)
[![Windows](https://github.com/raysan5/raylib/workflows/Windows/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWindows)
[![Linux](https://github.com/raysan5/raylib/workflows/Linux/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ALinux)
[![macOS](https://github.com/raysan5/raylib/workflows/macOS/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AmacOS)
[![Android](https://github.com/raysan5/raylib/workflows/Android/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AAndroid)
[![WebAssembly](https://github.com/raysan5/raylib/workflows/WebAssembly/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWebAssembly)
[![CMakeBuilds](https://github.com/raysan5/raylib/workflows/CMakeBuilds/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ACMakeBuilds)
[![Windows Examples](https://github.com/raysan5/raylib/actions/workflows/windows_examples.yml/badge.svg)](https://github.com/raysan5/raylib/actions/workflows/windows_examples.yml)
[![Linux Examples](https://github.com/raysan5/raylib/actions/workflows/linux_examples.yml/badge.svg)](https://github.com/raysan5/raylib/actions/workflows/linux_examples.yml)
features
--------
- **NO external dependencies**, all required libraries are [bundled into raylib](https://github.com/raysan5/raylib/tree/master/src/external)
- Multiple platforms supported: **Windows, Linux, MacOS, RPI, Android, HTML5... and more!**
- Written in plain C code (C99) in PascalCase/camelCase notation
- Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3 or ES 2.0**)
- **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h)
- Multiple **Fonts** formats supported (TTF, XNA fonts, AngelCode fonts)
- Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC)
- **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more!
- Flexible Materials system, supporting classic maps and **PBR maps**
- **Animated 3D models** supported (skeletal bones animation) (IQM)
- Shaders support, including model and **postprocessing** shaders.
- **Powerful math module** for Vector, Matrix and Quaternion operations: [raymath](https://github.com/raysan5/raylib/blob/master/src/raymath.h)
- Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD)
- **VR stereo rendering** support with configurable HMD device parameters
- Huge examples collection with [+120 code examples](https://github.com/raysan5/raylib/tree/master/examples)!
- Bindings to [+50 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)!
- **Free and open source**.
basic example
--------------
This is a basic raylib example, it creates a window and it draws the text `"Congrats! You created your first window!"` in the middle of the screen. Check this example [running live on web here](https://www.raylib.com/examples/core/loader.html?name=core_basic_window).
```odin
package example
import rl "vendor:raylib"
main :: proc() {
rl.InitWindow(800, 450, "raylib [core] example - basic window")
for !rl.WindowShouldClose() {
rl.BeginDrawing()
rl.ClearBackground(rl.RAYWHITE)
rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LIGHTGRAY)
rl.EndDrawing()
}
rl.CloseWindow()
}
```
learning and docs
------------------
raylib is designed to be learned using [the examples](https://github.com/raysan5/raylib/tree/master/examples) as the main reference. There is no standard API documentation but there is a [**cheatsheet**](https://www.raylib.com/cheatsheet/cheatsheet.html) containing all the functions available on the library and a short description of each one of them, input parameters and result value names should be intuitive enough to understand how each function works.
Some additional documentation about raylib design can be found in raylib GitHub Wiki. Here the more relevant links:
- [raylib cheatsheet](https://www.raylib.com/cheatsheet/cheatsheet.html)
- [raylib architecture](https://github.com/raysan5/raylib/wiki/raylib-architecture)
- [raylib library design](https://github.com/raysan5/raylib/wiki)
- [raylib examples collection](https://github.com/raysan5/raylib/tree/master/examples)
- [raylib games collection](https://github.com/raysan5/raylib-games)
contact and networks
---------------------
raylib is present in several networks and raylib community is growing everyday. If you are using raylib and enjoying it, feel free to join us in any of these networks. The most active network is our [Discord server](https://discord.gg/raylib)! :)
- Webpage: [https://www.raylib.com](https://www.raylib.com)
- Discord: [https://discord.gg/raylib](https://discord.gg/raylib)
- Twitter: [https://www.twitter.com/raysan5](https://www.twitter.com/raysan5)
- Twitch: [https://www.twitch.tv/raysan5](https://www.twitch.tv/raysan5)
- Reddit: [https://www.reddit.com/r/raylib](https://www.reddit.com/r/raylib)
- Patreon: [https://www.patreon.com/raylib](https://www.patreon.com/raylib)
- YouTube: [https://www.youtube.com/channel/raylib](https://www.youtube.com/c/raylib)
license
-------
raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, BSD-like license that allows static linking with closed source software. Check [LICENSE](LICENSE) for further details.
raylib uses internally some libraries for window/graphics/inputs management and also to support different fileformats loading, all those libraries are embedded with and are available in [src/external](https://github.com/raysan5/raylib/tree/master/src/external) directory. Check [raylib dependencies LICENSES](https://github.com/raysan5/raylib/wiki/raylib-dependencies) on raylib Wiki for details.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+185 -81
View File
@@ -1,3 +1,79 @@
/**********************************************************************************************
*
* raylib v4.0 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
*
* FEATURES:
* - NO external dependencies, all required libraries included with raylib
* - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly,
* MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5.
* - Written in plain C code (C99) in PascalCase/camelCase notation
* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3 or ES2 - choose at compile)
* - Unique OpenGL abstraction layer (usable as standalone module): [rlgl]
* - Multiple Fonts formats supported (TTF, XNA fonts, AngelCode fonts)
* - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC)
* - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more!
* - Flexible Materials system, supporting classic maps and PBR maps
* - Animated 3D models supported (skeletal bones animation) (IQM)
* - Shaders support, including Model shaders and Postprocessing shaders
* - Powerful math module for Vector, Matrix and Quaternion operations: [raymath]
* - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD)
* - VR stereo rendering with configurable HMD device parameters
* - Bindings to multiple programming languages available!
*
* NOTES:
* - One default Font is loaded on InitWindow()->LoadFontDefault() [core, text]
* - One default Texture2D is loaded on rlglInit(), 1x1 white pixel R8G8B8A8 [rlgl] (OpenGL 3.3 or ES2)
* - One default Shader is loaded on rlglInit()->rlLoadShaderDefault() [rlgl] (OpenGL 3.3 or ES2)
* - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2)
*
* DEPENDENCIES (included):
* [rcore] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input (PLATFORM_DESKTOP)
* [rlgl] glad (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading (PLATFORM_DESKTOP)
* [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management
*
* OPTIONAL DEPENDENCIES (included):
* [rcore] msf_gif (Miles Fogle) for GIF recording
* [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorythm
* [rcore] sdefl (Micha Mettke) for DEFLATE compression algorythm
* [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...)
* [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG)
* [rtextures] stb_image_resize (Sean Barret) for image resizing algorithms
* [rtext] stb_truetype (Sean Barret) for ttf fonts loading
* [rtext] stb_rect_pack (Sean Barret) for rectangles packing
* [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation
* [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL)
* [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF)
* [raudio] dr_wav (David Reid) for WAV audio file loading
* [raudio] dr_flac (David Reid) for FLAC audio file loading
* [raudio] dr_mp3 (David Reid) for MP3 audio file loading
* [raudio] stb_vorbis (Sean Barret) for OGG audio loading
* [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading
* [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading
*
*
* LICENSE: zlib/libpng
*
* raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software:
*
* Copyright (c) 2013-2021 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose, including commercial
* applications, and to alter it and redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not claim that you
* wrote the original software. If you use this software in a product, an acknowledgment
* in the product documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
* as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
**********************************************************************************************/
package raylib package raylib
import c "core:c/libc" import c "core:c/libc"
@@ -33,9 +109,12 @@ when ODIN_OS == "linux" {
} }
when ODIN_OS == "darwin" { foreign import lib "macos/libraylib.a" } when ODIN_OS == "darwin" { foreign import lib "macos/libraylib.a" }
VERSION :: "3.7" VERSION :: "4.0"
PI :: 3.14159265358979323846 PI :: 3.14159265358979323846
DEG2RAD :: PI/180.0
RAD2DEG :: 180.0/PI
// Some Basic Colors // Some Basic Colors
// NOTE: Custom raylib color palette for amazing visuals on WHITE background // NOTE: Custom raylib color palette for amazing visuals on WHITE background
@@ -92,26 +171,29 @@ when USE_LINALG {
// Quaternion type // Quaternion type
Quaternion :: distinct quaternion128 Quaternion :: distinct quaternion128
// Matrix type (OpenGL style 4x4 - right handed, column major) // Matrix, 4x4 components, column major, OpenGL style, right handed
Matrix :: struct { Matrix :: struct {
m0, m4, m8, m12: f32, m0, m4, m8, m12: f32, // Matrix first row (4 components)
m1, m5, m9, m13: f32, m1, m5, m9, m13: f32, // Matrix second row (4 components)
m2, m6, m10, m14: f32, m2, m6, m10, m14: f32, // Matrix third row (4 components)
m3, m7, m11, m15: f32, m3, m7, m11, m15: f32, // Matrix fourth row (4 components)
} }
} }
// Color type, RGBA (32bit) // Color, 4 components, R8G8B8A8 (32bit)
Color :: struct { Color :: struct {
r, g, b, a: u8, r: u8, // Color red value
g: u8, // Color green value
b: u8, // Color blue value
a: u8, // Color alpha value
} }
// Rectangle type // Rectangle type
Rectangle :: struct { Rectangle :: struct {
x: f32, x: f32, // Rectangle top-left corner position x
y: f32, y: f32, // Rectangle top-left corner position y
width: f32, width: f32, // Rectangle width
height: f32, height: f32, // Rectangle height
} }
// Image type, bpp always RGBA (32bit) // Image type, bpp always RGBA (32bit)
@@ -161,7 +243,7 @@ NPatchInfo :: struct {
} }
// Font character info // Font character info
CharInfo :: struct { GlyphInfo :: struct {
value: rune, // Character value (Unicode) value: rune, // Character value (Unicode)
offsetX: c.int, // Character offset X when drawing offsetX: c.int, // Character offset X when drawing
offsetY: c.int, // Character offset Y when drawing offsetY: c.int, // Character offset Y when drawing
@@ -176,7 +258,7 @@ Font :: struct {
charsPadding: c.int, // Padding around the chars charsPadding: c.int, // Padding around the chars
texture: Texture2D, // Characters texture atlas texture: Texture2D, // Characters texture atlas
recs: [^]Rectangle, // Characters rectangles in texture recs: [^]Rectangle, // Characters rectangles in texture
chars: [^]CharInfo, // Characters info data chars: [^]GlyphInfo, // Characters info data
} }
SpriteFont :: Font // SpriteFont type fallback, defaults to Font SpriteFont :: Font // SpriteFont type fallback, defaults to Font
@@ -289,11 +371,11 @@ Ray :: struct {
direction: Vector3, // Ray direction direction: Vector3, // Ray direction
} }
// Raycast hit information // RayCollision, ray hit information
RayHitInfo :: struct { RayCollision :: struct {
hit: bool, // Did the ray hit something? hit: bool, // Did the ray hit something?
distance: f32, // Distance to nearest hit distance: f32, // Distance to nearest hit
position: Vector3, // Position of nearest hit point: Vector3, // Point of nearest hit
normal: Vector3, // Surface normal of hit normal: Vector3, // Surface normal of hit
} }
@@ -838,6 +920,16 @@ foreign lib {
SetClipboardText :: proc(text: cstring) --- // Set clipboard text content SetClipboardText :: proc(text: cstring) --- // Set clipboard text content
GetClipboardText :: proc() -> cstring --- // Get clipboard text content GetClipboardText :: proc() -> cstring --- // Get clipboard text content
// Custom frame control functions
// NOTE: Those functions are intended for advance users that want full control over the frame processing
// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timming + PollInputEvents()
// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL
SwapScreenBuffer :: proc() --- // Swap back buffer with front buffer (screen drawing)
PollInputEvents :: proc() --- // Register all input events
WaitTime :: proc(ms: f32) --- // Wait for some milliseconds (halt program execution)
// Cursor-related functions // Cursor-related functions
ShowCursor :: proc() --- // Shows cursor ShowCursor :: proc() --- // Shows cursor
HideCursor :: proc() --- // Hides cursor HideCursor :: proc() --- // Hides cursor
@@ -898,6 +990,7 @@ foreign lib {
// Misc. functions // Misc. functions
GetRandomValue :: proc(min, max: c.int) -> c.int --- // Returns a random value between min and max (both included) GetRandomValue :: proc(min, max: c.int) -> c.int --- // Returns a random value between min and max (both included)
SetRandomSeed :: proc(seed: c.uint) --- // Set the seed for the random number generator
TakeScreenshot :: proc(fileName: cstring) --- // Takes a screenshot of current screen (filename extension defines format) TakeScreenshot :: proc(fileName: cstring) --- // Takes a screenshot of current screen (filename extension defines format)
SetConfigFlags :: proc(flags: ConfigFlags) --- // Setup init configuration flags (view FLAGS) SetConfigFlags :: proc(flags: ConfigFlags) --- // Setup init configuration flags (view FLAGS)
@@ -941,6 +1034,8 @@ foreign lib {
CompressData :: proc(data: [^]u8, dataLength: c.int, compDataLength: ^c.int) -> [^]u8 --- // Compress data (DEFLATE algorithm) CompressData :: proc(data: [^]u8, dataLength: c.int, compDataLength: ^c.int) -> [^]u8 --- // Compress data (DEFLATE algorithm)
DecompressData :: proc(compData: [^]u8, compDataLength: c.int, dataLength: ^c.int) -> [^]u8 --- // Decompress data (DEFLATE algorithm) DecompressData :: proc(compData: [^]u8, compDataLength: c.int, dataLength: ^c.int) -> [^]u8 --- // Decompress data (DEFLATE algorithm)
EncodeDataBase64 :: proc(data: [^]u8, dataLength: c.int, outputLength: ^c.int) -> [^]u8 --- // Encode data to Base64 string
DecodeDataBase64 :: proc(data: [^]u8, outputLength: ^c.int) -> [^]u8 --- // Decode Base64 string data
// Persistent storage management // Persistent storage management
SaveStorageValue :: proc(position: c.uint, value: c.int) -> bool --- // Save integer value to storage file (to defined position), returns true on success SaveStorageValue :: proc(position: c.uint, value: c.int) -> bool --- // Save integer value to storage file (to defined position), returns true on success
@@ -992,6 +1087,8 @@ foreign lib {
GetTouchX :: proc() -> c.int --- // Returns touch position X for touch point 0 (relative to screen size) GetTouchX :: proc() -> c.int --- // Returns touch position X for touch point 0 (relative to screen size)
GetTouchY :: proc() -> c.int --- // Returns touch position Y for touch point 0 (relative to screen size) GetTouchY :: proc() -> c.int --- // Returns touch position Y for touch point 0 (relative to screen size)
GetTouchPosition :: proc(index: c.int) -> Vector2 --- // Returns touch position XY for a touch point index (relative to screen size) GetTouchPosition :: proc(index: c.int) -> Vector2 --- // Returns touch position XY for a touch point index (relative to screen size)
GetTouchPointId :: proc(index: c.int) -> c.int --- // Get touch point identifier for given index
GetTouchPointCount :: proc() -> c.int --- // Get number of touch points
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Gestures and Touch Handling Functions (Module: gestures) // Gestures and Touch Handling Functions (Module: gestures)
@@ -999,7 +1096,6 @@ foreign lib {
SetGesturesEnabled :: proc(flags: Gestures) --- // Enable a set of gestures using flags SetGesturesEnabled :: proc(flags: Gestures) --- // Enable a set of gestures using flags
IsGestureDetected :: proc(gesture: Gesture) -> bool --- // Check if a gesture have been detected IsGestureDetected :: proc(gesture: Gesture) -> bool --- // Check if a gesture have been detected
GetGestureDetected :: proc() -> Gesture --- // Get latest detected gesture GetGestureDetected :: proc() -> Gesture --- // Get latest detected gesture
GetTouchPointsCount :: proc() -> c.int --- // Get touch points count
GetGestureHoldDuration :: proc() -> f32 --- // Get gesture hold time in milliseconds GetGestureHoldDuration :: proc() -> f32 --- // Get gesture hold time in milliseconds
GetGestureDragVector :: proc() -> Vector2 --- // Get gesture drag vector GetGestureDragVector :: proc() -> Vector2 --- // Get gesture drag vector
GetGestureDragAngle :: proc() -> f32 --- // Get gesture drag angle GetGestureDragAngle :: proc() -> f32 --- // Get gesture drag angle
@@ -1033,6 +1129,7 @@ foreign lib {
DrawLineEx :: proc(startPos, endPos: Vector2, thick: f32, color: Color) --- // Draw a line defining thickness DrawLineEx :: proc(startPos, endPos: Vector2, thick: f32, color: Color) --- // Draw a line defining thickness
DrawLineBezier :: proc(startPos, endPos: Vector2, thick: f32, color: Color) --- // Draw a line using cubic-bezier curves in-out DrawLineBezier :: proc(startPos, endPos: Vector2, thick: f32, color: Color) --- // Draw a line using cubic-bezier curves in-out
DrawLineBezierQuad :: proc(startPos, endPos: Vector2, controlPos: Vector2, thick: f32, color: Color) --- // Draw line using quadratic bezier curves with a control point DrawLineBezierQuad :: proc(startPos, endPos: Vector2, controlPos: Vector2, thick: f32, color: Color) --- // Draw line using quadratic bezier curves with a control point
DrawLineBezierCubic :: proc(startPos, endPos: Vector2, startControlPos, endControlPos: Vector2, thick: f32, color: Color) --- // Draw line using cubic bezier curves with 2 control points
DrawLineStrip :: proc(points: [^]Vector2, pointsCount: c.int, color: Color) --- // Draw lines sequence DrawLineStrip :: proc(points: [^]Vector2, pointsCount: c.int, color: Color) --- // Draw lines sequence
DrawCircle :: proc(centerX, centerY: c.int, radius: f32, color: Color) --- // Draw a color-filled circle DrawCircle :: proc(centerX, centerY: c.int, radius: f32, color: Color) --- // Draw a color-filled circle
DrawCircleSector :: proc(center: Vector2, radius: f32, startAngle, endAngle: f32, segments: c.int, color: Color) --- // Draw a piece of a circle DrawCircleSector :: proc(center: Vector2, radius: f32, startAngle, endAngle: f32, segments: c.int, color: Color) --- // Draw a piece of a circle
@@ -1061,6 +1158,7 @@ foreign lib {
DrawTriangleStrip :: proc(points: [^]Vector2, pointsCount: c.int, color: Color) --- // Draw a triangle strip defined by points DrawTriangleStrip :: proc(points: [^]Vector2, pointsCount: c.int, color: Color) --- // Draw a triangle strip defined by points
DrawPoly :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, color: Color) --- // Draw a regular polygon (Vector version) DrawPoly :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, color: Color) --- // Draw a regular polygon (Vector version)
DrawPolyLines :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, color: Color) --- // Draw a polygon outline of n sides DrawPolyLines :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, color: Color) --- // Draw a polygon outline of n sides
DrawPolyLinesEx :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, lineThick: f32, color: Color) --- // Draw a polygon outline of n sides with extended parameters
// Basic shapes collision detection functions // Basic shapes collision detection functions
CheckCollisionRecs :: proc(rec1, rec2: Rectangle) -> bool --- // Check collision between two rectangles CheckCollisionRecs :: proc(rec1, rec2: Rectangle) -> bool --- // Check collision between two rectangles
@@ -1070,6 +1168,7 @@ foreign lib {
CheckCollisionPointCircle :: proc(point: Vector2, center: Vector2, radius: f32) -> bool --- // Check if point is inside circle CheckCollisionPointCircle :: proc(point: Vector2, center: Vector2, radius: f32) -> bool --- // Check if point is inside circle
CheckCollisionPointTriangle :: proc(point, p1, p2, p3: Vector2) -> bool --- // Check if point is inside a triangle CheckCollisionPointTriangle :: proc(point, p1, p2, p3: Vector2) -> bool --- // Check if point is inside a triangle
CheckCollisionLines :: proc(startPos1, endPos1, startPos2, endPos2: Vector2, collisionPoint: ^Vector2) -> bool --- // Check the collision between two lines defined by two points each, returns collision point by reference CheckCollisionLines :: proc(startPos1, endPos1, startPos2, endPos2: Vector2, collisionPoint: ^Vector2) -> bool --- // Check the collision between two lines defined by two points each, returns collision point by reference
CheckCollisionPointLine :: proc(point, p1, p2: Vector2, threshold: c.int) -> bool --- // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold]
GetCollisionRec :: proc(rec1, rec2: Rectangle) -> Rectangle --- // Get collision rectangle for two rectangles collision GetCollisionRec :: proc(rec1, rec2: Rectangle) -> Rectangle --- // Get collision rectangle for two rectangles collision
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
@@ -1082,6 +1181,7 @@ foreign lib {
LoadImageRaw :: proc(fileName: cstring, width, height: c.int, format: PixelFormat, headerSize: c.int) -> Image --- // Load image from RAW file data LoadImageRaw :: proc(fileName: cstring, width, height: c.int, format: PixelFormat, headerSize: c.int) -> Image --- // Load image from RAW file data
LoadImageAnim :: proc(fileName: cstring, frames: ^c.int) -> Image --- // Load image sequence from file (frames appended to image.data) LoadImageAnim :: proc(fileName: cstring, frames: ^c.int) -> Image --- // Load image sequence from file (frames appended to image.data)
LoadImageFromMemory :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int) -> Image --- // Load image from memory buffer, fileType refers to extension: i.e. ".png" LoadImageFromMemory :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int) -> Image --- // Load image from memory buffer, fileType refers to extension: i.e. ".png"
LoadImageFromTexture :: proc(texture: Texture2D) -> Image --- // Load image from GPU texture data
UnloadImage :: proc(image: Image) --- // Unload image from CPU memory (RAM) UnloadImage :: proc(image: Image) --- // Unload image from CPU memory (RAM)
ExportImage :: proc(image: Image, fileName: cstring) -> bool --- // Export image data to file, returns true on success ExportImage :: proc(image: Image, fileName: cstring) -> bool --- // Export image data to file, returns true on success
ExportImageAsCode :: proc(image: Image, fileName: cstring) -> bool --- // Export image as code file defining an array of bytes, returns true on success ExportImageAsCode :: proc(image: Image, fileName: cstring) -> bool --- // Export image as code file defining an array of bytes, returns true on success
@@ -1093,7 +1193,6 @@ foreign lib {
GenImageGradientRadial :: proc(width, height: c.int, density: f32, inner, outer: Color) -> Image --- // Generate image: radial gradient GenImageGradientRadial :: proc(width, height: c.int, density: f32, inner, outer: Color) -> Image --- // Generate image: radial gradient
GenImageChecked :: proc(width, height: c.int, checksX, checksY: c.int, col1, col2: Color) -> Image --- // Generate image: checked GenImageChecked :: proc(width, height: c.int, checksX, checksY: c.int, col1, col2: Color) -> Image --- // Generate image: checked
GenImageWhiteNoise :: proc(width, height: c.int, factor: f32) -> Image --- // Generate image: white noise GenImageWhiteNoise :: proc(width, height: c.int, factor: f32) -> Image --- // Generate image: white noise
GenImagePerlinNoise :: proc(width, height: c.int, offsetX, offsetY: c.int, scale: f32) -> Image --- // Generate image: perlin noise
GenImageCellular :: proc(width, height: c.int, tileSize: c.int) -> Image --- // Generate image: cellular algorithm. Bigger tileSize means bigger cells GenImageCellular :: proc(width, height: c.int, tileSize: c.int) -> Image --- // Generate image: cellular algorithm. Bigger tileSize means bigger cells
// Image manipulation functions // Image manipulation functions
@@ -1128,6 +1227,7 @@ foreign lib {
UnloadImageColors :: proc(colors: [^]Color) --- // Unload color data loaded with LoadImageColors() UnloadImageColors :: proc(colors: [^]Color) --- // Unload color data loaded with LoadImageColors()
UnloadImagePalette :: proc(colors: [^]Color) --- // Unload colors palette loaded with LoadImagePalette() UnloadImagePalette :: proc(colors: [^]Color) --- // Unload colors palette loaded with LoadImagePalette()
GetImageAlphaBorder :: proc(image: Image, threshold: f32) -> Rectangle --- // Get image alpha border rectangle GetImageAlphaBorder :: proc(image: Image, threshold: f32) -> Rectangle --- // Get image alpha border rectangle
GetImageColor :: proc(image: Image, x, y: c.int) -> Color --- // Get image pixel color at (x, y) position
// Image drawing functions // Image drawing functions
// NOTE: Image software-rendering functions (CPU) // NOTE: Image software-rendering functions (CPU)
@@ -1156,8 +1256,6 @@ foreign lib {
UnloadRenderTexture :: proc(target: RenderTexture2D) --- // Unload render texture from GPU memory (VRAM) UnloadRenderTexture :: proc(target: RenderTexture2D) --- // Unload render texture from GPU memory (VRAM)
UpdateTexture :: proc(texture: Texture2D, pixels: rawptr) --- // Update GPU texture with new data UpdateTexture :: proc(texture: Texture2D, pixels: rawptr) --- // Update GPU texture with new data
UpdateTextureRec :: proc(texture: Texture2D, rec: Rectangle, pixels: rawptr) --- // Update GPU texture rectangle with new data UpdateTextureRec :: proc(texture: Texture2D, rec: Rectangle, pixels: rawptr) --- // Update GPU texture rectangle with new data
GetTextureData :: proc(texture: Texture2D) -> Image --- // Get pixel data from GPU texture and return an Image
GetScreenData :: proc() -> Image --- // Get pixel data from screen buffer and return an Image (screenshot)
// Texture configuration functions // Texture configuration functions
GenTextureMipmaps :: proc(texture: ^Texture2D) --- // Generate GPU mipmaps for a texture GenTextureMipmaps :: proc(texture: ^Texture2D) --- // Generate GPU mipmaps for a texture
@@ -1199,24 +1297,34 @@ foreign lib {
LoadFontEx :: proc(fileName: cstring, fontSize: c.int, fontChars: [^]rune, charsCount: c.int) -> Font --- // Load font from file with extended parameters LoadFontEx :: proc(fileName: cstring, fontSize: c.int, fontChars: [^]rune, charsCount: c.int) -> Font --- // Load font from file with extended parameters
LoadFontFromImage :: proc(image: Image, key: Color, firstChar: rune) -> Font --- // Load font from Image (XNA style) LoadFontFromImage :: proc(image: Image, key: Color, firstChar: rune) -> Font --- // Load font from Image (XNA style)
LoadFontFromMemory :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int, fontSize: c.int, fontChars: [^]rune, charsCount: c.int) -> Font --- // Load font from memory buffer, fileType refers to extension: i.e. ".ttf" LoadFontFromMemory :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int, fontSize: c.int, fontChars: [^]rune, charsCount: c.int) -> Font --- // Load font from memory buffer, fileType refers to extension: i.e. ".ttf"
LoadFontData :: proc(fileData: rawptr, dataSize: c.int, fontSize: c.int, fontChars: [^]rune, charsCount: c.int, type: FontType) -> [^]CharInfo --- // Load font data for further use LoadFontData :: proc(fileData: rawptr, dataSize: c.int, fontSize: c.int, fontChars: [^]rune, charsCount: c.int, type: FontType) -> [^]GlyphInfo --- // Load font data for further use
GenImageFontAtlas :: proc(chars: [^]CharInfo, recs: ^[^]Rectangle, charsCount: c.int, fontSize: c.int, padding: c.int, packMethod: c.int) -> Image --- // Generate image font atlas using chars info GenImageFontAtlas :: proc(chars: [^]GlyphInfo, recs: ^[^]Rectangle, charsCount: c.int, fontSize: c.int, padding: c.int, packMethod: c.int) -> Image --- // Generate image font atlas using chars info
UnloadFontData :: proc(chars: [^]CharInfo, charsCount: c.int) --- // Unload font chars info data (RAM) UnloadFontData :: proc(chars: [^]GlyphInfo, charsCount: c.int) --- // Unload font chars info data (RAM)
UnloadFont :: proc(font: Font) --- // Unload Font from GPU memory (VRAM) UnloadFont :: proc(font: Font) --- // Unload Font from GPU memory (VRAM)
// Text drawing functions // Text drawing functions
DrawFPS :: proc(posX, posY: c.int) --- // Draw current FPS DrawFPS :: proc(posX, posY: c.int) --- // Draw current FPS
DrawText :: proc(text: cstring, posX, posY: c.int, fontSize: c.int, color: Color) --- // Draw text (using default font) DrawText :: proc(text: cstring, posX, posY: c.int, fontSize: c.int, color: Color) --- // Draw text (using default font)
DrawTextEx :: proc(font: Font, text: cstring, position: Vector2, fontSize: f32, spacing: f32, tint: Color) --- // Draw text using font and additional parameters DrawTextEx :: proc(font: Font, text: cstring, position: Vector2, fontSize: f32, spacing: f32, tint: Color) --- // Draw text using font and additional parameters
DrawTextRec :: proc(font: Font, text: cstring, rec: Rectangle, fontSize, spacing: f32, wordWrap: bool, tint: Color) --- // Draw text using font inside rectangle limits DrawTextPro :: proc(font: Font, text: cstring, position, origin: Vector2,
DrawTextRecEx :: proc(font: Font, text: cstring, rec: Rectangle, fontSize, spacing: f32, wordWrap: bool, tint: Color, rotation: f32, fontSize: f32, spacing: f32, tint: Color) --- // Draw text using Font and pro parameters (rotation)
selectStart, selectLength: c.int, selectTint, selectBackTint: Color) --- // Draw text using font inside rectangle limits with support for text selection
DrawTextCodepoint :: proc(font: Font, codepoint: rune, position: Vector2, fontSize: f32, tint: Color) --- // Draw one character (codepoint) DrawTextCodepoint :: proc(font: Font, codepoint: rune, position: Vector2, fontSize: f32, tint: Color) --- // Draw one character (codepoint)
// Text misc. functions // Text misc. functions
MeasureText :: proc(text: cstring, fontSize: c.int) -> c.int --- // Measure string width for default font MeasureText :: proc(text: cstring, fontSize: c.int) -> c.int --- // Measure string width for default font
MeasureTextEx :: proc(font: Font, text: cstring, fontSize, spacing: f32) -> Vector2 --- // Measure string size for Font MeasureTextEx :: proc(font: Font, text: cstring, fontSize, spacing: f32) -> Vector2 --- // Measure string size for Font
GetGlyphIndex :: proc(font: Font, codepoint: rune) -> c.int --- // Get index position for a unicode character on font GetGlyphIndex :: proc(font: Font, codepoint: rune) -> c.int --- // Get index position for a unicode character on font
GetGlyphInfo :: proc(font: Font, codepoint: rune) -> GlyphInfo --- // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found
GetGlyphAtlasRec :: proc(font: Font, codepoint: rune) -> Rectangle --- // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found
// Text codepoints management functions (unicode characters)
LoadCodepoints :: proc(text: cstring, count: ^c.int) -> [^]rune --- // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter
UnloadCodepoints :: proc(codepoints: [^]rune) --- // Unload codepoints data from memory
GetCodepointCount :: proc(text: cstring) -> c.int --- // Get total number of codepoints in a UTF-8 encoded string
GetCodepoint :: proc(text: cstring, bytesProcessed: ^c.int) -> c.int --- // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
CodepointToUTF8 :: proc(codepoint: rune, byteSize: ^c.int) -> cstring --- // Encode one codepoint into UTF-8 byte array (array length returned as parameter)
TextCodepointsToUTF8 :: proc(codepoints: [^]rune, length: c.int) -> cstring --- // Encode text as codepoints array into UTF-8 text string (WARNING: memory must be freed!)
// Text strings management functions (no utf8 strings, only byte chars) // Text strings management functions (no utf8 strings, only byte chars)
// NOTE: Some strings allocate memory internally for returned strings, just be careful! // NOTE: Some strings allocate memory internally for returned strings, just be careful!
@@ -1234,14 +1342,6 @@ foreign lib {
TextToLower :: proc(text: cstring) -> cstring --- // Get lower case version of provided string TextToLower :: proc(text: cstring) -> cstring --- // Get lower case version of provided string
TextToPascal :: proc(text: cstring) -> cstring --- // Get Pascal case notation version of provided string TextToPascal :: proc(text: cstring) -> cstring --- // Get Pascal case notation version of provided string
TextToInteger :: proc(text: cstring) -> c.int --- // Get integer value from text (negative values not supported) TextToInteger :: proc(text: cstring) -> c.int --- // Get integer value from text (negative values not supported)
TextToUtf8 :: proc(codepoints: [^]rune, length: c.int) -> [^]byte --- // Encode text codepoint into utf8 text (memory must be freed!)
// UTF8 text strings management functions
GetCodepoints :: proc(text: cstring, count: ^c.int) -> [^]rune --- // Get all codepoints in a string, codepoints count returned by parameters
GetCodepointsCount :: proc(text: cstring) -> c.int --- // Get total number of characters (codepoints) in a UTF8 encoded string
GetNextCodepoint :: proc(text: cstring, bytesProcessed: ^c.int) -> c.int --- // Returns next codepoint in a UTF8 encoded string; 0x3f('?') is returned on failure
CodepointToUtf8 :: proc(codepoint: rune, byteLength: ^c.int) -> cstring --- // Encode codepoint into utf8 text (char array length returned as parameter)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Basic 3d Shapes Drawing Functions (Module: models) // Basic 3d Shapes Drawing Functions (Module: models)
@@ -1258,11 +1358,14 @@ foreign lib {
DrawCubeWires :: proc(position: Vector3, width, height, length: f32, color: Color) --- // Draw cube wires DrawCubeWires :: proc(position: Vector3, width, height, length: f32, color: Color) --- // Draw cube wires
DrawCubeWiresV :: proc(position: Vector3, size: Vector3, color: Color) --- // Draw cube wires (Vector version) DrawCubeWiresV :: proc(position: Vector3, size: Vector3, color: Color) --- // Draw cube wires (Vector version)
DrawCubeTexture :: proc(texture: Texture2D, position: Vector3, width, height, length: f32, color: Color) --- // Draw cube textured DrawCubeTexture :: proc(texture: Texture2D, position: Vector3, width, height, length: f32, color: Color) --- // Draw cube textured
DrawCubeTextureRec :: proc(texture: Texture2D, source: Rectangle, position: Vector3, width, height, length: f32, color: Color) --- // Draw cube with a region of a texture
DrawSphere :: proc(centerPos: Vector3, radius: f32, color: Color) --- // Draw sphere DrawSphere :: proc(centerPos: Vector3, radius: f32, color: Color) --- // Draw sphere
DrawSphereEx :: proc(centerPos: Vector3, radius: f32, rings, slices: c.int, color: Color) --- // Draw sphere with extended parameters DrawSphereEx :: proc(centerPos: Vector3, radius: f32, rings, slices: c.int, color: Color) --- // Draw sphere with extended parameters
DrawSphereWires :: proc(centerPos: Vector3, radius: f32, rings, slices: c.int, color: Color) --- // Draw sphere wires DrawSphereWires :: proc(centerPos: Vector3, radius: f32, rings, slices: c.int, color: Color) --- // Draw sphere wires
DrawCylinder :: proc(position: Vector3, radiusTop, radiusBottom: f32, height: f32, slices: c.int, color: Color) --- // Draw a cylinder/cone DrawCylinder :: proc(position: Vector3, radiusTop, radiusBottom: f32, height: f32, slices: c.int, color: Color) --- // Draw a cylinder/cone
DrawCylinderEx :: proc(startPos, endPos: Vector3, startRadius, endRadius: f32, sides: c.int, color: Color) --- // Draw a cylinder with base at startPos and top at endPos
DrawCylinderWires :: proc(position: Vector3, radiusTop, radiusBottom: f32, height: f32, slices: c.int, color: Color) --- // Draw a cylinder/cone wires DrawCylinderWires :: proc(position: Vector3, radiusTop, radiusBottom: f32, height: f32, slices: c.int, color: Color) --- // Draw a cylinder/cone wires
DrawCylinderWiresEx :: proc(startPos, endPos: Vector3, startRadius, endRadius: f32, sides: c.int, color: Color) --- // Draw a cylinder wires with base at startPos and top at endPos
DrawPlane :: proc(centerPos: Vector3, size: Vector2, color: Color) --- // Draw a plane XZ DrawPlane :: proc(centerPos: Vector3, size: Vector2, color: Color) --- // Draw a plane XZ
DrawRay :: proc(ray: Ray, color: Color) --- // Draw a ray line DrawRay :: proc(ray: Ray, color: Color) --- // Draw a ray line
DrawGrid :: proc(slices: c.int, spacing: f32) --- // Draw a grid (centered at (0, 0, 0)) DrawGrid :: proc(slices: c.int, spacing: f32) --- // Draw a grid (centered at (0, 0, 0))
@@ -1276,14 +1379,42 @@ foreign lib {
LoadModelFromMesh :: proc(mesh: Mesh) -> Model --- // Load model from generated mesh (default material) LoadModelFromMesh :: proc(mesh: Mesh) -> Model --- // Load model from generated mesh (default material)
UnloadModel :: proc(model: Model) --- // Unload model (including meshes) from memory (RAM and/or VRAM) UnloadModel :: proc(model: Model) --- // Unload model (including meshes) from memory (RAM and/or VRAM)
UnloadModelKeepMeshes :: proc(model: Model) --- // Unload model (but not meshes) from memory (RAM and/or VRAM) UnloadModelKeepMeshes :: proc(model: Model) --- // Unload model (but not meshes) from memory (RAM and/or VRAM)
GetModelBoundingBox :: proc(model: Model) -> BoundingBox --- // Compute model bounding box limits (considers all meshes)
// Mesh loading/unloading functions // Model drawing functions
DrawModel :: proc(model: Model, position: Vector3, scale: f32, tint: Color) --- // Draw a model (with texture if set)
DrawModelEx :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model with extended parameters
DrawModelWires :: proc(model: Model, position: Vector3, scale: f32, tint: Color) --- // Draw a model wires (with texture if set)
DrawModelWiresEx :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model wires (with texture if set) with extended parameters
DrawBoundingBox :: proc(box: BoundingBox, color: Color) --- // Draw bounding box (wires)
DrawBillboard :: proc(camera: Camera, texture: Texture2D, center: Vector3, size: f32, tint: Color) --- // Draw a billboard texture
DrawBillboardRec :: proc(camera: Camera, texture: Texture2D, source: Rectangle, center: Vector3, size: f32, tint: Color) --- // Draw a billboard texture defined by source
DrawBillboardPro :: proc(camera: Camera, texture: Texture2D, source: Rectangle,
position, up: Vector3, size, origin: Vector2, rotation: f32, tint: Color) --- // Draw a billboard texture defined by source and rotation
// Mesh management functions
UploadMesh :: proc(mesh: ^Mesh, is_dynamic: bool) --- // Upload mesh vertex data in GPU and provide VAO/VBO ids UploadMesh :: proc(mesh: ^Mesh, is_dynamic: bool) --- // Upload mesh vertex data in GPU and provide VAO/VBO ids
UpdateMeshBuffer :: proc(mesh: Mesh, index: c.int, data:rawptr, dataSize, offset: c.int) --- // Update mesh vertex data in GPU for a specific buffer index UpdateMeshBuffer :: proc(mesh: Mesh, index: c.int, data: rawptr, dataSize: c.int, offset: c.int) --- // Update mesh vertex data in GPU for a specific buffer index
UnloadMesh :: proc(mesh: Mesh) --- // Unload mesh data from CPU and GPU
DrawMesh :: proc(mesh: Mesh, material: Material, transform: Matrix) --- // Draw a 3d mesh with material and transform DrawMesh :: proc(mesh: Mesh, material: Material, transform: Matrix) --- // Draw a 3d mesh with material and transform
DrawMeshInstanced :: proc(mesh: Mesh, material: Material, transforms: [^]Matrix, instances: c.int) --- // Draw multiple mesh instances with material and different transforms DrawMeshInstanced :: proc(mesh: Mesh, material: Material, transforms: [^]Matrix, instances: c.int) --- // Draw multiple mesh instances with material and different transforms
UnloadMesh :: proc(mesh: Mesh) --- // Unload mesh data from CPU and GPU
ExportMesh :: proc(mesh: Mesh, fileName: cstring) -> bool --- // Export mesh data to file, returns true on success ExportMesh :: proc(mesh: Mesh, fileName: cstring) -> bool --- // Export mesh data to file, returns true on success
GetMeshBoundingBox :: proc(mesh: Mesh) -> BoundingBox --- // Compute mesh bounding box limits
GenMeshTangents :: proc(mesh: ^Mesh) --- // Compute mesh tangents
GenMeshBinormals :: proc(mesh: ^Mesh) ---
// Mesh generation functions
GenMeshPoly :: proc(sides: c.int, radius: f32) -> Mesh --- // Generate polygonal mesh
GenMeshPlane :: proc(width, length: f32, resX, resZ: c.int) -> Mesh --- // Generate plane mesh (with subdivisions)
GenMeshCube :: proc(width, height, length: f32) -> Mesh --- // Generate cuboid mesh
GenMeshSphere :: proc(radius: f32, rings, slices: c.int) -> Mesh --- // Generate sphere mesh (standard sphere)
GenMeshHemiSphere :: proc(radius: f32, rings, slices: c.int) -> Mesh --- // Generate half-sphere mesh (no bottom cap)
GenMeshCylinder :: proc(radius: f32, height: f32, slices: c.int) -> Mesh --- // Generate cylinder mesh
GenMeshCone :: proc(radius: f32, height: f32, slices: c.int) -> Mesh --- // Generate cone/pyramid mesh
GenMeshTorus :: proc(radius: f32, size: f32, radSeg: c.int, sides: c.int) -> Mesh --- // Generate torus mesh
GenMeshKnot :: proc(radius: f32, size: f32, radSeg: c.int, sides: c.int) -> Mesh --- // Generate trefoil knot mesh
GenMeshHeightmap :: proc(heightmap: Image, size: Vector3) -> Mesh --- // Generate heightmap mesh from image data
GenMeshCubicmap :: proc(cubicmap: Image, cubeSize: Vector3) -> Mesh --- // Generate cubes-based map mesh from image data
// Material loading/unloading functions // Material loading/unloading functions
LoadMaterials :: proc(fileName: cstring, materialCount: ^c.int) -> [^]Material --- // Load materials from model file LoadMaterials :: proc(fileName: cstring, materialCount: ^c.int) -> [^]Material --- // Load materials from model file
@@ -1299,44 +1430,16 @@ foreign lib {
UnloadModelAnimations :: proc(animations: [^]ModelAnimation, count: c.uint) --- // Unload animation array data UnloadModelAnimations :: proc(animations: [^]ModelAnimation, count: c.uint) --- // Unload animation array data
IsModelAnimationValid :: proc(model: Model, anim: ModelAnimation) -> bool --- // Check model animation skeleton match IsModelAnimationValid :: proc(model: Model, anim: ModelAnimation) -> bool --- // Check model animation skeleton match
// Mesh generation functions
GenMeshPoly :: proc(sides: c.int, radius: f32) -> Mesh --- // Generate polygonal mesh
GenMeshPlane :: proc(width, length: f32, resX, resZ: c.int) -> Mesh --- // Generate plane mesh (with subdivisions)
GenMeshCube :: proc(width, height, length: f32) -> Mesh --- // Generate cuboid mesh
GenMeshSphere :: proc(radius: f32, rings, slices: c.int) -> Mesh --- // Generate sphere mesh (standard sphere)
GenMeshHemiSphere :: proc(radius: f32, rings, slices: c.int) -> Mesh --- // Generate half-sphere mesh (no bottom cap)
GenMeshCylinder :: proc(radius: f32, height: f32, slices: c.int) -> Mesh --- // Generate cylinder mesh
GenMeshTorus :: proc(radius: f32, size: f32, radSeg: c.int, sides: c.int) -> Mesh --- // Generate torus mesh
GenMeshKnot :: proc(radius: f32, size: f32, radSeg: c.int, sides: c.int) -> Mesh --- // Generate trefoil knot mesh
GenMeshHeightmap :: proc(heightmap: Image, size: Vector3) -> Mesh --- // Generate heightmap mesh from image data
GenMeshCubicmap :: proc(cubicmap: Image, cubeSize: Vector3) -> Mesh --- // Generate cubes-based map mesh from image data
// Mesh manipulation functions
MeshBoundingBox :: proc(mesh: Mesh) -> BoundingBox --- // Compute mesh bounding box limits
MeshTangents :: proc(mesh: ^Mesh) --- // Compute mesh tangents
MeshBinormals :: proc(mesh: ^Mesh) --- // Compute mesh binormals
// Model drawing functions
DrawModel :: proc(model: Model, position: Vector3, scale: f32, tint: Color) --- // Draw a model (with texture if set)
DrawModelEx :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model with extended parameters
DrawModelWires :: proc(model: Model, position: Vector3, scale: f32, tint: Color) --- // Draw a model wires (with texture if set)
DrawModelWiresEx :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model wires (with texture if set) with extended parameters
DrawBoundingBox :: proc(box: BoundingBox, color: Color) --- // Draw bounding box (wires)
DrawBillboard :: proc(camera: Camera, texture: Texture2D, center: Vector3, size: f32, tint: Color) --- // Draw a billboard texture
DrawBillboardRec :: proc(camera: Camera, texture: Texture2D, source: Rectangle, center: Vector3, size: f32, tint: Color) --- // Draw a billboard texture defined by source
// Collision detection functions // Collision detection functions
CheckCollisionSpheres :: proc(center1: Vector3, radius1: f32, center2: Vector3, radius2: f32) -> bool --- // Detect collision between two spheres CheckCollisionSpheres :: proc(center1: Vector3, radius1: f32, center2: Vector3, radius2: f32) -> bool --- // Check collision between two spheres
CheckCollisionBoxes :: proc(box1, box2: BoundingBox) -> bool --- // Detect collision between two bounding boxes CheckCollisionBoxes :: proc(box1, box2: BoundingBox) -> bool --- // Check collision between two bounding boxes
CheckCollisionBoxSphere :: proc(box: BoundingBox, center: Vector3, radius: f32) -> bool --- // Detect collision between box and sphere CheckCollisionBoxSphere :: proc(box: BoundingBox, center: Vector3, radius: f32) -> bool --- // Check collision between box and sphere
CheckCollisionRaySphere :: proc(ray: Ray, center: Vector3, radius: f32) -> bool --- // Detect collision between ray and sphere GetRayCollisionSphere :: proc(ray: Ray, center: Vector3, radius: f32) -> RayCollision --- // Get collision info between ray and sphere
CheckCollisionRaySphereEx :: proc(ray: Ray, center: Vector3, radius: f32, collisionPoint: [^]Vector3) -> bool --- // Detect collision between ray and sphere, returns collision point GetRayCollisionBox :: proc(ray: Ray, box: BoundingBox) -> RayCollision --- // Get collision info between ray and box
CheckCollisionRayBox :: proc(ray: Ray, box: BoundingBox) -> bool --- // Detect collision between ray and box GetRayCollisionModel :: proc(ray: Ray, model: Model) -> RayCollision --- // Get collision info between ray and model
GetCollisionRayMesh :: proc(ray: Ray, mesh: Mesh, transform: Matrix) -> RayHitInfo --- // Get collision info between ray and mesh GetRayCollisionMesh :: proc(ray: Ray, mesh: Mesh, transform: Matrix) -> RayCollision --- // Get collision info between ray and mesh
GetCollisionRayModel :: proc(ray: Ray, model: Model) -> RayHitInfo --- // Get collision info between ray and model GetRayCollisionTriangle :: proc(ray: Ray, p1, p2, p3: Vector3) -> RayCollision --- // Get collision info between ray and triangle
GetCollisionRayTriangle :: proc(ray: Ray, p1, p2, p3: Vector3) -> RayHitInfo --- // Get collision info between ray and triangle GetRayCollisionQuad :: proc(ray: Ray, p1, p2, p3, p4: Vector3) -> RayCollision --- // Get collision info between ray and quad
GetCollisionRayGround :: proc(ray: Ray, groundHeight: f32) -> RayHitInfo --- // Get collision info between ray and ground plane (Y-normal plane)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Audio Loading and Playing Functions (Module: audio) // Audio Loading and Playing Functions (Module: audio)
@@ -1381,25 +1484,26 @@ foreign lib {
LoadMusicStreamFromMemory :: proc(fileType: cstring, data: rawptr, dataSize: c.int) -> Music --- // Load music stream from data LoadMusicStreamFromMemory :: proc(fileType: cstring, data: rawptr, dataSize: c.int) -> Music --- // Load music stream from data
UnloadMusicStream :: proc(music: Music) --- // Unload music stream UnloadMusicStream :: proc(music: Music) --- // Unload music stream
PlayMusicStream :: proc(music: Music) --- // Start music playing PlayMusicStream :: proc(music: Music) --- // Start music playing
IsMusicPlaying :: proc(music: Music) -> bool --- // Check if music is playing IsMusicStreamPlaying :: proc(music: Music) -> bool --- // Check if music is playing
UpdateMusicStream :: proc(music: Music) --- // Updates buffers for music streaming UpdateMusicStream :: proc(music: Music) --- // Updates buffers for music streaming
StopMusicStream :: proc(music: Music) --- // Stop music playing StopMusicStream :: proc(music: Music) --- // Stop music playing
PauseMusicStream :: proc(music: Music) --- // Pause music playing PauseMusicStream :: proc(music: Music) --- // Pause music playing
ResumeMusicStream :: proc(music: Music) --- // Resume playing paused music ResumeMusicStream :: proc(music: Music) --- // Resume playing paused music
SeekMusicStream :: proc(music: Music, position: f32) --- // Seek music to a position (in seconds)
SetMusicVolume :: proc(music: Music, volume: f32) --- // Set volume for music (1.0 is max level) SetMusicVolume :: proc(music: Music, volume: f32) --- // Set volume for music (1.0 is max level)
SetMusicPitch :: proc(music: Music, pitch: f32) --- // Set pitch for a music (1.0 is base level) SetMusicPitch :: proc(music: Music, pitch: f32) --- // Set pitch for a music (1.0 is base level)
GetMusicTimeLength :: proc(music: Music) -> f32 --- // Get music time length (in seconds) GetMusicTimeLength :: proc(music: Music) -> f32 --- // Get music time length (in seconds)
GetMusicTimePlayed :: proc(music: Music) -> f32 --- // Get current music time played (in seconds) GetMusicTimePlayed :: proc(music: Music) -> f32 --- // Get current music time played (in seconds)
// AudioStream management functions // AudioStream management functions
InitAudioStream :: proc(sampleRate, sampleSize, channels: c.uint) -> AudioStream --- // Init audio stream (to stream raw audio pcm data) LoadAudioStream :: proc(sampleRate, sampleSize, channels: c.uint) -> AudioStream --- // Load audio stream (to stream raw audio pcm data)
UpdateAudioStream :: proc(stream: AudioStream, data: rawptr, samplesCount: c.int) --- // Update audio stream buffers with data UnloadAudioStream :: proc(stream: AudioStream) --- // Unload audio stream and free memory
CloseAudioStream :: proc(stream: AudioStream) --- // Close audio stream and free memory UpdateAudioStream :: proc(stream: AudioStream, data: rawptr, frameCount: c.int) --- // Update audio stream buffers with data
IsAudioStreamProcessed :: proc(stream: AudioStream) -> AudioStream --- // Check if any audio stream buffers requires refill IsAudioStreamProcessed :: proc(stream: AudioStream) -> bool --- // Check if any audio stream buffers requires refill
PlayAudioStream :: proc(stream: AudioStream) --- // Play audio stream PlayAudioStream :: proc(stream: AudioStream) --- // Play audio stream
PauseAudioStream :: proc(stream: AudioStream) --- // Pause audio stream PauseAudioStream :: proc(stream: AudioStream) --- // Pause audio stream
ResumeAudioStream :: proc(stream: AudioStream) --- // Resume audio stream ResumeAudioStream :: proc(stream: AudioStream) --- // Resume audio stream
IsAudioStreamPlaying :: proc(stream: AudioStream) -> AudioStream --- // Check if audio stream is playing IsAudioStreamPlaying :: proc(stream: AudioStream) -> bool --- // Check if audio stream is playing
StopAudioStream :: proc(stream: AudioStream) --- // Stop audio stream StopAudioStream :: proc(stream: AudioStream) --- // Stop audio stream
SetAudioStreamVolume :: proc(stream: AudioStream, volume: f32) --- // Set volume for audio stream (1.0 is max level) SetAudioStreamVolume :: proc(stream: AudioStream, volume: f32) --- // Set volume for audio stream (1.0 is max level)
SetAudioStreamPitch :: proc(stream: AudioStream, pitch: f32) --- // Set pitch for audio stream (1.0 is base level) SetAudioStreamPitch :: proc(stream: AudioStream, pitch: f32) --- // Set pitch for audio stream (1.0 is base level)
+44 -8
View File
@@ -18,6 +18,7 @@ GRAPHICS_API_OPENGL_11 :: false
GRAPHICS_API_OPENGL_21 :: true GRAPHICS_API_OPENGL_21 :: true
GRAPHICS_API_OPENGL_33 :: GRAPHICS_API_OPENGL_21 GRAPHICS_API_OPENGL_33 :: GRAPHICS_API_OPENGL_21
GRAPHICS_API_OPENGL_ES2 :: false GRAPHICS_API_OPENGL_ES2 :: false
GRAPHICS_API_OPENGL_43 :: false
when !GRAPHICS_API_OPENGL_ES2 { when !GRAPHICS_API_OPENGL_ES2 {
// This is the maximum amount of elements (quads) per batch // This is the maximum amount of elements (quads) per batch
@@ -37,12 +38,8 @@ MAX_BATCH_ACTIVE_TEXTURES :: 4 // Maximum number of addi
// Internal Matrix stack // Internal Matrix stack
MAX_MATRIX_STACK_SIZE :: 32 // Maximum size of Matrix stack MAX_MATRIX_STACK_SIZE :: 32 // Maximum size of Matrix stack
// Vertex buffers id limit // Shader limits
MAX_MESH_VERTEX_BUFFERS :: 7 // Maximum vertex buffers (VBO) per mesh
// Shader and material limits
MAX_SHADER_LOCATIONS :: 32 // Maximum number of shader locations supported MAX_SHADER_LOCATIONS :: 32 // Maximum number of shader locations supported
MAX_MATERIAL_MAPS :: 12 // Maximum number of shader maps supported
// Projection matrix culling // Projection matrix culling
RL_CULL_DISTANCE_NEAR :: 0.01 // Default near cull distance RL_CULL_DISTANCE_NEAR :: 0.01 // Default near cull distance
@@ -79,7 +76,23 @@ RL_QUADS :: 0x0007 // GL_QUADS
// GL equivalent data types // GL equivalent data types
RL_UNSIGNED_BYTE :: 0x1401 // GL_UNSIGNED_BYTE RL_UNSIGNED_BYTE :: 0x1401 // GL_UNSIGNED_BYTE
RL_f32 :: 0x1406 // GL_f32 RL_FLOAT :: 0x1406 // GL_FLOAT
// Buffer usage hint
RL_STREAM_DRAW :: 0x88E0 // GL_STREAM_DRAW
RL_STREAM_READ :: 0x88E1 // GL_STREAM_READ
RL_STREAM_COPY :: 0x88E2 // GL_STREAM_COPY
RL_STATIC_DRAW :: 0x88E4 // GL_STATIC_DRAW
RL_STATIC_READ :: 0x88E5 // GL_STATIC_READ
RL_STATIC_COPY :: 0x88E6 // GL_STATIC_COPY
RL_DYNAMIC_DRAW :: 0x88E8 // GL_DYNAMIC_DRAW
RL_DYNAMIC_READ :: 0x88E9 // GL_DYNAMIC_READ
RL_DYNAMIC_COPY :: 0x88EA // GL_DYNAMIC_COPY
// GL Shader type
RL_FRAGMENT_SHADER :: 0x8B30 // GL_FRAGMENT_SHADER
RL_VERTEX_SHADER :: 0x8B31 // GL_VERTEX_SHADER
RL_COMPUTE_SHADER :: 0x91B9 // GL_COMPUTE_SHADER
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@@ -230,8 +243,11 @@ foreign lib {
// Framebuffer state // Framebuffer state
rlEnableFramebuffer :: proc(id: u32) --- // Enable render texture (fbo) rlEnableFramebuffer :: proc(id: u32) --- // Enable render texture (fbo)
rlDisableFramebuffer :: proc() --- // Disable render texture (fbo), return to default framebuffer rlDisableFramebuffer :: proc() --- // Disable render texture (fbo), return to default framebuffer
rlActiveDrawBuffers :: proc(count: c.int) --- // Activate multiple draw color buffers
// General render state // General render state
rlEnableColorBlend :: proc() --- // Enable color blending
rlDisableColorBlend :: proc() --- // Disable color blending
rlEnableDepthTest :: proc() --- // Enable depth test rlEnableDepthTest :: proc() --- // Enable depth test
rlDisableDepthTest :: proc() --- // Disable depth test rlDisableDepthTest :: proc() --- // Disable depth test
rlEnableDepthMask :: proc() --- // Enable depth write rlEnableDepthMask :: proc() --- // Enable depth write
@@ -268,8 +284,9 @@ foreign lib {
rlGetFramebufferWidth :: proc() -> c.int --- // Get default framebuffer width rlGetFramebufferWidth :: proc() -> c.int --- // Get default framebuffer width
rlGetFramebufferHeight :: proc() -> c.int --- // Get default framebuffer height rlGetFramebufferHeight :: proc() -> c.int --- // Get default framebuffer height
rlGetShaderDefault :: proc() -> Shader --- // Get default shader rlGetTextureIdDefault :: proc() -> u32 --- // Get default texture id
rlGetTextureDefault :: proc() -> Texture2D --- // Get default texture rlGetShaderIdDefault :: proc() -> u32 --- // Get default shader id
rlGetShaderLocsDefault :: proc() -> [^]i32 --- // Get default shader locations
// Render batch management // Render batch management
// NOTE: rlgl provides a default render batch to behave like OpenGL 1.1 immediate mode // NOTE: rlgl provides a default render batch to behave like OpenGL 1.1 immediate mode
@@ -305,6 +322,7 @@ foreign lib {
rlLoadTextureCubemap :: proc(data: rawptr, size: c.int, format: c.int) -> u32 --- // Load texture cubemap rlLoadTextureCubemap :: proc(data: rawptr, size: c.int, format: c.int) -> u32 --- // Load texture cubemap
rlUpdateTexture :: proc(id: u32, offsetX, offsetY, width, height: c.int, format: c.int, data: rawptr) --- // Update GPU texture with new data rlUpdateTexture :: proc(id: u32, offsetX, offsetY, width, height: c.int, format: c.int, data: rawptr) --- // Update GPU texture with new data
rlGetGlTextureFormats :: proc(format: c.int, glInternalFormat: ^u32, glFormat: ^u32, glType: ^u32) --- // Get OpenGL internal formats rlGetGlTextureFormats :: proc(format: c.int, glInternalFormat: ^u32, glFormat: ^u32, glType: ^u32) --- // Get OpenGL internal formats
rlGetPixelFormatName :: proc(format: PixelFormat) -> cstring --- // Get name string for pixel format
rlUnloadTexture :: proc(id: u32) --- // Unload texture from GPU memory rlUnloadTexture :: proc(id: u32) --- // Unload texture from GPU memory
rlGenerateMipmaps :: proc(texture: ^Texture2D) --- // Generate mipmap data for selected texture rlGenerateMipmaps :: proc(texture: ^Texture2D) --- // Generate mipmap data for selected texture
rlReadTexturePixels :: proc(texture: Texture2D) -> rawptr --- // Read texture pixel data rlReadTexturePixels :: proc(texture: Texture2D) -> rawptr --- // Read texture pixel data
@@ -328,6 +346,24 @@ foreign lib {
rlSetUniformSampler :: proc(locIndex: c.int, textureId: u32) --- // Set shader value sampler rlSetUniformSampler :: proc(locIndex: c.int, textureId: u32) --- // Set shader value sampler
rlSetShader :: proc(shader: Shader) --- // Set shader currently active rlSetShader :: proc(shader: Shader) --- // Set shader currently active
// Compute shader management
rlLoadComputeShaderProgram :: proc(shaderId: u32) -> u32 --- // Load compute shader program
rlComputeShaderDispatch :: proc(groupX, groupY, groupZ: u32) --- // Dispatch compute shader (equivalent to *draw* for graphics pilepine)
// Shader buffer storage object management (ssbo)
rlLoadShaderBuffer :: proc(size: u64, data: rawptr, usageHint: c.int) -> u32 --- // Load shader storage buffer object (SSBO)
rlUnloadShaderBuffer :: proc(ssboId: u32) --- // Unload shader storage buffer object (SSBO)
rlUpdateShaderBufferElements :: proc(id: u32, data: rawptr, dataSize: u64, offset: u64) --- // Update SSBO buffer data
rlGetShaderBufferSize :: proc(id: u32) -> u64 --- // Get SSBO buffer size
rlReadShaderBufferElements :: proc(id: u32, dest: rawptr, count: u64, offset: u64) --- // Bind SSBO buffer
rlBindShaderBuffer :: proc(id: u32, index: u32) --- // Copy SSBO buffer data
// Buffer management
rlCopyBuffersElements :: proc(destId, srcId: u32, destOffset, srcOffset: u64, count: u64) --- // Copy SSBO buffer data
rlBindImageTexture :: proc(id: u32, index: u32, format: u32, readonly: b32) --- // Bind image texture
// Matrix state management // Matrix state management
rlGetMatrixModelview :: proc() -> Matrix --- // Get internal modelview matrix rlGetMatrixModelview :: proc() -> Matrix --- // Get internal modelview matrix
rlGetMatrixProjection :: proc() -> Matrix --- // Get internal projection matrix rlGetMatrixProjection :: proc() -> Matrix --- // Get internal projection matrix