mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-06 03:31:38 -07:00
Compare commits
204 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f0a30e36e | |||
| 517c8ff1dd | |||
| 2b07afaf70 | |||
| 6616882708 | |||
| c9c197ba08 | |||
| 7876660d8c | |||
| db9326f31d | |||
| 27106dd9ae | |||
| 33dc12a61a | |||
| ffd7ca57f1 | |||
| 44897b5eac | |||
| 8255481204 | |||
| 1e453cf1d7 | |||
| c34a331696 | |||
| 07ec93bfeb | |||
| 994ee5a559 | |||
| 50057b0696 | |||
| 00597127dd | |||
| 70d4bc8573 | |||
| bc775afccb | |||
| 504ea7deeb | |||
| 5e2280a787 | |||
| 84e03421d3 | |||
| 0a87ffe0e6 | |||
| e5f961b48f | |||
| 5db505c42f | |||
| 275241f9b4 | |||
| 9246e89c4a | |||
| b56964e465 | |||
| 2e89585c8c | |||
| e230b7110c | |||
| a55f0cfb63 | |||
| de435c9318 | |||
| f40f12d480 | |||
| 8a2c829e07 | |||
| 42b9ce636f | |||
| ca6951d05e | |||
| 446f1f6183 | |||
| d424c84bf9 | |||
| 56d2bbc5b9 | |||
| 2c7bf87998 | |||
| daebaa8b50 | |||
| 9320a31f4d | |||
| 3e04b45106 | |||
| acd5878d66 | |||
| 4439d59105 | |||
| 12c1291805 | |||
| 61bc963e92 | |||
| ae59f214ee | |||
| 6bafa21bee | |||
| 61c581baeb | |||
| 6c4c9aef61 | |||
| 7bed317636 | |||
| 4647081f49 | |||
| 64db286582 | |||
| 1a7a6a9116 | |||
| d1e76ee4f2 | |||
| 9be0d18e5d | |||
| e877525073 | |||
| f09638318f | |||
| bb7703fcec | |||
| 1b28226a67 | |||
| 2b546a598c | |||
| b530ca9a5e | |||
| d232796149 | |||
| e721f26a76 | |||
| 91408cb21f | |||
| eb8b0d7a03 | |||
| 880af47ae7 | |||
| 91949b0992 | |||
| 6a101e69a2 | |||
| 1823b0cead | |||
| 1ec0b79345 | |||
| e814a3693f | |||
| f55fc4cd08 | |||
| f47311f2f6 | |||
| 3f038428a7 | |||
| b9701340b8 | |||
| 82110bf487 | |||
| a75dc9d86d | |||
| bfa23f1352 | |||
| c430a82721 | |||
| cc316a473e | |||
| c213274607 | |||
| c4a2580dfd | |||
| 8a547b5922 | |||
| c67c0789eb | |||
| cefe312ba1 | |||
| d8b1523161 | |||
| 61b02adc50 | |||
| 989ddbd688 | |||
| 96b670af49 | |||
| 359e02bad7 | |||
| 8aadcacc0b | |||
| 615efc7c86 | |||
| dd88104a81 | |||
| 5cb23725ae | |||
| 8c5c45a04c | |||
| 4a552e6326 | |||
| 1f0758708f | |||
| b8dec4268d | |||
| fc920a630f | |||
| ffeac8895d | |||
| cef9632607 | |||
| 76054dddb7 | |||
| 9dc8753a14 | |||
| a805d9a721 | |||
| 6c306f7633 | |||
| 05a86d5296 | |||
| 9422fd311f | |||
| 80360f3f51 | |||
| 321d93bff1 | |||
| 600d19c51b | |||
| ed933bca19 | |||
| a9ea590d24 | |||
| 275d39b59b | |||
| c24454ae70 | |||
| fbc38c78eb | |||
| b0db90de96 | |||
| eb96f9677e | |||
| 0a3b75c5f5 | |||
| 50562440bf | |||
| ce90c3c9ee | |||
| d4bdcd55e1 | |||
| 3f90faf0c9 | |||
| 3d35c5ceb1 | |||
| a674e842d0 | |||
| 23f0fbc376 | |||
| c63f4d68c8 | |||
| 518460af66 | |||
| 39f652de47 | |||
| 483afe462b | |||
| 1296fabe2c | |||
| dc2edd3e79 | |||
| e9c903f1ea | |||
| 83be954efd | |||
| f84bdee1ba | |||
| 5b074ceee5 | |||
| 40eed29527 | |||
| 3d3785a7f1 | |||
| 5df15b5724 | |||
| ee259e4229 | |||
| 36985f8da0 | |||
| eb0faf9602 | |||
| 899cc71990 | |||
| 7be18b4a80 | |||
| 0c9bb9d920 | |||
| 26e3daf5ad | |||
| 0af69f8cda | |||
| 86e26c9a44 | |||
| 541beb615b | |||
| 6646348e1a | |||
| c38d6dc959 | |||
| 924faa58b4 | |||
| 6be104e521 | |||
| e95204908a | |||
| e963fc4d6a | |||
| 1a75a71403 | |||
| 439fc86740 | |||
| 0010e882a7 | |||
| a022f18015 | |||
| cee9561259 | |||
| 3d0cd6f0dc | |||
| adb5928767 | |||
| 23c74bc67b | |||
| a22120fe94 | |||
| ae25eaf10c | |||
| adcfca966e | |||
| d8e34bd9b7 | |||
| 68046d0c08 | |||
| bc2bf1caeb | |||
| d551144841 | |||
| 84540d7aa2 | |||
| 57eedfc4f4 | |||
| 2718ade2bc | |||
| 95f36d4fa5 | |||
| 3accf4048e | |||
| eb05879148 | |||
| a882118c56 | |||
| 57d15ac6e7 | |||
| e3cfdf6982 | |||
| 017fe10762 | |||
| 7bb7a741c6 | |||
| 14351c5bf2 | |||
| 7ef3c87dbb | |||
| b2a2aa15c2 | |||
| 1ec2f8d537 | |||
| 6ded538546 | |||
| 0d1bc05419 | |||
| db2d7a4fdb | |||
| 3fa7dabaa8 | |||
| 1980f32bd6 | |||
| 9ab71ca0da | |||
| 3d06dddb72 | |||
| 9896205a06 | |||
| 8a626ef564 | |||
| 8429943569 | |||
| edd12d505d | |||
| 69f978f22b | |||
| 229c98309e | |||
| c2665462e5 | |||
| 73648bb2d8 | |||
| ba0daaa706 | |||
| dcc5697a48 |
@@ -11,8 +11,8 @@ assignees: ''
|
||||
|
||||
Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
|
||||
|
||||
* Operating System:
|
||||
* Please paste `odin version` output:
|
||||
* Operating System & Odin Version:
|
||||
* Please paste `odin report` output:
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM
|
||||
run: sudo apt-get install llvm-11 clang-11 llvm
|
||||
- name: Download LLVM, botan
|
||||
run: sudo apt-get install llvm-11 clang-11 llvm libbotan-2-dev botan
|
||||
- name: build odin
|
||||
run: make release
|
||||
- name: Odin version
|
||||
@@ -30,13 +30,18 @@ jobs:
|
||||
cd tests/core
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Vendor library tests
|
||||
run: |
|
||||
cd tests/vendor
|
||||
make
|
||||
timeout-minutes: 10
|
||||
build_macOS:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
- name: Download LLVM, botan and setup PATH
|
||||
run: |
|
||||
brew install llvm@11
|
||||
brew install llvm@11 botan
|
||||
echo "/usr/local/opt/llvm@11/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||
@@ -57,6 +62,16 @@ jobs:
|
||||
- name: Odin run -debug
|
||||
run: ./odin run examples/demo/demo.odin -debug
|
||||
timeout-minutes: 10
|
||||
- name: Core library tests
|
||||
run: |
|
||||
cd tests/core
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Vendor library tests
|
||||
run: |
|
||||
cd tests/vendor
|
||||
make
|
||||
timeout-minutes: 10
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
@@ -97,6 +112,13 @@ jobs:
|
||||
cd tests\core
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: Vendor library tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\vendor
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: core:math/big tests
|
||||
shell: cmd
|
||||
run: |
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
NASM is now licensed under the 2-clause BSD license, also known as the
|
||||
simplified BSD license.
|
||||
|
||||
Copyright 1996-2010 the NASM Authors - All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following
|
||||
conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Binary file not shown.
Binary file not shown.
@@ -79,4 +79,4 @@ if %release_mode% EQU 0 odin run examples/demo
|
||||
|
||||
del *.obj > NUL 2> NUL
|
||||
|
||||
:end_of_build
|
||||
:end_of_build
|
||||
@@ -4,6 +4,8 @@ package libc
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == "darwin" {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package libc
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == "darwin" {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ package libc
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == "darwin" {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -38,6 +40,20 @@ when ODIN_OS == "windows" {
|
||||
ERANGE :: 34
|
||||
}
|
||||
|
||||
when ODIN_OS == "darwin" {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="__error")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
// Unknown
|
||||
EDOM :: 33
|
||||
EILSEQ :: 92
|
||||
ERANGE :: 34
|
||||
}
|
||||
|
||||
// Odin has no way to make an identifier "errno" behave as a function call to
|
||||
// read the value, or to produce an lvalue such that you can assign a different
|
||||
// error value to errno. To work around this, just expose it as a function like
|
||||
|
||||
@@ -6,6 +6,8 @@ import "core:intrinsics"
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == "darwin" {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
@@ -4,10 +4,11 @@ package libc
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == "darwin" {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
|
||||
+16
-1
@@ -4,6 +4,8 @@ package libc
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == "darwin" {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -32,7 +34,20 @@ when ODIN_OS == "windows" {
|
||||
SIGTERM :: 15
|
||||
}
|
||||
|
||||
when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" {
|
||||
when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
|
||||
SIG_ERR :: rawptr(~uintptr(0))
|
||||
SIG_DFL :: rawptr(uintptr(0))
|
||||
SIG_IGN :: rawptr(uintptr(1))
|
||||
|
||||
SIGABRT :: 6
|
||||
SIGFPE :: 8
|
||||
SIGILL :: 4
|
||||
SIGINT :: 2
|
||||
SIGSEGV :: 11
|
||||
SIGTERM :: 15
|
||||
}
|
||||
|
||||
when ODIN_OS == "darwin" {
|
||||
SIG_ERR :: rawptr(~uintptr(0))
|
||||
SIG_DFL :: rawptr(uintptr(0))
|
||||
SIG_IGN :: rawptr(uintptr(1))
|
||||
|
||||
+33
-1
@@ -2,6 +2,8 @@ package libc
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == "darwin" {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -67,7 +69,7 @@ when ODIN_OS == "linux" {
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
|
||||
TMP_MAX :: 10000
|
||||
TMP_MAX :: 308915776
|
||||
|
||||
foreign libc {
|
||||
stderr: ^FILE
|
||||
@@ -76,6 +78,36 @@ when ODIN_OS == "linux" {
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == "darwin" {
|
||||
fpos_t :: distinct i64
|
||||
|
||||
_IOFBF :: 0
|
||||
_IOLBF :: 1
|
||||
_IONBF :: 2
|
||||
|
||||
BUFSIZ :: 1024
|
||||
|
||||
EOF :: int(-1)
|
||||
|
||||
FOPEN_MAX :: 20
|
||||
|
||||
FILENAME_MAX :: 1024
|
||||
|
||||
L_tmpnam :: 1024
|
||||
|
||||
SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
|
||||
TMP_MAX :: 308915776
|
||||
|
||||
foreign libc {
|
||||
@(link_name="__stderrp") stderr: ^FILE
|
||||
@(link_name="__stdinp") stdin: ^FILE
|
||||
@(link_name="__stdoutp") stdout: ^FILE
|
||||
}
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.21.4 Operations on files
|
||||
|
||||
+19
-1
@@ -4,6 +4,8 @@ package libc
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == "darwin" {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -33,7 +35,23 @@ when ODIN_OS == "linux" {
|
||||
}
|
||||
|
||||
MB_CUR_MAX :: #force_inline proc() -> size_t {
|
||||
return __ctype_get_mb_cur_max()
|
||||
return size_t(__ctype_get_mb_cur_max())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
when ODIN_OS == "darwin" {
|
||||
RAND_MAX :: 0x7fffffff
|
||||
|
||||
// GLIBC and MUSL only
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
___mb_cur_max :: proc() -> int ---
|
||||
}
|
||||
|
||||
MB_CUR_MAX :: #force_inline proc() -> size_t {
|
||||
return size_t(___mb_cur_max())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import "core:runtime"
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == "darwin" {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
@@ -136,3 +136,8 @@ when ODIN_OS == "linux" {
|
||||
tss_set :: proc(key: tss_t, val: rawptr) -> int ---
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
when ODIN_OS == "darwin" {
|
||||
// TODO: find out what this is meant to be!
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ package libc
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == "darwin" {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -43,7 +45,7 @@ when ODIN_OS == "windows" {
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
|
||||
when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" {
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.27.2 Time manipulation functions
|
||||
@@ -75,7 +77,7 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
|
||||
|
||||
tm :: struct {
|
||||
tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday, tm_isdst: int,
|
||||
_: long,
|
||||
_: rawptr,
|
||||
tm_gmtoff: long,
|
||||
tm_zone: rawptr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ package libc
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == "darwin" {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ package libc
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == "darwin" {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ package libc
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == "darwin" {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -14,10 +16,15 @@ when ODIN_OS == "windows" {
|
||||
}
|
||||
|
||||
when ODIN_OS == "linux" {
|
||||
wctrans_t :: distinct rawptr
|
||||
wctrans_t :: distinct intptr_t
|
||||
wctype_t :: distinct ulong
|
||||
}
|
||||
|
||||
when ODIN_OS == "darwin" {
|
||||
wctrans_t :: distinct int
|
||||
wctype_t :: distinct u32
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.30.2.1 Wide character classification functions
|
||||
|
||||
+35
-43
@@ -2,48 +2,43 @@
|
||||
A crypto library for the Odin language
|
||||
|
||||
## Supported
|
||||
This library offers various algorithms available in either native Odin or via bindings to the [Botan](https://botan.randombit.net/) crypto library.
|
||||
This library offers various algorithms implemented in Odin.
|
||||
Please see the chart below for the options.
|
||||
**Note:** All crypto hash algorithms, offered by [Botan\'s FFI](https://botan.randombit.net/handbook/api_ref/hash.html), have been added.
|
||||
|
||||
## Hashing algorithms
|
||||
| Algorithm | Odin | Botan |
|
||||
|:-------------------------------------------------------------------------------------------------------------|:-----------------|:---------------------|
|
||||
| [BLAKE](https://web.archive.org/web/20190915215948/https://131002.net/blake) | ✔️ | |
|
||||
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ | ✔️ |
|
||||
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ | |
|
||||
| [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | ✔️ | ✔️ |
|
||||
| [Grøstl](http://www.groestl.info/Groestl.zip) | ✔️ | |
|
||||
| [HAVAL](https://web.archive.org/web/20150111210116/http://labs.calyptix.com/haval.php) | ✔️ | |
|
||||
| [JH](https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html) | ✔️ | |
|
||||
| [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ | ✔️ |
|
||||
| [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | ✔️ | |
|
||||
| [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | ✔️ | ✔️ |
|
||||
| [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | ✔️ | ✔️ |
|
||||
| [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | ✔️ | ✔️\* |
|
||||
| [SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | ✔️ | ✔️ |
|
||||
| [SHA-2](https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf) | ✔️ | ✔️ |
|
||||
| [SHA-3](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ | ✔️ |
|
||||
| [SHAKE](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ | ✔️ |
|
||||
| [Skein](https://www.schneier.com/academic/skein/) | | ✔️\*\* |
|
||||
| [SM3](https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02) | ✔️ | ✔️ |
|
||||
| [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | ✔️ | ✔️ |
|
||||
| [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ | ✔️ |
|
||||
| [Tiger2](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ | |
|
||||
| [Whirlpool](https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html) | ✔️ | ✔️ |
|
||||
|
||||
\* Only `RIPEMD-160`
|
||||
\*\* Only `SKEIN-512`
|
||||
| Algorithm | |
|
||||
|:-------------------------------------------------------------------------------------------------------------|:-----------------|
|
||||
| [BLAKE](https://web.archive.org/web/20190915215948/https://131002.net/blake) | ✔️ |
|
||||
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ |
|
||||
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ |
|
||||
| [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | ✔️ |
|
||||
| [Grøstl](http://www.groestl.info/Groestl.zip) | ✔️ |
|
||||
| [HAVAL](https://web.archive.org/web/20150111210116/http://labs.calyptix.com/haval.php) | ✔️ |
|
||||
| [JH](https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html) | ✔️ |
|
||||
| [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
||||
| [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | ✔️ |
|
||||
| [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | ✔️ |
|
||||
| [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | ✔️ |
|
||||
| [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | ✔️ |
|
||||
| [SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | ✔️ |
|
||||
| [SHA-2](https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf) | ✔️ |
|
||||
| [SHA-3](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
||||
| [SHAKE](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
||||
| [SM3](https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02) | ✔️ |
|
||||
| [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | ✔️ |
|
||||
| [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ |
|
||||
| [Tiger2](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ |
|
||||
| [Whirlpool](https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html) | ✔️ |
|
||||
|
||||
#### 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.
|
||||
* `hash_string` - Hash a given string and return the computed hash. Just calls `hash_bytes` internally
|
||||
* `hash_bytes` - Hash a given byte slice and return the computed hash
|
||||
* `hash_stream` - Takes a stream from io.Stream and returns the computed hash from it
|
||||
* `hash_file` - 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.
|
||||
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`.
|
||||
You may also directly call them, if you wish.
|
||||
|
||||
#### Context system
|
||||
The library uses a context system internally to be able to switch between Odin / Botan implementations freely.
|
||||
When an Odin implementation is available, it is the default.
|
||||
You may change what is used during runtime by calling `foo.use_botan()` or `foo.use_odin()`.
|
||||
It is also possible to set this during compile time via `USE_BOTAN_LIB=true`.
|
||||
Internally a vtable is used to set the appropriate procedures when switching. This works for all the procedures mentioned in the APIs above.
|
||||
|
||||
#### Example
|
||||
```odin
|
||||
package crypto_example
|
||||
@@ -67,12 +55,16 @@ import "core:crypto/md4"
|
||||
|
||||
main :: proc() {
|
||||
input := "foo"
|
||||
// Compute the hash via Odin implementation
|
||||
|
||||
// Compute the hash, using the high level API
|
||||
computed_hash := md4.hash(input)
|
||||
// Switch to Botan
|
||||
md4.use_botan()
|
||||
// Compute the hash via Botan bindings
|
||||
computed_hash_botan := md4.hash(input)
|
||||
|
||||
// Compute the hash, using the low level API
|
||||
ctx: md4.Md4_Context
|
||||
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`.
|
||||
|
||||
@@ -6,7 +6,6 @@ package _blake2
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the BLAKE2 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/rfc7693> and <https://www.blake2.net/>
|
||||
*/
|
||||
@@ -76,7 +75,7 @@ BLAKE2B_IV := [8]u64 {
|
||||
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^$T) {
|
||||
init :: proc(ctx: ^$T) {
|
||||
when T == Blake2s_Context {
|
||||
block_size :: BLAKE2S_BLOCK_SIZE
|
||||
} else when T == Blake2b_Context {
|
||||
@@ -139,17 +138,17 @@ init_odin :: proc(ctx: ^$T) {
|
||||
}
|
||||
if len(ctx.cfg.key) > 0 {
|
||||
copy(ctx.padded_key[:], ctx.cfg.key)
|
||||
update_odin(ctx, ctx.padded_key[:])
|
||||
update(ctx, ctx.padded_key[:])
|
||||
ctx.is_keyed = true
|
||||
}
|
||||
copy(ctx.ih[:], ctx.h[:])
|
||||
copy(ctx.h[:], ctx.ih[:])
|
||||
if ctx.is_keyed {
|
||||
update_odin(ctx, ctx.padded_key[:])
|
||||
update(ctx, ctx.padded_key[:])
|
||||
}
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^$T, p: []byte) {
|
||||
update :: proc "contextless" (ctx: ^$T, p: []byte) {
|
||||
p := p
|
||||
when T == Blake2s_Context {
|
||||
block_size :: BLAKE2S_BLOCK_SIZE
|
||||
@@ -161,7 +160,7 @@ update_odin :: proc(ctx: ^$T, p: []byte) {
|
||||
if len(p) > left {
|
||||
copy(ctx.x[ctx.nx:], p[:left])
|
||||
p = p[left:]
|
||||
blake2_blocks(ctx, ctx.x[:])
|
||||
blocks(ctx, ctx.x[:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
if len(p) > block_size {
|
||||
@@ -169,13 +168,22 @@ update_odin :: proc(ctx: ^$T, p: []byte) {
|
||||
if n == len(p) {
|
||||
n -= block_size
|
||||
}
|
||||
blake2_blocks(ctx, p[:n])
|
||||
blocks(ctx, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
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 {
|
||||
for i := 0; i < len(ctx.padded_key); i += 1 {
|
||||
ctx.padded_key[i] = 0
|
||||
@@ -193,7 +201,7 @@ blake2s_final_odin :: proc(ctx: $T, hash: []byte) {
|
||||
ctx.f[1] = 0xffffffff
|
||||
}
|
||||
|
||||
blake2_blocks(ctx, ctx.x[:])
|
||||
blocks(ctx, ctx.x[:])
|
||||
|
||||
j := 0
|
||||
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 {
|
||||
for i := 0; i < len(ctx.padded_key); i += 1 {
|
||||
ctx.padded_key[i] = 0
|
||||
@@ -223,7 +231,7 @@ blake2b_final_odin :: proc(ctx: $T, hash: []byte) {
|
||||
ctx.f[1] = 0xffffffffffffffff
|
||||
}
|
||||
|
||||
blake2_blocks(ctx, ctx.x[:])
|
||||
blocks(ctx, ctx.x[:])
|
||||
|
||||
j := 0
|
||||
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 {
|
||||
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]
|
||||
p := p
|
||||
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
|
||||
}
|
||||
|
||||
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]
|
||||
p := p
|
||||
for len(p) >= BLAKE2B_BLOCK_SIZE {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# fiat
|
||||
|
||||
This package contains low level arithmetic required to implement certain
|
||||
cryptographic primitives, ported from the [fiat-crypto project][1]
|
||||
along with some higher-level helpers.
|
||||
|
||||
## Notes
|
||||
|
||||
fiat-crypto gives the choice of 3 licenses for derived works. The 1-Clause
|
||||
BSD license is chosen as it is compatible with Odin's existing licensing.
|
||||
|
||||
The routines are intended to be timing-safe, as long as the underlying
|
||||
integer arithmetic is constant time. This is true on most systems commonly
|
||||
used today, with the notable exception of WASM.
|
||||
|
||||
While fiat-crypto provides both output targeting both 32-bit and 64-bit
|
||||
architectures, only the 64-bit versions were used, as 32-bit architectures
|
||||
are becoming increasingly uncommon and irrelevant.
|
||||
|
||||
With the current Odin syntax, the Go output is trivially ported in most
|
||||
cases and was used as the basis of the port.
|
||||
|
||||
In the future, it would be better to auto-generate Odin either directly
|
||||
by adding an appropriate code-gen backend written in Coq, or perhaps by
|
||||
parsing the JSON output.
|
||||
|
||||
As this is a port rather than autogenerated output, none of fiat-crypto's
|
||||
formal verification guarantees apply, unless it is possible to prove binary
|
||||
equivalence.
|
||||
|
||||
For the most part, alterations to the base fiat-crypto generated code was
|
||||
kept to a minimum, to aid auditability. This results in a somewhat
|
||||
ideosyncratic style, and in some cases minor performance penalties.
|
||||
|
||||
[1]: https://github.com/mit-plv/fiat-crypto
|
||||
@@ -0,0 +1,24 @@
|
||||
package fiat
|
||||
|
||||
// This package provides various helpers and types common to all of the
|
||||
// fiat-crypto derived backends.
|
||||
|
||||
// This code only works on a two's complement system.
|
||||
#assert((-1 & 3) == 3)
|
||||
|
||||
u1 :: distinct u8
|
||||
i1 :: distinct i8
|
||||
|
||||
cmovznz_u64 :: #force_inline proc "contextless" (arg1: u1, arg2, arg3: u64) -> (out1: u64) {
|
||||
x1 := (u64(arg1) * 0xffffffffffffffff)
|
||||
x2 := ((x1 & arg3) | ((~x1) & arg2))
|
||||
out1 = x2
|
||||
return
|
||||
}
|
||||
|
||||
cmovznz_u32 :: #force_inline proc "contextless" (arg1: u1, arg2, arg3: u32) -> (out1: u32) {
|
||||
x1 := (u32(arg1) * 0xffffffff)
|
||||
x2 := ((x1 & arg3) | ((~x1) & arg2))
|
||||
out1 = x2
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package field_curve25519
|
||||
|
||||
import "core:crypto"
|
||||
import "core:mem"
|
||||
|
||||
fe_relax_cast :: #force_inline proc "contextless" (arg1: ^Tight_Field_Element) -> ^Loose_Field_Element {
|
||||
return transmute(^Loose_Field_Element)(arg1)
|
||||
}
|
||||
|
||||
fe_tighten_cast :: #force_inline proc "contextless" (arg1: ^Loose_Field_Element) -> ^Tight_Field_Element {
|
||||
return transmute(^Tight_Field_Element)(arg1)
|
||||
}
|
||||
|
||||
fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte) {
|
||||
// Ignore the unused bit by copying the input and masking the bit off
|
||||
// prior to deserialization.
|
||||
tmp1: [32]byte = ---
|
||||
copy_slice(tmp1[:], arg1[:])
|
||||
tmp1[31] &= 127
|
||||
|
||||
_fe_from_bytes(out1, &tmp1)
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
}
|
||||
|
||||
fe_equal :: proc "contextless" (arg1, arg2: ^Tight_Field_Element) -> int {
|
||||
tmp2: [32]byte = ---
|
||||
|
||||
fe_to_bytes(&tmp2, arg2)
|
||||
ret := fe_equal_bytes(arg1, &tmp2)
|
||||
|
||||
mem.zero_explicit(&tmp2, size_of(tmp2))
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
fe_equal_bytes :: proc "contextless" (arg1: ^Tight_Field_Element, arg2: ^[32]byte) -> int {
|
||||
tmp1: [32]byte = ---
|
||||
|
||||
fe_to_bytes(&tmp1, arg1)
|
||||
|
||||
ret := crypto.compare_constant_time(tmp1[:], arg2[:])
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
fe_carry_pow2k :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element, arg2: uint) {
|
||||
// Special case: `arg1^(2 * 0) = 1`, though this should never happen.
|
||||
if arg2 == 0 {
|
||||
fe_one(out1)
|
||||
return
|
||||
}
|
||||
|
||||
fe_carry_square(out1, arg1)
|
||||
for _ in 1..<arg2 {
|
||||
fe_carry_square(out1, fe_relax_cast(out1))
|
||||
}
|
||||
}
|
||||
|
||||
fe_carry_opp :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
|
||||
fe_opp(fe_relax_cast(out1), arg1)
|
||||
fe_carry(out1, fe_relax_cast(out1))
|
||||
}
|
||||
|
||||
fe_carry_invsqrt :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) -> int {
|
||||
// Inverse square root taken from Monocypher.
|
||||
|
||||
tmp1, tmp2, tmp3: Tight_Field_Element = ---, ---, ---
|
||||
|
||||
// t0 = x^((p-5)/8)
|
||||
// Can be achieved with a simple double & add ladder,
|
||||
// but it would be slower.
|
||||
fe_carry_pow2k(&tmp1, arg1, 1)
|
||||
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 2)
|
||||
fe_carry_mul(&tmp2, arg1, fe_relax_cast(&tmp2))
|
||||
fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), fe_relax_cast(&tmp2))
|
||||
fe_carry_pow2k(&tmp1, fe_relax_cast(&tmp1), 1)
|
||||
fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
|
||||
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 5)
|
||||
fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
|
||||
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 10)
|
||||
fe_carry_mul(&tmp2, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
|
||||
fe_carry_pow2k(&tmp3, fe_relax_cast(&tmp2), 20)
|
||||
fe_carry_mul(&tmp2, fe_relax_cast(&tmp3), fe_relax_cast(&tmp2))
|
||||
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp2), 10)
|
||||
fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
|
||||
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 50)
|
||||
fe_carry_mul(&tmp2, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
|
||||
fe_carry_pow2k(&tmp3, fe_relax_cast(&tmp2), 100)
|
||||
fe_carry_mul(&tmp2, fe_relax_cast(&tmp3), fe_relax_cast(&tmp2))
|
||||
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp2), 50)
|
||||
fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
|
||||
fe_carry_pow2k(&tmp1, fe_relax_cast(&tmp1), 2)
|
||||
fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), arg1)
|
||||
|
||||
// quartic = x^((p-1)/4)
|
||||
quartic := &tmp2
|
||||
fe_carry_square(quartic, fe_relax_cast(&tmp1))
|
||||
fe_carry_mul(quartic, fe_relax_cast(quartic), arg1)
|
||||
|
||||
// Serialize quartic once to save on repeated serialization/sanitization.
|
||||
quartic_buf: [32]byte = ---
|
||||
fe_to_bytes(&quartic_buf, quartic)
|
||||
check := &tmp3
|
||||
|
||||
fe_one(check)
|
||||
p1 := fe_equal_bytes(check, &quartic_buf)
|
||||
fe_carry_opp(check, check)
|
||||
m1 := fe_equal_bytes(check, &quartic_buf)
|
||||
fe_carry_opp(check, &SQRT_M1)
|
||||
ms := fe_equal_bytes(check, &quartic_buf)
|
||||
|
||||
// if quartic == -1 or sqrt(-1)
|
||||
// then isr = x^((p-1)/4) * sqrt(-1)
|
||||
// else isr = x^((p-1)/4)
|
||||
fe_carry_mul(out1, fe_relax_cast(&tmp1), fe_relax_cast(&SQRT_M1))
|
||||
fe_cond_assign(out1, &tmp1, (m1|ms) ~ 1)
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
mem.zero_explicit(&tmp2, size_of(tmp2))
|
||||
mem.zero_explicit(&tmp3, size_of(tmp3))
|
||||
mem.zero_explicit(&quartic_buf, size_of(quartic_buf))
|
||||
|
||||
return p1 | m1
|
||||
}
|
||||
|
||||
fe_carry_inv :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
tmp1: Tight_Field_Element
|
||||
|
||||
fe_carry_square(&tmp1, arg1)
|
||||
_ = fe_carry_invsqrt(&tmp1, fe_relax_cast(&tmp1))
|
||||
fe_carry_square(&tmp1, fe_relax_cast(&tmp1))
|
||||
fe_carry_mul(out1, fe_relax_cast(&tmp1), arg1)
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
}
|
||||
@@ -0,0 +1,616 @@
|
||||
// The BSD 1-Clause License (BSD-1-Clause)
|
||||
//
|
||||
// Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file)
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY the fiat-crypto authors "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design,
|
||||
// Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package field_curve25519
|
||||
|
||||
// The file provides arithmetic on the field Z/(2^255-19) using
|
||||
// unsaturated 64-bit integer arithmetic. It is derived primarily
|
||||
// from the machine generated Golang output from the fiat-crypto project.
|
||||
//
|
||||
// While the base implementation is provably correct, this implementation
|
||||
// makes no such claims as the port and optimizations were done by hand.
|
||||
// At some point, it may be worth adding support to fiat-crypto for
|
||||
// generating Odin output.
|
||||
//
|
||||
// TODO:
|
||||
// * When fiat-crypto supports it, using a saturated 64-bit limbs
|
||||
// instead of 51-bit limbs will be faster, though the gains are
|
||||
// minimal unless adcx/adox/mulx are used.
|
||||
|
||||
import fiat "core:crypto/_fiat"
|
||||
import "core:math/bits"
|
||||
|
||||
Loose_Field_Element :: distinct [5]u64
|
||||
Tight_Field_Element :: distinct [5]u64
|
||||
|
||||
SQRT_M1 := Tight_Field_Element{
|
||||
1718705420411056,
|
||||
234908883556509,
|
||||
2233514472574048,
|
||||
2117202627021982,
|
||||
765476049583133,
|
||||
}
|
||||
|
||||
_addcarryx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
|
||||
x1 := ((u64(arg1) + arg2) + arg3)
|
||||
x2 := (x1 & 0x7ffffffffffff)
|
||||
x3 := fiat.u1((x1 >> 51))
|
||||
out1 = x2
|
||||
out2 = x3
|
||||
return
|
||||
}
|
||||
|
||||
_subborrowx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
|
||||
x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
|
||||
x2 := fiat.i1((x1 >> 51))
|
||||
x3 := (u64(x1) & 0x7ffffffffffff)
|
||||
out1 = x3
|
||||
out2 = (0x0 - fiat.u1(x2))
|
||||
return
|
||||
}
|
||||
|
||||
fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
|
||||
x2, x1 := bits.mul_u64(arg1[4], (arg2[4] * 0x13))
|
||||
x4, x3 := bits.mul_u64(arg1[4], (arg2[3] * 0x13))
|
||||
x6, x5 := bits.mul_u64(arg1[4], (arg2[2] * 0x13))
|
||||
x8, x7 := bits.mul_u64(arg1[4], (arg2[1] * 0x13))
|
||||
x10, x9 := bits.mul_u64(arg1[3], (arg2[4] * 0x13))
|
||||
x12, x11 := bits.mul_u64(arg1[3], (arg2[3] * 0x13))
|
||||
x14, x13 := bits.mul_u64(arg1[3], (arg2[2] * 0x13))
|
||||
x16, x15 := bits.mul_u64(arg1[2], (arg2[4] * 0x13))
|
||||
x18, x17 := bits.mul_u64(arg1[2], (arg2[3] * 0x13))
|
||||
x20, x19 := bits.mul_u64(arg1[1], (arg2[4] * 0x13))
|
||||
x22, x21 := bits.mul_u64(arg1[4], arg2[0])
|
||||
x24, x23 := bits.mul_u64(arg1[3], arg2[1])
|
||||
x26, x25 := bits.mul_u64(arg1[3], arg2[0])
|
||||
x28, x27 := bits.mul_u64(arg1[2], arg2[2])
|
||||
x30, x29 := bits.mul_u64(arg1[2], arg2[1])
|
||||
x32, x31 := bits.mul_u64(arg1[2], arg2[0])
|
||||
x34, x33 := bits.mul_u64(arg1[1], arg2[3])
|
||||
x36, x35 := bits.mul_u64(arg1[1], arg2[2])
|
||||
x38, x37 := bits.mul_u64(arg1[1], arg2[1])
|
||||
x40, x39 := bits.mul_u64(arg1[1], arg2[0])
|
||||
x42, x41 := bits.mul_u64(arg1[0], arg2[4])
|
||||
x44, x43 := bits.mul_u64(arg1[0], arg2[3])
|
||||
x46, x45 := bits.mul_u64(arg1[0], arg2[2])
|
||||
x48, x47 := bits.mul_u64(arg1[0], arg2[1])
|
||||
x50, x49 := bits.mul_u64(arg1[0], arg2[0])
|
||||
x51, x52 := bits.add_u64(x13, x7, u64(0x0))
|
||||
x53, _ := bits.add_u64(x14, x8, u64(fiat.u1(x52)))
|
||||
x55, x56 := bits.add_u64(x17, x51, u64(0x0))
|
||||
x57, _ := bits.add_u64(x18, x53, u64(fiat.u1(x56)))
|
||||
x59, x60 := bits.add_u64(x19, x55, u64(0x0))
|
||||
x61, _ := bits.add_u64(x20, x57, u64(fiat.u1(x60)))
|
||||
x63, x64 := bits.add_u64(x49, x59, u64(0x0))
|
||||
x65, _ := bits.add_u64(x50, x61, u64(fiat.u1(x64)))
|
||||
x67 := ((x63 >> 51) | ((x65 << 13) & 0xffffffffffffffff))
|
||||
x68 := (x63 & 0x7ffffffffffff)
|
||||
x69, x70 := bits.add_u64(x23, x21, u64(0x0))
|
||||
x71, _ := bits.add_u64(x24, x22, u64(fiat.u1(x70)))
|
||||
x73, x74 := bits.add_u64(x27, x69, u64(0x0))
|
||||
x75, _ := bits.add_u64(x28, x71, u64(fiat.u1(x74)))
|
||||
x77, x78 := bits.add_u64(x33, x73, u64(0x0))
|
||||
x79, _ := bits.add_u64(x34, x75, u64(fiat.u1(x78)))
|
||||
x81, x82 := bits.add_u64(x41, x77, u64(0x0))
|
||||
x83, _ := bits.add_u64(x42, x79, u64(fiat.u1(x82)))
|
||||
x85, x86 := bits.add_u64(x25, x1, u64(0x0))
|
||||
x87, _ := bits.add_u64(x26, x2, u64(fiat.u1(x86)))
|
||||
x89, x90 := bits.add_u64(x29, x85, u64(0x0))
|
||||
x91, _ := bits.add_u64(x30, x87, u64(fiat.u1(x90)))
|
||||
x93, x94 := bits.add_u64(x35, x89, u64(0x0))
|
||||
x95, _ := bits.add_u64(x36, x91, u64(fiat.u1(x94)))
|
||||
x97, x98 := bits.add_u64(x43, x93, u64(0x0))
|
||||
x99, _ := bits.add_u64(x44, x95, u64(fiat.u1(x98)))
|
||||
x101, x102 := bits.add_u64(x9, x3, u64(0x0))
|
||||
x103, _ := bits.add_u64(x10, x4, u64(fiat.u1(x102)))
|
||||
x105, x106 := bits.add_u64(x31, x101, u64(0x0))
|
||||
x107, _ := bits.add_u64(x32, x103, u64(fiat.u1(x106)))
|
||||
x109, x110 := bits.add_u64(x37, x105, u64(0x0))
|
||||
x111, _ := bits.add_u64(x38, x107, u64(fiat.u1(x110)))
|
||||
x113, x114 := bits.add_u64(x45, x109, u64(0x0))
|
||||
x115, _ := bits.add_u64(x46, x111, u64(fiat.u1(x114)))
|
||||
x117, x118 := bits.add_u64(x11, x5, u64(0x0))
|
||||
x119, _ := bits.add_u64(x12, x6, u64(fiat.u1(x118)))
|
||||
x121, x122 := bits.add_u64(x15, x117, u64(0x0))
|
||||
x123, _ := bits.add_u64(x16, x119, u64(fiat.u1(x122)))
|
||||
x125, x126 := bits.add_u64(x39, x121, u64(0x0))
|
||||
x127, _ := bits.add_u64(x40, x123, u64(fiat.u1(x126)))
|
||||
x129, x130 := bits.add_u64(x47, x125, u64(0x0))
|
||||
x131, _ := bits.add_u64(x48, x127, u64(fiat.u1(x130)))
|
||||
x133, x134 := bits.add_u64(x67, x129, u64(0x0))
|
||||
x135 := (u64(fiat.u1(x134)) + x131)
|
||||
x136 := ((x133 >> 51) | ((x135 << 13) & 0xffffffffffffffff))
|
||||
x137 := (x133 & 0x7ffffffffffff)
|
||||
x138, x139 := bits.add_u64(x136, x113, u64(0x0))
|
||||
x140 := (u64(fiat.u1(x139)) + x115)
|
||||
x141 := ((x138 >> 51) | ((x140 << 13) & 0xffffffffffffffff))
|
||||
x142 := (x138 & 0x7ffffffffffff)
|
||||
x143, x144 := bits.add_u64(x141, x97, u64(0x0))
|
||||
x145 := (u64(fiat.u1(x144)) + x99)
|
||||
x146 := ((x143 >> 51) | ((x145 << 13) & 0xffffffffffffffff))
|
||||
x147 := (x143 & 0x7ffffffffffff)
|
||||
x148, x149 := bits.add_u64(x146, x81, u64(0x0))
|
||||
x150 := (u64(fiat.u1(x149)) + x83)
|
||||
x151 := ((x148 >> 51) | ((x150 << 13) & 0xffffffffffffffff))
|
||||
x152 := (x148 & 0x7ffffffffffff)
|
||||
x153 := (x151 * 0x13)
|
||||
x154 := (x68 + x153)
|
||||
x155 := (x154 >> 51)
|
||||
x156 := (x154 & 0x7ffffffffffff)
|
||||
x157 := (x155 + x137)
|
||||
x158 := fiat.u1((x157 >> 51))
|
||||
x159 := (x157 & 0x7ffffffffffff)
|
||||
x160 := (u64(x158) + x142)
|
||||
out1[0] = x156
|
||||
out1[1] = x159
|
||||
out1[2] = x160
|
||||
out1[3] = x147
|
||||
out1[4] = x152
|
||||
}
|
||||
|
||||
fe_carry_square :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
x1 := (arg1[4] * 0x13)
|
||||
x2 := (x1 * 0x2)
|
||||
x3 := (arg1[4] * 0x2)
|
||||
x4 := (arg1[3] * 0x13)
|
||||
x5 := (x4 * 0x2)
|
||||
x6 := (arg1[3] * 0x2)
|
||||
x7 := (arg1[2] * 0x2)
|
||||
x8 := (arg1[1] * 0x2)
|
||||
x10, x9 := bits.mul_u64(arg1[4], x1)
|
||||
x12, x11 := bits.mul_u64(arg1[3], x2)
|
||||
x14, x13 := bits.mul_u64(arg1[3], x4)
|
||||
x16, x15 := bits.mul_u64(arg1[2], x2)
|
||||
x18, x17 := bits.mul_u64(arg1[2], x5)
|
||||
x20, x19 := bits.mul_u64(arg1[2], arg1[2])
|
||||
x22, x21 := bits.mul_u64(arg1[1], x2)
|
||||
x24, x23 := bits.mul_u64(arg1[1], x6)
|
||||
x26, x25 := bits.mul_u64(arg1[1], x7)
|
||||
x28, x27 := bits.mul_u64(arg1[1], arg1[1])
|
||||
x30, x29 := bits.mul_u64(arg1[0], x3)
|
||||
x32, x31 := bits.mul_u64(arg1[0], x6)
|
||||
x34, x33 := bits.mul_u64(arg1[0], x7)
|
||||
x36, x35 := bits.mul_u64(arg1[0], x8)
|
||||
x38, x37 := bits.mul_u64(arg1[0], arg1[0])
|
||||
x39, x40 := bits.add_u64(x21, x17, u64(0x0))
|
||||
x41, _ := bits.add_u64(x22, x18, u64(fiat.u1(x40)))
|
||||
x43, x44 := bits.add_u64(x37, x39, u64(0x0))
|
||||
x45, _ := bits.add_u64(x38, x41, u64(fiat.u1(x44)))
|
||||
x47 := ((x43 >> 51) | ((x45 << 13) & 0xffffffffffffffff))
|
||||
x48 := (x43 & 0x7ffffffffffff)
|
||||
x49, x50 := bits.add_u64(x23, x19, u64(0x0))
|
||||
x51, _ := bits.add_u64(x24, x20, u64(fiat.u1(x50)))
|
||||
x53, x54 := bits.add_u64(x29, x49, u64(0x0))
|
||||
x55, _ := bits.add_u64(x30, x51, u64(fiat.u1(x54)))
|
||||
x57, x58 := bits.add_u64(x25, x9, u64(0x0))
|
||||
x59, _ := bits.add_u64(x26, x10, u64(fiat.u1(x58)))
|
||||
x61, x62 := bits.add_u64(x31, x57, u64(0x0))
|
||||
x63, _ := bits.add_u64(x32, x59, u64(fiat.u1(x62)))
|
||||
x65, x66 := bits.add_u64(x27, x11, u64(0x0))
|
||||
x67, _ := bits.add_u64(x28, x12, u64(fiat.u1(x66)))
|
||||
x69, x70 := bits.add_u64(x33, x65, u64(0x0))
|
||||
x71, _ := bits.add_u64(x34, x67, u64(fiat.u1(x70)))
|
||||
x73, x74 := bits.add_u64(x15, x13, u64(0x0))
|
||||
x75, _ := bits.add_u64(x16, x14, u64(fiat.u1(x74)))
|
||||
x77, x78 := bits.add_u64(x35, x73, u64(0x0))
|
||||
x79, _ := bits.add_u64(x36, x75, u64(fiat.u1(x78)))
|
||||
x81, x82 := bits.add_u64(x47, x77, u64(0x0))
|
||||
x83 := (u64(fiat.u1(x82)) + x79)
|
||||
x84 := ((x81 >> 51) | ((x83 << 13) & 0xffffffffffffffff))
|
||||
x85 := (x81 & 0x7ffffffffffff)
|
||||
x86, x87 := bits.add_u64(x84, x69, u64(0x0))
|
||||
x88 := (u64(fiat.u1(x87)) + x71)
|
||||
x89 := ((x86 >> 51) | ((x88 << 13) & 0xffffffffffffffff))
|
||||
x90 := (x86 & 0x7ffffffffffff)
|
||||
x91, x92 := bits.add_u64(x89, x61, u64(0x0))
|
||||
x93 := (u64(fiat.u1(x92)) + x63)
|
||||
x94 := ((x91 >> 51) | ((x93 << 13) & 0xffffffffffffffff))
|
||||
x95 := (x91 & 0x7ffffffffffff)
|
||||
x96, x97 := bits.add_u64(x94, x53, u64(0x0))
|
||||
x98 := (u64(fiat.u1(x97)) + x55)
|
||||
x99 := ((x96 >> 51) | ((x98 << 13) & 0xffffffffffffffff))
|
||||
x100 := (x96 & 0x7ffffffffffff)
|
||||
x101 := (x99 * 0x13)
|
||||
x102 := (x48 + x101)
|
||||
x103 := (x102 >> 51)
|
||||
x104 := (x102 & 0x7ffffffffffff)
|
||||
x105 := (x103 + x85)
|
||||
x106 := fiat.u1((x105 >> 51))
|
||||
x107 := (x105 & 0x7ffffffffffff)
|
||||
x108 := (u64(x106) + x90)
|
||||
out1[0] = x104
|
||||
out1[1] = x107
|
||||
out1[2] = x108
|
||||
out1[3] = x95
|
||||
out1[4] = x100
|
||||
}
|
||||
|
||||
fe_carry :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := ((x1 >> 51) + arg1[1])
|
||||
x3 := ((x2 >> 51) + arg1[2])
|
||||
x4 := ((x3 >> 51) + arg1[3])
|
||||
x5 := ((x4 >> 51) + arg1[4])
|
||||
x6 := ((x1 & 0x7ffffffffffff) + ((x5 >> 51) * 0x13))
|
||||
x7 := (u64(fiat.u1((x6 >> 51))) + (x2 & 0x7ffffffffffff))
|
||||
x8 := (x6 & 0x7ffffffffffff)
|
||||
x9 := (x7 & 0x7ffffffffffff)
|
||||
x10 := (u64(fiat.u1((x7 >> 51))) + (x3 & 0x7ffffffffffff))
|
||||
x11 := (x4 & 0x7ffffffffffff)
|
||||
x12 := (x5 & 0x7ffffffffffff)
|
||||
out1[0] = x8
|
||||
out1[1] = x9
|
||||
out1[2] = x10
|
||||
out1[3] = x11
|
||||
out1[4] = x12
|
||||
}
|
||||
|
||||
fe_add :: proc "contextless" (out1: ^Loose_Field_Element, arg1, arg2: ^Tight_Field_Element) {
|
||||
x1 := (arg1[0] + arg2[0])
|
||||
x2 := (arg1[1] + arg2[1])
|
||||
x3 := (arg1[2] + arg2[2])
|
||||
x4 := (arg1[3] + arg2[3])
|
||||
x5 := (arg1[4] + arg2[4])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
fe_sub :: proc "contextless" (out1: ^Loose_Field_Element, arg1, arg2: ^Tight_Field_Element) {
|
||||
x1 := ((0xfffffffffffda + arg1[0]) - arg2[0])
|
||||
x2 := ((0xffffffffffffe + arg1[1]) - arg2[1])
|
||||
x3 := ((0xffffffffffffe + arg1[2]) - arg2[2])
|
||||
x4 := ((0xffffffffffffe + arg1[3]) - arg2[3])
|
||||
x5 := ((0xffffffffffffe + arg1[4]) - arg2[4])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Element) {
|
||||
x1 := (0xfffffffffffda - arg1[0])
|
||||
x2 := (0xffffffffffffe - arg1[1])
|
||||
x3 := (0xffffffffffffe - arg1[2])
|
||||
x4 := (0xffffffffffffe - arg1[3])
|
||||
x5 := (0xffffffffffffe - arg1[4])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
fe_cond_assign :: proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: int) {
|
||||
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
|
||||
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
|
||||
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
|
||||
x4 := fiat.cmovznz_u64(fiat.u1(arg2), out1[3], arg1[3])
|
||||
x5 := fiat.cmovznz_u64(fiat.u1(arg2), out1[4], arg1[4])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
fe_to_bytes :: proc "contextless" (out1: ^[32]byte, arg1: ^Tight_Field_Element) {
|
||||
x1, x2 := _subborrowx_u51(0x0, arg1[0], 0x7ffffffffffed)
|
||||
x3, x4 := _subborrowx_u51(x2, arg1[1], 0x7ffffffffffff)
|
||||
x5, x6 := _subborrowx_u51(x4, arg1[2], 0x7ffffffffffff)
|
||||
x7, x8 := _subborrowx_u51(x6, arg1[3], 0x7ffffffffffff)
|
||||
x9, x10 := _subborrowx_u51(x8, arg1[4], 0x7ffffffffffff)
|
||||
x11 := fiat.cmovznz_u64(x10, u64(0x0), 0xffffffffffffffff)
|
||||
x12, x13 := _addcarryx_u51(0x0, x1, (x11 & 0x7ffffffffffed))
|
||||
x14, x15 := _addcarryx_u51(x13, x3, (x11 & 0x7ffffffffffff))
|
||||
x16, x17 := _addcarryx_u51(x15, x5, (x11 & 0x7ffffffffffff))
|
||||
x18, x19 := _addcarryx_u51(x17, x7, (x11 & 0x7ffffffffffff))
|
||||
x20, _ := _addcarryx_u51(x19, x9, (x11 & 0x7ffffffffffff))
|
||||
x22 := (x20 << 4)
|
||||
x23 := (x18 * u64(0x2))
|
||||
x24 := (x16 << 6)
|
||||
x25 := (x14 << 3)
|
||||
x26 := (u8(x12) & 0xff)
|
||||
x27 := (x12 >> 8)
|
||||
x28 := (u8(x27) & 0xff)
|
||||
x29 := (x27 >> 8)
|
||||
x30 := (u8(x29) & 0xff)
|
||||
x31 := (x29 >> 8)
|
||||
x32 := (u8(x31) & 0xff)
|
||||
x33 := (x31 >> 8)
|
||||
x34 := (u8(x33) & 0xff)
|
||||
x35 := (x33 >> 8)
|
||||
x36 := (u8(x35) & 0xff)
|
||||
x37 := u8((x35 >> 8))
|
||||
x38 := (x25 + u64(x37))
|
||||
x39 := (u8(x38) & 0xff)
|
||||
x40 := (x38 >> 8)
|
||||
x41 := (u8(x40) & 0xff)
|
||||
x42 := (x40 >> 8)
|
||||
x43 := (u8(x42) & 0xff)
|
||||
x44 := (x42 >> 8)
|
||||
x45 := (u8(x44) & 0xff)
|
||||
x46 := (x44 >> 8)
|
||||
x47 := (u8(x46) & 0xff)
|
||||
x48 := (x46 >> 8)
|
||||
x49 := (u8(x48) & 0xff)
|
||||
x50 := u8((x48 >> 8))
|
||||
x51 := (x24 + u64(x50))
|
||||
x52 := (u8(x51) & 0xff)
|
||||
x53 := (x51 >> 8)
|
||||
x54 := (u8(x53) & 0xff)
|
||||
x55 := (x53 >> 8)
|
||||
x56 := (u8(x55) & 0xff)
|
||||
x57 := (x55 >> 8)
|
||||
x58 := (u8(x57) & 0xff)
|
||||
x59 := (x57 >> 8)
|
||||
x60 := (u8(x59) & 0xff)
|
||||
x61 := (x59 >> 8)
|
||||
x62 := (u8(x61) & 0xff)
|
||||
x63 := (x61 >> 8)
|
||||
x64 := (u8(x63) & 0xff)
|
||||
x65 := fiat.u1((x63 >> 8))
|
||||
x66 := (x23 + u64(x65))
|
||||
x67 := (u8(x66) & 0xff)
|
||||
x68 := (x66 >> 8)
|
||||
x69 := (u8(x68) & 0xff)
|
||||
x70 := (x68 >> 8)
|
||||
x71 := (u8(x70) & 0xff)
|
||||
x72 := (x70 >> 8)
|
||||
x73 := (u8(x72) & 0xff)
|
||||
x74 := (x72 >> 8)
|
||||
x75 := (u8(x74) & 0xff)
|
||||
x76 := (x74 >> 8)
|
||||
x77 := (u8(x76) & 0xff)
|
||||
x78 := u8((x76 >> 8))
|
||||
x79 := (x22 + u64(x78))
|
||||
x80 := (u8(x79) & 0xff)
|
||||
x81 := (x79 >> 8)
|
||||
x82 := (u8(x81) & 0xff)
|
||||
x83 := (x81 >> 8)
|
||||
x84 := (u8(x83) & 0xff)
|
||||
x85 := (x83 >> 8)
|
||||
x86 := (u8(x85) & 0xff)
|
||||
x87 := (x85 >> 8)
|
||||
x88 := (u8(x87) & 0xff)
|
||||
x89 := (x87 >> 8)
|
||||
x90 := (u8(x89) & 0xff)
|
||||
x91 := u8((x89 >> 8))
|
||||
out1[0] = x26
|
||||
out1[1] = x28
|
||||
out1[2] = x30
|
||||
out1[3] = x32
|
||||
out1[4] = x34
|
||||
out1[5] = x36
|
||||
out1[6] = x39
|
||||
out1[7] = x41
|
||||
out1[8] = x43
|
||||
out1[9] = x45
|
||||
out1[10] = x47
|
||||
out1[11] = x49
|
||||
out1[12] = x52
|
||||
out1[13] = x54
|
||||
out1[14] = x56
|
||||
out1[15] = x58
|
||||
out1[16] = x60
|
||||
out1[17] = x62
|
||||
out1[18] = x64
|
||||
out1[19] = x67
|
||||
out1[20] = x69
|
||||
out1[21] = x71
|
||||
out1[22] = x73
|
||||
out1[23] = x75
|
||||
out1[24] = x77
|
||||
out1[25] = x80
|
||||
out1[26] = x82
|
||||
out1[27] = x84
|
||||
out1[28] = x86
|
||||
out1[29] = x88
|
||||
out1[30] = x90
|
||||
out1[31] = x91
|
||||
}
|
||||
|
||||
_fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte) {
|
||||
x1 := (u64(arg1[31]) << 44)
|
||||
x2 := (u64(arg1[30]) << 36)
|
||||
x3 := (u64(arg1[29]) << 28)
|
||||
x4 := (u64(arg1[28]) << 20)
|
||||
x5 := (u64(arg1[27]) << 12)
|
||||
x6 := (u64(arg1[26]) << 4)
|
||||
x7 := (u64(arg1[25]) << 47)
|
||||
x8 := (u64(arg1[24]) << 39)
|
||||
x9 := (u64(arg1[23]) << 31)
|
||||
x10 := (u64(arg1[22]) << 23)
|
||||
x11 := (u64(arg1[21]) << 15)
|
||||
x12 := (u64(arg1[20]) << 7)
|
||||
x13 := (u64(arg1[19]) << 50)
|
||||
x14 := (u64(arg1[18]) << 42)
|
||||
x15 := (u64(arg1[17]) << 34)
|
||||
x16 := (u64(arg1[16]) << 26)
|
||||
x17 := (u64(arg1[15]) << 18)
|
||||
x18 := (u64(arg1[14]) << 10)
|
||||
x19 := (u64(arg1[13]) << 2)
|
||||
x20 := (u64(arg1[12]) << 45)
|
||||
x21 := (u64(arg1[11]) << 37)
|
||||
x22 := (u64(arg1[10]) << 29)
|
||||
x23 := (u64(arg1[9]) << 21)
|
||||
x24 := (u64(arg1[8]) << 13)
|
||||
x25 := (u64(arg1[7]) << 5)
|
||||
x26 := (u64(arg1[6]) << 48)
|
||||
x27 := (u64(arg1[5]) << 40)
|
||||
x28 := (u64(arg1[4]) << 32)
|
||||
x29 := (u64(arg1[3]) << 24)
|
||||
x30 := (u64(arg1[2]) << 16)
|
||||
x31 := (u64(arg1[1]) << 8)
|
||||
x32 := arg1[0]
|
||||
x33 := (x31 + u64(x32))
|
||||
x34 := (x30 + x33)
|
||||
x35 := (x29 + x34)
|
||||
x36 := (x28 + x35)
|
||||
x37 := (x27 + x36)
|
||||
x38 := (x26 + x37)
|
||||
x39 := (x38 & 0x7ffffffffffff)
|
||||
x40 := u8((x38 >> 51))
|
||||
x41 := (x25 + u64(x40))
|
||||
x42 := (x24 + x41)
|
||||
x43 := (x23 + x42)
|
||||
x44 := (x22 + x43)
|
||||
x45 := (x21 + x44)
|
||||
x46 := (x20 + x45)
|
||||
x47 := (x46 & 0x7ffffffffffff)
|
||||
x48 := u8((x46 >> 51))
|
||||
x49 := (x19 + u64(x48))
|
||||
x50 := (x18 + x49)
|
||||
x51 := (x17 + x50)
|
||||
x52 := (x16 + x51)
|
||||
x53 := (x15 + x52)
|
||||
x54 := (x14 + x53)
|
||||
x55 := (x13 + x54)
|
||||
x56 := (x55 & 0x7ffffffffffff)
|
||||
x57 := u8((x55 >> 51))
|
||||
x58 := (x12 + u64(x57))
|
||||
x59 := (x11 + x58)
|
||||
x60 := (x10 + x59)
|
||||
x61 := (x9 + x60)
|
||||
x62 := (x8 + x61)
|
||||
x63 := (x7 + x62)
|
||||
x64 := (x63 & 0x7ffffffffffff)
|
||||
x65 := u8((x63 >> 51))
|
||||
x66 := (x6 + u64(x65))
|
||||
x67 := (x5 + x66)
|
||||
x68 := (x4 + x67)
|
||||
x69 := (x3 + x68)
|
||||
x70 := (x2 + x69)
|
||||
x71 := (x1 + x70)
|
||||
out1[0] = x39
|
||||
out1[1] = x47
|
||||
out1[2] = x56
|
||||
out1[3] = x64
|
||||
out1[4] = x71
|
||||
}
|
||||
|
||||
fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
x4 := arg1[3]
|
||||
x5 := arg1[4]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
fe_carry_scmul_121666 :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
x2, x1 := bits.mul_u64(0x1db42, arg1[4])
|
||||
x4, x3 := bits.mul_u64(0x1db42, arg1[3])
|
||||
x6, x5 := bits.mul_u64(0x1db42, arg1[2])
|
||||
x8, x7 := bits.mul_u64(0x1db42, arg1[1])
|
||||
x10, x9 := bits.mul_u64(0x1db42, arg1[0])
|
||||
x11 := ((x9 >> 51) | ((x10 << 13) & 0xffffffffffffffff))
|
||||
x12 := (x9 & 0x7ffffffffffff)
|
||||
x13, x14 := bits.add_u64(x11, x7, u64(0x0))
|
||||
x15 := (u64(fiat.u1(x14)) + x8)
|
||||
x16 := ((x13 >> 51) | ((x15 << 13) & 0xffffffffffffffff))
|
||||
x17 := (x13 & 0x7ffffffffffff)
|
||||
x18, x19 := bits.add_u64(x16, x5, u64(0x0))
|
||||
x20 := (u64(fiat.u1(x19)) + x6)
|
||||
x21 := ((x18 >> 51) | ((x20 << 13) & 0xffffffffffffffff))
|
||||
x22 := (x18 & 0x7ffffffffffff)
|
||||
x23, x24 := bits.add_u64(x21, x3, u64(0x0))
|
||||
x25 := (u64(fiat.u1(x24)) + x4)
|
||||
x26 := ((x23 >> 51) | ((x25 << 13) & 0xffffffffffffffff))
|
||||
x27 := (x23 & 0x7ffffffffffff)
|
||||
x28, x29 := bits.add_u64(x26, x1, u64(0x0))
|
||||
x30 := (u64(fiat.u1(x29)) + x2)
|
||||
x31 := ((x28 >> 51) | ((x30 << 13) & 0xffffffffffffffff))
|
||||
x32 := (x28 & 0x7ffffffffffff)
|
||||
x33 := (x31 * 0x13)
|
||||
x34 := (x12 + x33)
|
||||
x35 := fiat.u1((x34 >> 51))
|
||||
x36 := (x34 & 0x7ffffffffffff)
|
||||
x37 := (u64(x35) + x17)
|
||||
x38 := fiat.u1((x37 >> 51))
|
||||
x39 := (x37 & 0x7ffffffffffff)
|
||||
x40 := (u64(x38) + x22)
|
||||
out1[0] = x36
|
||||
out1[1] = x39
|
||||
out1[2] = x40
|
||||
out1[3] = x27
|
||||
out1[4] = x32
|
||||
}
|
||||
|
||||
// The following routines were added by hand, and do not come from fiat-crypto.
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
|
||||
out1[0] = 0
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
out1[3] = 0
|
||||
out1[4] = 0
|
||||
}
|
||||
|
||||
fe_one :: proc "contextless" (out1: ^Tight_Field_Element) {
|
||||
out1[0] = 1
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
out1[3] = 0
|
||||
out1[4] = 0
|
||||
}
|
||||
|
||||
fe_set :: proc "contextless" (out1, arg1: ^Tight_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
x4 := arg1[3]
|
||||
x5 := arg1[4]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
fe_cond_swap :: proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: int) {
|
||||
mask := -u64(arg1)
|
||||
x := (out1[0] ~ out2[0]) & mask
|
||||
x1, y1 := out1[0] ~ x, out2[0] ~ x
|
||||
x = (out1[1] ~ out2[1]) & mask
|
||||
x2, y2 := out1[1] ~ x, out2[1] ~ x
|
||||
x = (out1[2] ~ out2[2]) & mask
|
||||
x3, y3 := out1[2] ~ x, out2[2] ~ x
|
||||
x = (out1[3] ~ out2[3]) & mask
|
||||
x4, y4 := out1[3] ~ x, out2[3] ~ x
|
||||
x = (out1[4] ~ out2[4]) & mask
|
||||
x5, y5 := out1[4] ~ x, out2[4] ~ x
|
||||
out1[0], out2[0] = x1, y1
|
||||
out1[1], out2[1] = x2, y2
|
||||
out1[2], out2[2] = x3, y3
|
||||
out1[3], out2[3] = x4, y4
|
||||
out1[4], out2[4] = x5, y5
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package field_poly1305
|
||||
|
||||
import "core:crypto/util"
|
||||
import "core:mem"
|
||||
|
||||
fe_relax_cast :: #force_inline proc "contextless" (arg1: ^Tight_Field_Element) -> ^Loose_Field_Element {
|
||||
return transmute(^Loose_Field_Element)(arg1)
|
||||
}
|
||||
|
||||
fe_tighten_cast :: #force_inline proc "contextless" (arg1: ^Loose_Field_Element) -> ^Tight_Field_Element {
|
||||
return transmute(^Tight_Field_Element)(arg1)
|
||||
}
|
||||
|
||||
fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, arg2: byte, sanitize: bool = true) {
|
||||
// fiat-crypto's deserialization routine effectively processes a
|
||||
// single byte at a time, and wants 256-bits of input for a value
|
||||
// that will be 128-bits or 129-bits.
|
||||
//
|
||||
// This is somewhat cumbersome to use, so at a minimum a wrapper
|
||||
// makes implementing the actual MAC block processing considerably
|
||||
// neater.
|
||||
|
||||
assert(len(arg1) == 16)
|
||||
|
||||
when ODIN_ARCH == "386" || ODIN_ARCH == "amd64" {
|
||||
// While it may be unwise to do deserialization here on our
|
||||
// own when fiat-crypto provides equivalent functionality,
|
||||
// doing it this way provides a little under 3x performance
|
||||
// improvement when optimization is enabled.
|
||||
src_p := transmute(^[2]u64)(&arg1[0])
|
||||
lo := src_p[0]
|
||||
hi := src_p[1]
|
||||
|
||||
// This is inspired by poly1305-donna, though adjustments were
|
||||
// made since a Tight_Field_Element's limbs are 44-bits, 43-bits,
|
||||
// and 43-bits wide.
|
||||
//
|
||||
// Note: This could be transplated into fe_from_u64s, but that
|
||||
// code is called once per MAC, and is non-criticial path.
|
||||
hibit := u64(arg2) << 41 // arg2 << 128
|
||||
out1[0] = lo & 0xfffffffffff
|
||||
out1[1] = ((lo >> 44) | (hi << 20)) & 0x7ffffffffff
|
||||
out1[2] = ((hi >> 23) & 0x7ffffffffff) | hibit
|
||||
} else {
|
||||
tmp: [32]byte
|
||||
copy_slice(tmp[0:16], arg1[:])
|
||||
tmp[16] = arg2
|
||||
|
||||
_fe_from_bytes(out1, &tmp)
|
||||
if sanitize {
|
||||
// This is used to deserialize `s` which is confidential.
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fe_from_u64s :: proc "contextless" (out1: ^Tight_Field_Element, lo, hi: u64) {
|
||||
tmp: [32]byte
|
||||
util.PUT_U64_LE(tmp[0:8], lo)
|
||||
util.PUT_U64_LE(tmp[8:16], hi)
|
||||
|
||||
_fe_from_bytes(out1, &tmp)
|
||||
|
||||
// This routine is only used to deserialize `r` which is confidential.
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
// The BSD 1-Clause License (BSD-1-Clause)
|
||||
//
|
||||
// Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file)
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY the fiat-crypto authors "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design,
|
||||
// Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package field_poly1305
|
||||
|
||||
// This file provides arithmetic on the field Z/(2^130 - 5) using
|
||||
// unsaturated 64-bit integer arithmetic. It is derived primarily
|
||||
// from the machine generate Golang output from the fiat-crypto project.
|
||||
//
|
||||
// While the base implementation is provably correct, this implementation
|
||||
// makes no such claims as the port and optimizations were done by hand.
|
||||
// At some point, it may be worth adding support to fiat-crypto for
|
||||
// generating Odin output.
|
||||
|
||||
import fiat "core:crypto/_fiat"
|
||||
import "core:math/bits"
|
||||
|
||||
Loose_Field_Element :: distinct [3]u64
|
||||
Tight_Field_Element :: distinct [3]u64
|
||||
|
||||
_addcarryx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
|
||||
x1 := ((u64(arg1) + arg2) + arg3)
|
||||
x2 := (x1 & 0xfffffffffff)
|
||||
x3 := fiat.u1((x1 >> 44))
|
||||
out1 = x2
|
||||
out2 = x3
|
||||
return
|
||||
}
|
||||
|
||||
_subborrowx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
|
||||
x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
|
||||
x2 := fiat.i1((x1 >> 44))
|
||||
x3 := (u64(x1) & 0xfffffffffff)
|
||||
out1 = x3
|
||||
out2 = (0x0 - fiat.u1(x2))
|
||||
return
|
||||
}
|
||||
|
||||
_addcarryx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
|
||||
x1 := ((u64(arg1) + arg2) + arg3)
|
||||
x2 := (x1 & 0x7ffffffffff)
|
||||
x3 := fiat.u1((x1 >> 43))
|
||||
out1 = x2
|
||||
out2 = x3
|
||||
return
|
||||
}
|
||||
|
||||
_subborrowx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
|
||||
x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
|
||||
x2 := fiat.i1((x1 >> 43))
|
||||
x3 := (u64(x1) & 0x7ffffffffff)
|
||||
out1 = x3
|
||||
out2 = (0x0 - fiat.u1(x2))
|
||||
return
|
||||
}
|
||||
|
||||
fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
|
||||
x2, x1 := bits.mul_u64(arg1[2], (arg2[2] * 0x5))
|
||||
x4, x3 := bits.mul_u64(arg1[2], (arg2[1] * 0xa))
|
||||
x6, x5 := bits.mul_u64(arg1[1], (arg2[2] * 0xa))
|
||||
x8, x7 := bits.mul_u64(arg1[2], arg2[0])
|
||||
x10, x9 := bits.mul_u64(arg1[1], (arg2[1] * 0x2))
|
||||
x12, x11 := bits.mul_u64(arg1[1], arg2[0])
|
||||
x14, x13 := bits.mul_u64(arg1[0], arg2[2])
|
||||
x16, x15 := bits.mul_u64(arg1[0], arg2[1])
|
||||
x18, x17 := bits.mul_u64(arg1[0], arg2[0])
|
||||
x19, x20 := bits.add_u64(x5, x3, u64(0x0))
|
||||
x21, _ := bits.add_u64(x6, x4, u64(fiat.u1(x20)))
|
||||
x23, x24 := bits.add_u64(x17, x19, u64(0x0))
|
||||
x25, _ := bits.add_u64(x18, x21, u64(fiat.u1(x24)))
|
||||
x27 := ((x23 >> 44) | ((x25 << 20) & 0xffffffffffffffff))
|
||||
x28 := (x23 & 0xfffffffffff)
|
||||
x29, x30 := bits.add_u64(x9, x7, u64(0x0))
|
||||
x31, _ := bits.add_u64(x10, x8, u64(fiat.u1(x30)))
|
||||
x33, x34 := bits.add_u64(x13, x29, u64(0x0))
|
||||
x35, _ := bits.add_u64(x14, x31, u64(fiat.u1(x34)))
|
||||
x37, x38 := bits.add_u64(x11, x1, u64(0x0))
|
||||
x39, _ := bits.add_u64(x12, x2, u64(fiat.u1(x38)))
|
||||
x41, x42 := bits.add_u64(x15, x37, u64(0x0))
|
||||
x43, _ := bits.add_u64(x16, x39, u64(fiat.u1(x42)))
|
||||
x45, x46 := bits.add_u64(x27, x41, u64(0x0))
|
||||
x47 := (u64(fiat.u1(x46)) + x43)
|
||||
x48 := ((x45 >> 43) | ((x47 << 21) & 0xffffffffffffffff))
|
||||
x49 := (x45 & 0x7ffffffffff)
|
||||
x50, x51 := bits.add_u64(x48, x33, u64(0x0))
|
||||
x52 := (u64(fiat.u1(x51)) + x35)
|
||||
x53 := ((x50 >> 43) | ((x52 << 21) & 0xffffffffffffffff))
|
||||
x54 := (x50 & 0x7ffffffffff)
|
||||
x55 := (x53 * 0x5)
|
||||
x56 := (x28 + x55)
|
||||
x57 := (x56 >> 44)
|
||||
x58 := (x56 & 0xfffffffffff)
|
||||
x59 := (x57 + x49)
|
||||
x60 := fiat.u1((x59 >> 43))
|
||||
x61 := (x59 & 0x7ffffffffff)
|
||||
x62 := (u64(x60) + x54)
|
||||
out1[0] = x58
|
||||
out1[1] = x61
|
||||
out1[2] = x62
|
||||
}
|
||||
|
||||
fe_carry_square :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
x1 := (arg1[2] * 0x5)
|
||||
x2 := (x1 * 0x2)
|
||||
x3 := (arg1[2] * 0x2)
|
||||
x4 := (arg1[1] * 0x2)
|
||||
x6, x5 := bits.mul_u64(arg1[2], x1)
|
||||
x8, x7 := bits.mul_u64(arg1[1], (x2 * 0x2))
|
||||
x10, x9 := bits.mul_u64(arg1[1], (arg1[1] * 0x2))
|
||||
x12, x11 := bits.mul_u64(arg1[0], x3)
|
||||
x14, x13 := bits.mul_u64(arg1[0], x4)
|
||||
x16, x15 := bits.mul_u64(arg1[0], arg1[0])
|
||||
x17, x18 := bits.add_u64(x15, x7, u64(0x0))
|
||||
x19, _ := bits.add_u64(x16, x8, u64(fiat.u1(x18)))
|
||||
x21 := ((x17 >> 44) | ((x19 << 20) & 0xffffffffffffffff))
|
||||
x22 := (x17 & 0xfffffffffff)
|
||||
x23, x24 := bits.add_u64(x11, x9, u64(0x0))
|
||||
x25, _ := bits.add_u64(x12, x10, u64(fiat.u1(x24)))
|
||||
x27, x28 := bits.add_u64(x13, x5, u64(0x0))
|
||||
x29, _ := bits.add_u64(x14, x6, u64(fiat.u1(x28)))
|
||||
x31, x32 := bits.add_u64(x21, x27, u64(0x0))
|
||||
x33 := (u64(fiat.u1(x32)) + x29)
|
||||
x34 := ((x31 >> 43) | ((x33 << 21) & 0xffffffffffffffff))
|
||||
x35 := (x31 & 0x7ffffffffff)
|
||||
x36, x37 := bits.add_u64(x34, x23, u64(0x0))
|
||||
x38 := (u64(fiat.u1(x37)) + x25)
|
||||
x39 := ((x36 >> 43) | ((x38 << 21) & 0xffffffffffffffff))
|
||||
x40 := (x36 & 0x7ffffffffff)
|
||||
x41 := (x39 * 0x5)
|
||||
x42 := (x22 + x41)
|
||||
x43 := (x42 >> 44)
|
||||
x44 := (x42 & 0xfffffffffff)
|
||||
x45 := (x43 + x35)
|
||||
x46 := fiat.u1((x45 >> 43))
|
||||
x47 := (x45 & 0x7ffffffffff)
|
||||
x48 := (u64(x46) + x40)
|
||||
out1[0] = x44
|
||||
out1[1] = x47
|
||||
out1[2] = x48
|
||||
}
|
||||
|
||||
fe_carry :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := ((x1 >> 44) + arg1[1])
|
||||
x3 := ((x2 >> 43) + arg1[2])
|
||||
x4 := ((x1 & 0xfffffffffff) + ((x3 >> 43) * 0x5))
|
||||
x5 := (u64(fiat.u1((x4 >> 44))) + (x2 & 0x7ffffffffff))
|
||||
x6 := (x4 & 0xfffffffffff)
|
||||
x7 := (x5 & 0x7ffffffffff)
|
||||
x8 := (u64(fiat.u1((x5 >> 43))) + (x3 & 0x7ffffffffff))
|
||||
out1[0] = x6
|
||||
out1[1] = x7
|
||||
out1[2] = x8
|
||||
}
|
||||
|
||||
fe_add :: proc "contextless" (out1: ^Loose_Field_Element, arg1, arg2: ^Tight_Field_Element) {
|
||||
x1 := (arg1[0] + arg2[0])
|
||||
x2 := (arg1[1] + arg2[1])
|
||||
x3 := (arg1[2] + arg2[2])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
}
|
||||
|
||||
fe_sub :: proc "contextless" (out1: ^Loose_Field_Element, arg1, arg2: ^Tight_Field_Element) {
|
||||
x1 := ((0x1ffffffffff6 + arg1[0]) - arg2[0])
|
||||
x2 := ((0xffffffffffe + arg1[1]) - arg2[1])
|
||||
x3 := ((0xffffffffffe + arg1[2]) - arg2[2])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
}
|
||||
|
||||
fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Element) {
|
||||
x1 := (0x1ffffffffff6 - arg1[0])
|
||||
x2 := (0xffffffffffe - arg1[1])
|
||||
x3 := (0xffffffffffe - arg1[2])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
}
|
||||
|
||||
fe_cond_assign :: proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: bool) {
|
||||
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
|
||||
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
|
||||
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
}
|
||||
|
||||
fe_to_bytes :: proc "contextless" (out1: ^[32]byte, arg1: ^Tight_Field_Element) {
|
||||
x1, x2 := _subborrowx_u44(0x0, arg1[0], 0xffffffffffb)
|
||||
x3, x4 := _subborrowx_u43(x2, arg1[1], 0x7ffffffffff)
|
||||
x5, x6 := _subborrowx_u43(x4, arg1[2], 0x7ffffffffff)
|
||||
x7 := fiat.cmovznz_u64(x6, u64(0x0), 0xffffffffffffffff)
|
||||
x8, x9 := _addcarryx_u44(0x0, x1, (x7 & 0xffffffffffb))
|
||||
x10, x11 := _addcarryx_u43(x9, x3, (x7 & 0x7ffffffffff))
|
||||
x12, _ := _addcarryx_u43(x11, x5, (x7 & 0x7ffffffffff))
|
||||
x14 := (x12 << 7)
|
||||
x15 := (x10 << 4)
|
||||
x16 := (u8(x8) & 0xff)
|
||||
x17 := (x8 >> 8)
|
||||
x18 := (u8(x17) & 0xff)
|
||||
x19 := (x17 >> 8)
|
||||
x20 := (u8(x19) & 0xff)
|
||||
x21 := (x19 >> 8)
|
||||
x22 := (u8(x21) & 0xff)
|
||||
x23 := (x21 >> 8)
|
||||
x24 := (u8(x23) & 0xff)
|
||||
x25 := u8((x23 >> 8))
|
||||
x26 := (x15 + u64(x25))
|
||||
x27 := (u8(x26) & 0xff)
|
||||
x28 := (x26 >> 8)
|
||||
x29 := (u8(x28) & 0xff)
|
||||
x30 := (x28 >> 8)
|
||||
x31 := (u8(x30) & 0xff)
|
||||
x32 := (x30 >> 8)
|
||||
x33 := (u8(x32) & 0xff)
|
||||
x34 := (x32 >> 8)
|
||||
x35 := (u8(x34) & 0xff)
|
||||
x36 := u8((x34 >> 8))
|
||||
x37 := (x14 + u64(x36))
|
||||
x38 := (u8(x37) & 0xff)
|
||||
x39 := (x37 >> 8)
|
||||
x40 := (u8(x39) & 0xff)
|
||||
x41 := (x39 >> 8)
|
||||
x42 := (u8(x41) & 0xff)
|
||||
x43 := (x41 >> 8)
|
||||
x44 := (u8(x43) & 0xff)
|
||||
x45 := (x43 >> 8)
|
||||
x46 := (u8(x45) & 0xff)
|
||||
x47 := (x45 >> 8)
|
||||
x48 := (u8(x47) & 0xff)
|
||||
x49 := u8((x47 >> 8))
|
||||
out1[0] = x16
|
||||
out1[1] = x18
|
||||
out1[2] = x20
|
||||
out1[3] = x22
|
||||
out1[4] = x24
|
||||
out1[5] = x27
|
||||
out1[6] = x29
|
||||
out1[7] = x31
|
||||
out1[8] = x33
|
||||
out1[9] = x35
|
||||
out1[10] = x38
|
||||
out1[11] = x40
|
||||
out1[12] = x42
|
||||
out1[13] = x44
|
||||
out1[14] = x46
|
||||
out1[15] = x48
|
||||
out1[16] = x49
|
||||
}
|
||||
|
||||
_fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte) {
|
||||
x1 := (u64(arg1[16]) << 41)
|
||||
x2 := (u64(arg1[15]) << 33)
|
||||
x3 := (u64(arg1[14]) << 25)
|
||||
x4 := (u64(arg1[13]) << 17)
|
||||
x5 := (u64(arg1[12]) << 9)
|
||||
x6 := (u64(arg1[11]) * u64(0x2))
|
||||
x7 := (u64(arg1[10]) << 36)
|
||||
x8 := (u64(arg1[9]) << 28)
|
||||
x9 := (u64(arg1[8]) << 20)
|
||||
x10 := (u64(arg1[7]) << 12)
|
||||
x11 := (u64(arg1[6]) << 4)
|
||||
x12 := (u64(arg1[5]) << 40)
|
||||
x13 := (u64(arg1[4]) << 32)
|
||||
x14 := (u64(arg1[3]) << 24)
|
||||
x15 := (u64(arg1[2]) << 16)
|
||||
x16 := (u64(arg1[1]) << 8)
|
||||
x17 := arg1[0]
|
||||
x18 := (x16 + u64(x17))
|
||||
x19 := (x15 + x18)
|
||||
x20 := (x14 + x19)
|
||||
x21 := (x13 + x20)
|
||||
x22 := (x12 + x21)
|
||||
x23 := (x22 & 0xfffffffffff)
|
||||
x24 := u8((x22 >> 44))
|
||||
x25 := (x11 + u64(x24))
|
||||
x26 := (x10 + x25)
|
||||
x27 := (x9 + x26)
|
||||
x28 := (x8 + x27)
|
||||
x29 := (x7 + x28)
|
||||
x30 := (x29 & 0x7ffffffffff)
|
||||
x31 := fiat.u1((x29 >> 43))
|
||||
x32 := (x6 + u64(x31))
|
||||
x33 := (x5 + x32)
|
||||
x34 := (x4 + x33)
|
||||
x35 := (x3 + x34)
|
||||
x36 := (x2 + x35)
|
||||
x37 := (x1 + x36)
|
||||
out1[0] = x23
|
||||
out1[1] = x30
|
||||
out1[2] = x37
|
||||
}
|
||||
|
||||
fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
}
|
||||
|
||||
// The following routines were added by hand, and do not come from fiat-crypto.
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
|
||||
out1[0] = 0
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
}
|
||||
|
||||
fe_set :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
}
|
||||
|
||||
fe_cond_swap :: proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: bool) {
|
||||
mask := -u64(arg1)
|
||||
x := (out1[0] ~ out2[0]) & mask
|
||||
x1, y1 := out1[0] ~ x, out2[0] ~ x
|
||||
x = (out1[1] ~ out2[1]) & mask
|
||||
x2, y2 := out1[1] ~ x, out2[1] ~ x
|
||||
x = (out1[2] ~ out2[2]) & mask
|
||||
x3, y3 := out1[2] ~ x, out2[2] ~ x
|
||||
out1[0], out2[0] = x1, y1
|
||||
out1[1], out2[1] = x2, y2
|
||||
out1[2], out2[2] = x3, y3
|
||||
}
|
||||
@@ -6,7 +6,6 @@ package _sha3
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the Keccak hashing algorithm, standardized as SHA3 in <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.
|
||||
@@ -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 {
|
||||
c.st.q[i] = 0
|
||||
}
|
||||
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
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
c.st.b[j] ~= data[i]
|
||||
@@ -135,7 +134,7 @@ update_odin :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
|
||||
c.pt = j
|
||||
}
|
||||
|
||||
final_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
||||
final :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
||||
if c.is_keccak {
|
||||
c.st.b[c.pt] ~= 0x01
|
||||
} 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.rsiz - 1] ~= 0x80
|
||||
keccakf(&c.st.q)
|
||||
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
|
||||
for i := 0; i < len(hash); i += 1 {
|
||||
if j >= c.rsiz {
|
||||
|
||||
@@ -6,7 +6,6 @@ package _tiger
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the Tiger hashing algorithm, as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
|
||||
*/
|
||||
@@ -291,7 +290,7 @@ Tiger_Context :: struct {
|
||||
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
|
||||
c ~= x
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
y, z, x = round(y, z, x, d[1], 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
|
||||
}
|
||||
|
||||
key_schedule :: #force_inline proc "contextless"(x: []u64) {
|
||||
key_schedule :: #force_inline proc "contextless" (x: []u64) {
|
||||
x[0] -= x[7] ~ 0xa5a5a5a5a5a5a5a5
|
||||
x[1] ~= x[0]
|
||||
x[2] += x[1]
|
||||
@@ -331,7 +330,7 @@ key_schedule :: #force_inline proc "contextless"(x: []u64) {
|
||||
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
|
||||
b := ctx.b
|
||||
c := ctx.c
|
||||
@@ -346,13 +345,13 @@ compress :: #force_inline proc "contextless"(ctx: ^Tiger_Context, data: []byte)
|
||||
ctx.c += c
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Tiger_Context) {
|
||||
init :: proc "contextless" (ctx: ^Tiger_Context) {
|
||||
ctx.a = 0x0123456789abcdef
|
||||
ctx.b = 0xfedcba9876543210
|
||||
ctx.c = 0xf096a5b4c3b2e187
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Tiger_Context, input: []byte) {
|
||||
update :: proc(ctx: ^Tiger_Context, input: []byte) {
|
||||
p := make([]byte, len(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
|
||||
tmp: [64]byte
|
||||
if ctx.ver == 1 {
|
||||
@@ -391,16 +390,16 @@ final_odin :: proc(ctx: ^Tiger_Context, hash: []byte) {
|
||||
|
||||
size := length & 0x3f
|
||||
if size < 56 {
|
||||
update_odin(ctx, tmp[:56 - size])
|
||||
update(ctx, tmp[:56 - size])
|
||||
} else {
|
||||
update_odin(ctx, tmp[:64 + 56 - size])
|
||||
update(ctx, tmp[:64 + 56 - size])
|
||||
}
|
||||
|
||||
length <<= 3
|
||||
for i := uint(0); i < 8; i += 1 {
|
||||
tmp[i] = byte(length >> (8 * i))
|
||||
}
|
||||
update_odin(ctx, tmp[:8])
|
||||
update(ctx, tmp[:8])
|
||||
|
||||
for i := uint(0); i < 8; i += 1 {
|
||||
tmp[i] = byte(ctx.a >> (8 * i))
|
||||
|
||||
+300
-497
@@ -6,7 +6,6 @@ package blake
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the BLAKE hashing algorithm, as defined in <https://web.archive.org/web/20190915215948/https://131002.net/blake>
|
||||
*/
|
||||
@@ -14,102 +13,59 @@ package blake
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since BLAKE is not available in Botan
|
||||
@(warning="BLAKE is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_blake256_ctx :: #force_inline proc(is224: bool, size: _ctx.Hash_Size) {
|
||||
ctx: Blake256_Context
|
||||
ctx.is224 = is224
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = size
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_blake512_ctx :: #force_inline proc(is384: bool, size: _ctx.Hash_Size) {
|
||||
ctx: Blake512_Context
|
||||
ctx.is384 = is384
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = size
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
hash_string_224 :: proc "contextless" (data: string) -> [28]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_blake256_ctx(true, ._28)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
hash_bytes_224 :: proc "contextless" (data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
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 from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_blake256_ctx(true, ._28)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
hash: [28]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
_create_blake256_ctx(true, ._28)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -121,29 +77,53 @@ hash_224 :: proc {
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// 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))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_blake256_ctx(false, ._32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash_bytes_256 :: proc "contextless" (data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
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 from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_blake256_ctx(false, ._32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash: [32]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_blake256_ctx(false, ._32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -155,29 +135,53 @@ hash_256 :: proc {
|
||||
|
||||
// hash_string_384 will hash the given input and return the
|
||||
// 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))
|
||||
}
|
||||
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
|
||||
_create_blake512_ctx(true, ._48)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
hash_bytes_384 :: proc "contextless" (data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
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 from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_blake512_ctx(true, ._48)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
hash: [48]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
_create_blake512_ctx(true, ._48)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -189,29 +193,53 @@ hash_384 :: proc {
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// 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))
|
||||
}
|
||||
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
|
||||
_create_blake512_ctx(false, ._64)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash_bytes_512 :: proc "contextless" (data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
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 from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_blake512_ctx(false, ._64)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash: [64]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_blake512_ctx(false, ._64)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -225,231 +253,188 @@ hash_512 :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Blake256_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Blake256_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
init :: proc "contextless" (ctx: ^$T) {
|
||||
when T == Blake256_Context {
|
||||
if ctx.is224 {
|
||||
ctx.h[0] = 0xc1059ed8
|
||||
ctx.h[1] = 0x367cd507
|
||||
ctx.h[2] = 0x3070dd17
|
||||
ctx.h[3] = 0xf70e5939
|
||||
ctx.h[4] = 0xffc00b31
|
||||
ctx.h[5] = 0x68581511
|
||||
ctx.h[6] = 0x64f98fa7
|
||||
ctx.h[7] = 0xbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667
|
||||
ctx.h[1] = 0xbb67ae85
|
||||
ctx.h[2] = 0x3c6ef372
|
||||
ctx.h[3] = 0xa54ff53a
|
||||
ctx.h[4] = 0x510e527f
|
||||
ctx.h[5] = 0x9b05688c
|
||||
ctx.h[6] = 0x1f83d9ab
|
||||
ctx.h[7] = 0x5be0cd19
|
||||
}
|
||||
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.(Blake256_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Blake256_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, 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.(Blake512_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, 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.(Blake512_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
if ctx.hash_size == ._28 || ctx.hash_size == ._32 {
|
||||
_create_blake256_ctx(ctx.hash_size == ._28, ctx.hash_size)
|
||||
if c, ok := ctx.internal_ctx.(Blake256_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
return
|
||||
}
|
||||
if ctx.hash_size == ._48 || ctx.hash_size == ._64 {
|
||||
_create_blake512_ctx(ctx.hash_size == ._48, ctx.hash_size)
|
||||
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
|
||||
init_odin(&c)
|
||||
} 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(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)
|
||||
update :: proc "contextless" (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
|
||||
}
|
||||
case ._48, ._64:
|
||||
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
|
||||
update_odin(&c, data)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(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)
|
||||
final :: proc "contextless" (ctx: ^$T, hash: []byte) {
|
||||
when T == Blake256_Context {
|
||||
tmp: [65]byte
|
||||
} else when T == Blake512_Context {
|
||||
tmp: [129]byte
|
||||
}
|
||||
nx := u64(ctx.nx)
|
||||
tmp[0] = 0x80
|
||||
length := (ctx.t + nx) << 3
|
||||
|
||||
when T == Blake256_Context {
|
||||
if nx == 55 {
|
||||
if ctx.is224 {
|
||||
write_additional(ctx, {0x80})
|
||||
} else {
|
||||
write_additional(ctx, {0x81})
|
||||
}
|
||||
case ._48, ._64:
|
||||
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
BLAKE implementation
|
||||
*/
|
||||
|
||||
SIZE_224 :: 28
|
||||
SIZE_256 :: 32
|
||||
SIZE_384 :: 48
|
||||
@@ -542,8 +527,8 @@ G512 :: #force_inline proc "contextless" (a, b, c, d: u64, m: [16]u64, i, j: int
|
||||
return a, b, c, d
|
||||
}
|
||||
|
||||
block256 :: proc "contextless" (ctx: ^Blake256_Context, p: []byte) {
|
||||
i, j: int = ---, ---
|
||||
block256 :: proc "contextless" (ctx: ^Blake256_Context, p: []byte) #no_bounds_check {
|
||||
i, j: int = ---, ---
|
||||
v, m: [16]u32 = ---, ---
|
||||
p := p
|
||||
for len(p) >= BLOCKSIZE_256 {
|
||||
@@ -595,7 +580,7 @@ block256 :: proc "contextless" (ctx: ^Blake256_Context, p: []byte) {
|
||||
}
|
||||
|
||||
block512 :: proc "contextless" (ctx: ^Blake512_Context, p: []byte) #no_bounds_check {
|
||||
i, j: int = ---, ---
|
||||
i, j: int = ---, ---
|
||||
v, m: [16]u64 = ---, ---
|
||||
p := p
|
||||
for len(p) >= BLOCKSIZE_512 {
|
||||
@@ -646,189 +631,7 @@ block512 :: proc "contextless" (ctx: ^Blake512_Context, p: []byte) #no_bounds_ch
|
||||
}
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^$T) {
|
||||
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) {
|
||||
write_additional :: proc "contextless" (ctx: ^$T, data: []byte) {
|
||||
ctx.t -= u64(len(data)) << 3
|
||||
update_odin(ctx, data)
|
||||
update(ctx, data)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package blake2b
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the BLAKE2B hashing algorithm.
|
||||
BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
|
||||
@@ -15,49 +14,8 @@ package blake2b
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_blake2"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_64 = hash_bytes_odin
|
||||
ctx.hash_file_64 = hash_file_odin
|
||||
ctx.hash_stream_64 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_BLAKE2B)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
@@ -71,22 +29,50 @@ hash_string :: proc(data: string) -> [64]byte {
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [64]byte {
|
||||
_create_blake2b_ctx()
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash: [64]byte
|
||||
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 from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_blake2b_ctx()
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash: [64]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_blake2b_ctx()
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -100,87 +86,16 @@ hash :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
Blake2b_Context :: _blake2.Blake2b_Context
|
||||
|
||||
init :: proc(ctx: ^_blake2.Blake2b_Context) {
|
||||
_blake2.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, data: []byte) {
|
||||
_blake2.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
_blake2.update_odin(&c, data)
|
||||
_blake2.blake2b_final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_blake2.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_blake2.blake2b_final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, 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)
|
||||
}
|
||||
final :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, hash: []byte) {
|
||||
_blake2.final(ctx, hash)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package blake2s
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the BLAKE2S hashing algorithm.
|
||||
BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
|
||||
@@ -15,49 +14,8 @@ package blake2s
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
import "../_blake2"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_32 = hash_bytes_odin
|
||||
ctx.hash_file_32 = hash_file_odin
|
||||
ctx.hash_stream_32 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since Blake2s is not available in Botan
|
||||
@(warning="Blake2s is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
@@ -71,22 +29,50 @@ hash_string :: proc(data: string) -> [32]byte {
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [32]byte {
|
||||
_create_blake2s_ctx()
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash: [32]byte
|
||||
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 from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_blake2s_ctx()
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash: [32]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_blake2s_ctx()
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -100,87 +86,16 @@ hash :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
Blake2s_Context :: _blake2.Blake2b_Context
|
||||
|
||||
init :: proc(ctx: ^_blake2.Blake2s_Context) {
|
||||
_blake2.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, data: []byte) {
|
||||
_blake2.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
_blake2.update_odin(&c, data)
|
||||
_blake2.blake2s_final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_blake2.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_blake2.blake2s_final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, 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)
|
||||
}
|
||||
final :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, hash: []byte) {
|
||||
_blake2.final(ctx, hash)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,581 @@
|
||||
package chacha20
|
||||
|
||||
import "core:crypto/util"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
|
||||
KEY_SIZE :: 32
|
||||
NONCE_SIZE :: 12
|
||||
XNONCE_SIZE :: 24
|
||||
|
||||
_MAX_CTR_IETF :: 0xffffffff
|
||||
|
||||
_BLOCK_SIZE :: 64
|
||||
_STATE_SIZE_U32 :: 16
|
||||
_ROUNDS :: 20
|
||||
|
||||
_SIGMA_0 : u32 : 0x61707865
|
||||
_SIGMA_1 : u32 : 0x3320646e
|
||||
_SIGMA_2 : u32 : 0x79622d32
|
||||
_SIGMA_3 : u32 : 0x6b206574
|
||||
|
||||
Context :: struct {
|
||||
_s: [_STATE_SIZE_U32]u32,
|
||||
|
||||
_buffer: [_BLOCK_SIZE]byte,
|
||||
_off: int,
|
||||
|
||||
_is_ietf_flavor: bool,
|
||||
_is_initialized: bool,
|
||||
}
|
||||
|
||||
init :: proc (ctx: ^Context, key, nonce: []byte) {
|
||||
if len(key) != KEY_SIZE {
|
||||
panic("crypto/chacha20: invalid ChaCha20 key size")
|
||||
}
|
||||
if n_len := len(nonce); n_len != NONCE_SIZE && n_len != XNONCE_SIZE {
|
||||
panic("crypto/chacha20: invalid (X)ChaCha20 nonce size")
|
||||
}
|
||||
|
||||
k, n := key, nonce
|
||||
|
||||
// Derive the XChaCha20 subkey and sub-nonce via HChaCha20.
|
||||
is_xchacha := len(nonce) == XNONCE_SIZE
|
||||
if is_xchacha {
|
||||
sub_key := ctx._buffer[:KEY_SIZE]
|
||||
_hchacha20(sub_key, k, n)
|
||||
k = sub_key
|
||||
n = n[16:24]
|
||||
}
|
||||
|
||||
ctx._s[0] = _SIGMA_0
|
||||
ctx._s[1] = _SIGMA_1
|
||||
ctx._s[2] = _SIGMA_2
|
||||
ctx._s[3] = _SIGMA_3
|
||||
ctx._s[4] = util.U32_LE(k[0:4])
|
||||
ctx._s[5] = util.U32_LE(k[4:8])
|
||||
ctx._s[6] = util.U32_LE(k[8:12])
|
||||
ctx._s[7] = util.U32_LE(k[12:16])
|
||||
ctx._s[8] = util.U32_LE(k[16:20])
|
||||
ctx._s[9] = util.U32_LE(k[20:24])
|
||||
ctx._s[10] = util.U32_LE(k[24:28])
|
||||
ctx._s[11] = util.U32_LE(k[28:32])
|
||||
ctx._s[12] = 0
|
||||
if !is_xchacha {
|
||||
ctx._s[13] = util.U32_LE(n[0:4])
|
||||
ctx._s[14] = util.U32_LE(n[4:8])
|
||||
ctx._s[15] = util.U32_LE(n[8:12])
|
||||
} else {
|
||||
ctx._s[13] = 0
|
||||
ctx._s[14] = util.U32_LE(n[0:4])
|
||||
ctx._s[15] = util.U32_LE(n[4:8])
|
||||
|
||||
// The sub-key is stored in the keystream buffer. While
|
||||
// this will be overwritten in most circumstances, explicitly
|
||||
// clear it out early.
|
||||
mem.zero_explicit(&ctx._buffer, KEY_SIZE)
|
||||
}
|
||||
|
||||
ctx._off = _BLOCK_SIZE
|
||||
ctx._is_ietf_flavor = !is_xchacha
|
||||
ctx._is_initialized = true
|
||||
}
|
||||
|
||||
seek :: proc (ctx: ^Context, block_nr: u64) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
if ctx._is_ietf_flavor {
|
||||
if block_nr > _MAX_CTR_IETF {
|
||||
panic("crypto/chacha20: attempted to seek past maximum counter")
|
||||
}
|
||||
} else {
|
||||
ctx._s[13] = u32(block_nr >> 32)
|
||||
}
|
||||
ctx._s[12] = u32(block_nr)
|
||||
ctx._off = _BLOCK_SIZE
|
||||
}
|
||||
|
||||
xor_bytes :: proc (ctx: ^Context, dst, src: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
// TODO: Enforcing that dst and src alias exactly or not at all
|
||||
// is a good idea, though odd aliasing should be extremely uncommon.
|
||||
|
||||
src, dst := src, dst
|
||||
if dst_len := len(dst); dst_len < len(src) {
|
||||
src = src[:dst_len]
|
||||
}
|
||||
|
||||
for remaining := len(src); remaining > 0; {
|
||||
// Process multiple blocks at once
|
||||
if ctx._off == _BLOCK_SIZE {
|
||||
if nr_blocks := remaining / _BLOCK_SIZE; nr_blocks > 0 {
|
||||
direct_bytes := nr_blocks * _BLOCK_SIZE
|
||||
_do_blocks(ctx, dst, src, nr_blocks)
|
||||
remaining -= direct_bytes
|
||||
if remaining == 0 {
|
||||
return
|
||||
}
|
||||
dst = dst[direct_bytes:]
|
||||
src = src[direct_bytes:]
|
||||
}
|
||||
|
||||
// If there is a partial block, generate and buffer 1 block
|
||||
// worth of keystream.
|
||||
_do_blocks(ctx, ctx._buffer[:], nil, 1)
|
||||
ctx._off = 0
|
||||
}
|
||||
|
||||
// Process partial blocks from the buffered keystream.
|
||||
to_xor := min(_BLOCK_SIZE - ctx._off, remaining)
|
||||
buffered_keystream := ctx._buffer[ctx._off:]
|
||||
for i := 0; i < to_xor; i = i + 1 {
|
||||
dst[i] = buffered_keystream[i] ~ src[i]
|
||||
}
|
||||
ctx._off += to_xor
|
||||
dst = dst[to_xor:]
|
||||
src = src[to_xor:]
|
||||
remaining -= to_xor
|
||||
}
|
||||
}
|
||||
|
||||
keystream_bytes :: proc (ctx: ^Context, dst: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
dst := dst
|
||||
for remaining := len(dst); remaining > 0; {
|
||||
// Process multiple blocks at once
|
||||
if ctx._off == _BLOCK_SIZE {
|
||||
if nr_blocks := remaining / _BLOCK_SIZE; nr_blocks > 0 {
|
||||
direct_bytes := nr_blocks * _BLOCK_SIZE
|
||||
_do_blocks(ctx, dst, nil, nr_blocks)
|
||||
remaining -= direct_bytes
|
||||
if remaining == 0 {
|
||||
return
|
||||
}
|
||||
dst = dst[direct_bytes:]
|
||||
}
|
||||
|
||||
// If there is a partial block, generate and buffer 1 block
|
||||
// worth of keystream.
|
||||
_do_blocks(ctx, ctx._buffer[:], nil, 1)
|
||||
ctx._off = 0
|
||||
}
|
||||
|
||||
// Process partial blocks from the buffered keystream.
|
||||
to_copy := min(_BLOCK_SIZE - ctx._off, remaining)
|
||||
buffered_keystream := ctx._buffer[ctx._off:]
|
||||
copy(dst[:to_copy], buffered_keystream[:to_copy])
|
||||
ctx._off += to_copy
|
||||
dst = dst[to_copy:]
|
||||
remaining -= to_copy
|
||||
}
|
||||
}
|
||||
|
||||
reset :: proc (ctx: ^Context) {
|
||||
mem.zero_explicit(&ctx._s, size_of(ctx._s))
|
||||
mem.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
|
||||
|
||||
ctx._is_initialized = false
|
||||
}
|
||||
|
||||
_do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
|
||||
// Enforce the maximum consumed keystream per nonce.
|
||||
//
|
||||
// While all modern "standard" definitions of ChaCha20 use
|
||||
// the IETF 32-bit counter, for XChaCha20 most common
|
||||
// implementations allow for a 64-bit counter.
|
||||
//
|
||||
// Honestly, the answer here is "use a MRAE primitive", but
|
||||
// go with common practice in the case of XChaCha20.
|
||||
if ctx._is_ietf_flavor {
|
||||
if u64(ctx._s[12]) + u64(nr_blocks) > 0xffffffff {
|
||||
panic("crypto/chacha20: maximum ChaCha20 keystream per nonce reached")
|
||||
}
|
||||
} else {
|
||||
ctr := (u64(ctx._s[13]) << 32) | u64(ctx._s[12])
|
||||
if _, carry := bits.add_u64(ctr, u64(nr_blocks), 0); carry != 0 {
|
||||
panic("crypto/chacha20: maximum XChaCha20 keystream per nonce reached")
|
||||
}
|
||||
}
|
||||
|
||||
dst, src := dst, src
|
||||
x := &ctx._s
|
||||
for n := 0; n < nr_blocks; n = n + 1 {
|
||||
x0, x1, x2, x3 := _SIGMA_0, _SIGMA_1, _SIGMA_2, _SIGMA_3
|
||||
x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 := x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15]
|
||||
|
||||
for i := _ROUNDS; i > 0; i = i - 2 {
|
||||
// Even when forcing inlining manually inlining all of
|
||||
// these is decently faster.
|
||||
|
||||
// quarterround(x, 0, 4, 8, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = util.ROTL32(x12, 16)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = util.ROTL32(x4, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = util.ROTL32(x12, 8)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = util.ROTL32(x4, 7)
|
||||
|
||||
// quarterround(x, 1, 5, 9, 13)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = util.ROTL32(x13, 16)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = util.ROTL32(x5, 12)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = util.ROTL32(x13, 8)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = util.ROTL32(x5, 7)
|
||||
|
||||
// quarterround(x, 2, 6, 10, 14)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = util.ROTL32(x14, 16)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = util.ROTL32(x6, 12)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = util.ROTL32(x14, 8)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = util.ROTL32(x6, 7)
|
||||
|
||||
// quarterround(x, 3, 7, 11, 15)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = util.ROTL32(x15, 16)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = util.ROTL32(x7, 12)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = util.ROTL32(x15, 8)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = util.ROTL32(x7, 7)
|
||||
|
||||
// quarterround(x, 0, 5, 10, 15)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = util.ROTL32(x15, 16)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = util.ROTL32(x5, 12)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = util.ROTL32(x15, 8)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = util.ROTL32(x5, 7)
|
||||
|
||||
// quarterround(x, 1, 6, 11, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = util.ROTL32(x12, 16)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = util.ROTL32(x6, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = util.ROTL32(x12, 8)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = util.ROTL32(x6, 7)
|
||||
|
||||
// quarterround(x, 2, 7, 8, 13)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = util.ROTL32(x13, 16)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = util.ROTL32(x7, 12)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = util.ROTL32(x13, 8)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = util.ROTL32(x7, 7)
|
||||
|
||||
// quarterround(x, 3, 4, 9, 14)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = util.ROTL32(x14, 16)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = util.ROTL32(x4, 12)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = util.ROTL32(x14, 8)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = util.ROTL32(x4, 7)
|
||||
}
|
||||
|
||||
x0 += _SIGMA_0
|
||||
x1 += _SIGMA_1
|
||||
x2 += _SIGMA_2
|
||||
x3 += _SIGMA_3
|
||||
x4 += x[4]
|
||||
x5 += x[5]
|
||||
x6 += x[6]
|
||||
x7 += x[7]
|
||||
x8 += x[8]
|
||||
x9 += x[9]
|
||||
x10 += x[10]
|
||||
x11 += x[11]
|
||||
x12 += x[12]
|
||||
x13 += x[13]
|
||||
x14 += x[14]
|
||||
x15 += x[15]
|
||||
|
||||
// While the "correct" answer to getting more performance out of
|
||||
// this is "use vector operations", support for that is currently
|
||||
// a work in progress/to be designed.
|
||||
//
|
||||
// Until dedicated assembly can be written leverage the fact that
|
||||
// the callers of this routine ensure that src/dst are valid.
|
||||
|
||||
when ODIN_ARCH == "386" || ODIN_ARCH == "amd64" {
|
||||
// util.PUT_U32_LE/util.U32_LE are not required on little-endian
|
||||
// systems that also happen to not be strict about aligned
|
||||
// memory access.
|
||||
|
||||
dst_p := transmute(^[16]u32)(&dst[0])
|
||||
if src != nil {
|
||||
src_p := transmute(^[16]u32)(&src[0])
|
||||
dst_p[0] = src_p[0] ~ x0
|
||||
dst_p[1] = src_p[1] ~ x1
|
||||
dst_p[2] = src_p[2] ~ x2
|
||||
dst_p[3] = src_p[3] ~ x3
|
||||
dst_p[4] = src_p[4] ~ x4
|
||||
dst_p[5] = src_p[5] ~ x5
|
||||
dst_p[6] = src_p[6] ~ x6
|
||||
dst_p[7] = src_p[7] ~ x7
|
||||
dst_p[8] = src_p[8] ~ x8
|
||||
dst_p[9] = src_p[9] ~ x9
|
||||
dst_p[10] = src_p[10] ~ x10
|
||||
dst_p[11] = src_p[11] ~ x11
|
||||
dst_p[12] = src_p[12] ~ x12
|
||||
dst_p[13] = src_p[13] ~ x13
|
||||
dst_p[14] = src_p[14] ~ x14
|
||||
dst_p[15] = src_p[15] ~ x15
|
||||
src = src[_BLOCK_SIZE:]
|
||||
} else {
|
||||
dst_p[0] = x0
|
||||
dst_p[1] = x1
|
||||
dst_p[2] = x2
|
||||
dst_p[3] = x3
|
||||
dst_p[4] = x4
|
||||
dst_p[5] = x5
|
||||
dst_p[6] = x6
|
||||
dst_p[7] = x7
|
||||
dst_p[8] = x8
|
||||
dst_p[9] = x9
|
||||
dst_p[10] = x10
|
||||
dst_p[11] = x11
|
||||
dst_p[12] = x12
|
||||
dst_p[13] = x13
|
||||
dst_p[14] = x14
|
||||
dst_p[15] = x15
|
||||
}
|
||||
dst = dst[_BLOCK_SIZE:]
|
||||
} else {
|
||||
#no_bounds_check {
|
||||
if src != nil {
|
||||
util.PUT_U32_LE(dst[0:4], util.U32_LE(src[0:4]) ~ x0)
|
||||
util.PUT_U32_LE(dst[4:8], util.U32_LE(src[4:8]) ~ x1)
|
||||
util.PUT_U32_LE(dst[8:12], util.U32_LE(src[8:12]) ~ x2)
|
||||
util.PUT_U32_LE(dst[12:16], util.U32_LE(src[12:16]) ~ x3)
|
||||
util.PUT_U32_LE(dst[16:20], util.U32_LE(src[16:20]) ~ x4)
|
||||
util.PUT_U32_LE(dst[20:24], util.U32_LE(src[20:24]) ~ x5)
|
||||
util.PUT_U32_LE(dst[24:28], util.U32_LE(src[24:28]) ~ x6)
|
||||
util.PUT_U32_LE(dst[28:32], util.U32_LE(src[28:32]) ~ x7)
|
||||
util.PUT_U32_LE(dst[32:36], util.U32_LE(src[32:36]) ~ x8)
|
||||
util.PUT_U32_LE(dst[36:40], util.U32_LE(src[36:40]) ~ x9)
|
||||
util.PUT_U32_LE(dst[40:44], util.U32_LE(src[40:44]) ~ x10)
|
||||
util.PUT_U32_LE(dst[44:48], util.U32_LE(src[44:48]) ~ x11)
|
||||
util.PUT_U32_LE(dst[48:52], util.U32_LE(src[48:52]) ~ x12)
|
||||
util.PUT_U32_LE(dst[52:56], util.U32_LE(src[52:56]) ~ x13)
|
||||
util.PUT_U32_LE(dst[56:60], util.U32_LE(src[56:60]) ~ x14)
|
||||
util.PUT_U32_LE(dst[60:64], util.U32_LE(src[60:64]) ~ x15)
|
||||
src = src[_BLOCK_SIZE:]
|
||||
} else {
|
||||
util.PUT_U32_LE(dst[0:4], x0)
|
||||
util.PUT_U32_LE(dst[4:8], x1)
|
||||
util.PUT_U32_LE(dst[8:12], x2)
|
||||
util.PUT_U32_LE(dst[12:16], x3)
|
||||
util.PUT_U32_LE(dst[16:20], x4)
|
||||
util.PUT_U32_LE(dst[20:24], x5)
|
||||
util.PUT_U32_LE(dst[24:28], x6)
|
||||
util.PUT_U32_LE(dst[28:32], x7)
|
||||
util.PUT_U32_LE(dst[32:36], x8)
|
||||
util.PUT_U32_LE(dst[36:40], x9)
|
||||
util.PUT_U32_LE(dst[40:44], x10)
|
||||
util.PUT_U32_LE(dst[44:48], x11)
|
||||
util.PUT_U32_LE(dst[48:52], x12)
|
||||
util.PUT_U32_LE(dst[52:56], x13)
|
||||
util.PUT_U32_LE(dst[56:60], x14)
|
||||
util.PUT_U32_LE(dst[60:64], x15)
|
||||
}
|
||||
dst = dst[_BLOCK_SIZE:]
|
||||
}
|
||||
}
|
||||
|
||||
// Increment the counter. Overflow checking is done upon
|
||||
// entry into the routine, so a 64-bit increment safely
|
||||
// covers both cases.
|
||||
new_ctr := ((u64(ctx._s[13]) << 32) | u64(ctx._s[12])) + 1
|
||||
x[12] = u32(new_ctr)
|
||||
x[13] = u32(new_ctr >> 32)
|
||||
}
|
||||
}
|
||||
|
||||
_hchacha20 :: proc (dst, key, nonce: []byte) {
|
||||
x0, x1, x2, x3 := _SIGMA_0, _SIGMA_1, _SIGMA_2, _SIGMA_3
|
||||
x4 := util.U32_LE(key[0:4])
|
||||
x5 := util.U32_LE(key[4:8])
|
||||
x6 := util.U32_LE(key[8:12])
|
||||
x7 := util.U32_LE(key[12:16])
|
||||
x8 := util.U32_LE(key[16:20])
|
||||
x9 := util.U32_LE(key[20:24])
|
||||
x10 := util.U32_LE(key[24:28])
|
||||
x11 := util.U32_LE(key[28:32])
|
||||
x12 := util.U32_LE(nonce[0:4])
|
||||
x13 := util.U32_LE(nonce[4:8])
|
||||
x14 := util.U32_LE(nonce[8:12])
|
||||
x15 := util.U32_LE(nonce[12:16])
|
||||
|
||||
for i := _ROUNDS; i > 0; i = i - 2 {
|
||||
// quarterround(x, 0, 4, 8, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = util.ROTL32(x12, 16)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = util.ROTL32(x4, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = util.ROTL32(x12, 8)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = util.ROTL32(x4, 7)
|
||||
|
||||
// quarterround(x, 1, 5, 9, 13)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = util.ROTL32(x13, 16)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = util.ROTL32(x5, 12)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = util.ROTL32(x13, 8)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = util.ROTL32(x5, 7)
|
||||
|
||||
// quarterround(x, 2, 6, 10, 14)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = util.ROTL32(x14, 16)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = util.ROTL32(x6, 12)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = util.ROTL32(x14, 8)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = util.ROTL32(x6, 7)
|
||||
|
||||
// quarterround(x, 3, 7, 11, 15)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = util.ROTL32(x15, 16)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = util.ROTL32(x7, 12)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = util.ROTL32(x15, 8)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = util.ROTL32(x7, 7)
|
||||
|
||||
// quarterround(x, 0, 5, 10, 15)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = util.ROTL32(x15, 16)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = util.ROTL32(x5, 12)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = util.ROTL32(x15, 8)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = util.ROTL32(x5, 7)
|
||||
|
||||
// quarterround(x, 1, 6, 11, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = util.ROTL32(x12, 16)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = util.ROTL32(x6, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = util.ROTL32(x12, 8)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = util.ROTL32(x6, 7)
|
||||
|
||||
// quarterround(x, 2, 7, 8, 13)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = util.ROTL32(x13, 16)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = util.ROTL32(x7, 12)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = util.ROTL32(x13, 8)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = util.ROTL32(x7, 7)
|
||||
|
||||
// quarterround(x, 3, 4, 9, 14)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = util.ROTL32(x14, 16)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = util.ROTL32(x4, 12)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = util.ROTL32(x14, 8)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = util.ROTL32(x4, 7)
|
||||
}
|
||||
|
||||
util.PUT_U32_LE(dst[0:4], x0)
|
||||
util.PUT_U32_LE(dst[4:8], x1)
|
||||
util.PUT_U32_LE(dst[8:12], x2)
|
||||
util.PUT_U32_LE(dst[12:16], x3)
|
||||
util.PUT_U32_LE(dst[16:20], x12)
|
||||
util.PUT_U32_LE(dst[20:24], x13)
|
||||
util.PUT_U32_LE(dst[24:28], x14)
|
||||
util.PUT_U32_LE(dst[28:32], x15)
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package chacha20poly1305
|
||||
|
||||
import "core:crypto"
|
||||
import "core:crypto/chacha20"
|
||||
import "core:crypto/poly1305"
|
||||
import "core:crypto/util"
|
||||
import "core:mem"
|
||||
|
||||
KEY_SIZE :: chacha20.KEY_SIZE
|
||||
NONCE_SIZE :: chacha20.NONCE_SIZE
|
||||
TAG_SIZE :: poly1305.TAG_SIZE
|
||||
|
||||
_P_MAX :: 64 * 0xffffffff // 64 * (2^32-1)
|
||||
|
||||
_validate_common_slice_sizes :: proc (tag, key, nonce, aad, text: []byte) {
|
||||
if len(tag) != TAG_SIZE {
|
||||
panic("crypto/chacha20poly1305: invalid destination tag size")
|
||||
}
|
||||
if len(key) != KEY_SIZE {
|
||||
panic("crypto/chacha20poly1305: invalid key size")
|
||||
}
|
||||
if len(nonce) != NONCE_SIZE {
|
||||
panic("crypto/chacha20poly1305: invalid nonce size")
|
||||
}
|
||||
|
||||
#assert(size_of(int) == 8 || size_of(int) <= 4)
|
||||
when size_of(int) == 8 {
|
||||
// A_MAX = 2^64 - 1 due to the length field limit.
|
||||
// P_MAX = 64 * (2^32 - 1) due to the IETF ChaCha20 counter limit.
|
||||
//
|
||||
// A_MAX is limited by size_of(int), so there is no need to
|
||||
// enforce it. P_MAX only needs to be checked on 64-bit targets,
|
||||
// for reasons that should be obvious.
|
||||
if text_len := len(text); text_len > _P_MAX {
|
||||
panic("crypto/chacha20poly1305: oversized src data")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_PAD: [16]byte
|
||||
_update_mac_pad16 :: #force_inline proc (ctx: ^poly1305.Context, x_len: int) {
|
||||
if pad_len := 16 - (x_len & (16-1)); pad_len != 16 {
|
||||
poly1305.update(ctx, _PAD[:pad_len])
|
||||
}
|
||||
}
|
||||
|
||||
encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) {
|
||||
_validate_common_slice_sizes(tag, key, nonce, aad, plaintext)
|
||||
if len(ciphertext) != len(plaintext) {
|
||||
panic("crypto/chacha20poly1305: invalid destination ciphertext size")
|
||||
}
|
||||
|
||||
stream_ctx: chacha20.Context = ---
|
||||
chacha20.init(&stream_ctx, key, nonce)
|
||||
|
||||
// otk = poly1305_key_gen(key, nonce)
|
||||
otk: [poly1305.KEY_SIZE]byte = ---
|
||||
chacha20.keystream_bytes(&stream_ctx, otk[:])
|
||||
mac_ctx: poly1305.Context = ---
|
||||
poly1305.init(&mac_ctx, otk[:])
|
||||
mem.zero_explicit(&otk, size_of(otk))
|
||||
|
||||
aad_len, ciphertext_len := len(aad), len(ciphertext)
|
||||
|
||||
// There is nothing preventing aad and ciphertext from overlapping
|
||||
// so auth the AAD before encrypting (slightly different from the
|
||||
// RFC, since the RFC encrypts into a new buffer).
|
||||
//
|
||||
// mac_data = aad | pad16(aad)
|
||||
poly1305.update(&mac_ctx, aad)
|
||||
_update_mac_pad16(&mac_ctx, aad_len)
|
||||
|
||||
// ciphertext = chacha20_encrypt(key, 1, nonce, plaintext)
|
||||
chacha20.seek(&stream_ctx, 1)
|
||||
chacha20.xor_bytes(&stream_ctx, ciphertext, plaintext)
|
||||
chacha20.reset(&stream_ctx) // Don't need the stream context anymore.
|
||||
|
||||
// mac_data |= ciphertext | pad16(ciphertext)
|
||||
poly1305.update(&mac_ctx, ciphertext)
|
||||
_update_mac_pad16(&mac_ctx, ciphertext_len)
|
||||
|
||||
// mac_data |= num_to_8_le_bytes(aad.length)
|
||||
// mac_data |= num_to_8_le_bytes(ciphertext.length)
|
||||
l_buf := otk[0:16] // Reuse the scratch buffer.
|
||||
util.PUT_U64_LE(l_buf[0:8], u64(aad_len))
|
||||
util.PUT_U64_LE(l_buf[8:16], u64(ciphertext_len))
|
||||
poly1305.update(&mac_ctx, l_buf)
|
||||
|
||||
// tag = poly1305_mac(mac_data, otk)
|
||||
poly1305.final(&mac_ctx, tag) // Implicitly sanitizes context.
|
||||
}
|
||||
|
||||
decrypt :: proc (plaintext, tag, key, nonce, aad, ciphertext: []byte) -> bool {
|
||||
_validate_common_slice_sizes(tag, key, nonce, aad, ciphertext)
|
||||
if len(ciphertext) != len(plaintext) {
|
||||
panic("crypto/chacha20poly1305: invalid destination plaintext size")
|
||||
}
|
||||
|
||||
// Note: Unlike encrypt, this can fail early, so use defer for
|
||||
// sanitization rather than assuming control flow reaches certain
|
||||
// points where needed.
|
||||
|
||||
stream_ctx: chacha20.Context = ---
|
||||
chacha20.init(&stream_ctx, key, nonce)
|
||||
|
||||
// otk = poly1305_key_gen(key, nonce)
|
||||
otk: [poly1305.KEY_SIZE]byte = ---
|
||||
chacha20.keystream_bytes(&stream_ctx, otk[:])
|
||||
defer chacha20.reset(&stream_ctx)
|
||||
|
||||
mac_ctx: poly1305.Context = ---
|
||||
poly1305.init(&mac_ctx, otk[:])
|
||||
defer mem.zero_explicit(&otk, size_of(otk))
|
||||
|
||||
aad_len, ciphertext_len := len(aad), len(ciphertext)
|
||||
|
||||
// mac_data = aad | pad16(aad)
|
||||
// mac_data |= ciphertext | pad16(ciphertext)
|
||||
// mac_data |= num_to_8_le_bytes(aad.length)
|
||||
// mac_data |= num_to_8_le_bytes(ciphertext.length)
|
||||
poly1305.update(&mac_ctx, aad)
|
||||
_update_mac_pad16(&mac_ctx, aad_len)
|
||||
poly1305.update(&mac_ctx, ciphertext)
|
||||
_update_mac_pad16(&mac_ctx, ciphertext_len)
|
||||
l_buf := otk[0:16] // Reuse the scratch buffer.
|
||||
util.PUT_U64_LE(l_buf[0:8], u64(aad_len))
|
||||
util.PUT_U64_LE(l_buf[8:16], u64(ciphertext_len))
|
||||
poly1305.update(&mac_ctx, l_buf)
|
||||
|
||||
// tag = poly1305_mac(mac_data, otk)
|
||||
derived_tag := otk[0:poly1305.TAG_SIZE] // Reuse the scratch buffer again.
|
||||
poly1305.final(&mac_ctx, derived_tag) // Implicitly sanitizes context.
|
||||
|
||||
// Validate the tag in constant time.
|
||||
if crypto.compare_constant_time(tag, derived_tag) != 1 {
|
||||
// Zero out the plaintext, as a defense in depth measure.
|
||||
mem.zero_explicit(raw_data(plaintext), ciphertext_len)
|
||||
return false
|
||||
}
|
||||
|
||||
// plaintext = chacha20_decrypt(key, 1, nonce, ciphertext)
|
||||
chacha20.seek(&stream_ctx, 1)
|
||||
chacha20.xor_bytes(&stream_ctx, plaintext, ciphertext)
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package crypto
|
||||
|
||||
import "core:mem"
|
||||
|
||||
// compare_constant_time returns 1 iff a and b are equal, 0 otherwise.
|
||||
//
|
||||
// The execution time of this routine is constant regardless of the contents
|
||||
// of the slices being compared, as long as the length of the slices is equal.
|
||||
// If the length of the two slices is different, it will early-return 0.
|
||||
compare_constant_time :: proc "contextless" (a, b: []byte) -> int {
|
||||
// If the length of the slices is different, early return.
|
||||
//
|
||||
// This leaks the fact that the slices have a different length,
|
||||
// but the routine is primarily intended for comparing things
|
||||
// like MACS and password digests.
|
||||
n := len(a)
|
||||
if n != len(b) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return compare_byte_ptrs_constant_time(raw_data(a), raw_data(b), n)
|
||||
}
|
||||
|
||||
// compare_byte_ptrs_constant_time returns 1 iff the bytes pointed to by
|
||||
// a and b are equal, 0 otherwise.
|
||||
//
|
||||
// The execution time of this routine is constant regardless of the
|
||||
// contents of the memory being compared.
|
||||
compare_byte_ptrs_constant_time :: proc "contextless" (a, b: ^byte, n: int) -> int {
|
||||
x := mem.slice_ptr(a, n)
|
||||
y := mem.slice_ptr(b, n)
|
||||
|
||||
v: byte
|
||||
for i in 0..<n {
|
||||
v |= x[i] ~ y[i]
|
||||
}
|
||||
|
||||
// After the loop, v == 0 iff a == b. The subtraction will underflow
|
||||
// iff v == 0, setting the sign-bit, which gets returned.
|
||||
return int((u32(v)-1) >> 31)
|
||||
}
|
||||
|
||||
// rand_bytes fills the dst buffer with cryptographic entropy taken from
|
||||
// the system entropy source. This routine will block if the system entropy
|
||||
// source is not ready yet. All system entropy source failures are treated
|
||||
// as catastrophic, resulting in a panic.
|
||||
rand_bytes :: proc (dst: []byte) {
|
||||
// zero-fill the buffer first
|
||||
mem.zero_explicit(raw_data(dst), len(dst))
|
||||
|
||||
_rand_bytes(dst)
|
||||
}
|
||||
+113
-210
@@ -6,7 +6,6 @@ package gost
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the GOST hashing algorithm, as defined in RFC 5831 <https://datatracker.ietf.org/doc/html/rfc5831>
|
||||
*/
|
||||
@@ -15,42 +14,6 @@ import "core:mem"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
_assign_hash_vtable(ctx)
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_32 = hash_bytes_odin
|
||||
ctx.hash_file_32 = hash_file_odin
|
||||
ctx.hash_stream_32 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since MD2 is not available in Botan
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_GOST)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
@@ -64,22 +27,44 @@ hash_string :: proc(data: string) -> [32]byte {
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [32]byte {
|
||||
_create_gost_ctx()
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash: [32]byte
|
||||
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 from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_gost_ctx()
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash: [32]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_gost_ctx()
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -93,85 +78,77 @@ hash :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
init :: proc "contextless" (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 },
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, 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
|
||||
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)
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_gost_ctx :: #force_inline proc() {
|
||||
ctx: Gost_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._32
|
||||
}
|
||||
update :: proc(ctx: ^Gost_Context, data: []byte) {
|
||||
length := byte(len(data))
|
||||
j: byte
|
||||
|
||||
@(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)
|
||||
i := ctx.partial_bytes
|
||||
for i < 32 && j < length {
|
||||
ctx.partial[i] = data[j]
|
||||
i, j = i + 1, j + 1
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
|
||||
update_odin(&c, data)
|
||||
if i < 32 {
|
||||
ctx.partial_bytes = i
|
||||
return
|
||||
}
|
||||
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_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
final :: proc(ctx: ^Gost_Context, hash: []byte) {
|
||||
if ctx.partial_bytes > 0 {
|
||||
mem.set(&ctx.partial[ctx.partial_bytes], 0, 32 - int(ctx.partial_bytes))
|
||||
bytes(ctx, ctx.partial[:], u32(ctx.partial_bytes) << 3)
|
||||
}
|
||||
|
||||
compress(ctx.hash[:], ctx.len[:])
|
||||
compress(ctx.hash[:], ctx.sum[:])
|
||||
|
||||
for i, j := 0, 0; i < 8; i, j = i + 1, j + 4 {
|
||||
hash[j] = byte(ctx.hash[i])
|
||||
hash[j + 1] = byte(ctx.hash[i] >> 8)
|
||||
hash[j + 2] = byte(ctx.hash[i] >> 16)
|
||||
hash[j + 3] = byte(ctx.hash[i] >> 24)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,12 +164,12 @@ Gost_Context :: struct {
|
||||
partial_bytes: byte,
|
||||
}
|
||||
|
||||
SBOX_1 : [256]u32
|
||||
SBOX_2 : [256]u32
|
||||
SBOX_3 : [256]u32
|
||||
SBOX_4 : [256]u32
|
||||
SBOX_1: [256]u32
|
||||
SBOX_2: [256]u32
|
||||
SBOX_3: [256]u32
|
||||
SBOX_4: [256]u32
|
||||
|
||||
GOST_ENCRYPT_ROUND :: #force_inline proc "contextless"(l, r, t, k1, k2: u32) -> (u32, u32, u32) {
|
||||
ENCRYPT_ROUND :: #force_inline proc "contextless" (l, r, t, k1, k2: u32) -> (u32, u32, u32) {
|
||||
l, r, t := l, r, t
|
||||
t = (k1) + r
|
||||
l ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24]
|
||||
@@ -201,30 +178,30 @@ GOST_ENCRYPT_ROUND :: #force_inline proc "contextless"(l, r, t, k1, k2: u32) ->
|
||||
return l, r, t
|
||||
}
|
||||
|
||||
GOST_ENCRYPT :: #force_inline proc "contextless"(a, b, c: u32, key: []u32) -> (l, r, t: u32) {
|
||||
l, r, t = GOST_ENCRYPT_ROUND(a, b, c, key[0], key[1])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[7], key[6])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[5], key[4])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[3], key[2])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[1], key[0])
|
||||
ENCRYPT :: #force_inline proc "contextless" (a, b, c: u32, key: []u32) -> (l, r, t: u32) {
|
||||
l, r, t = ENCRYPT_ROUND(a, b, c, key[0], key[1])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[7], key[6])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[5], key[4])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[3], key[2])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[1], key[0])
|
||||
t = r
|
||||
r = l
|
||||
l = t
|
||||
return
|
||||
}
|
||||
|
||||
gost_bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
|
||||
bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
|
||||
a, c: u32
|
||||
m: [8]u32
|
||||
|
||||
@@ -237,14 +214,14 @@ gost_bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
|
||||
c = c < a ? 1 : 0
|
||||
}
|
||||
|
||||
gost_compress(ctx.hash[:], m[:])
|
||||
compress(ctx.hash[:], m[:])
|
||||
ctx.len[0] += bits
|
||||
if ctx.len[0] < bits {
|
||||
ctx.len[1] += 1
|
||||
}
|
||||
}
|
||||
|
||||
gost_compress :: proc(h, m: []u32) {
|
||||
compress :: proc(h, m: []u32) {
|
||||
key, u, v, w, s: [8]u32
|
||||
|
||||
copy(u[:], h)
|
||||
@@ -272,7 +249,7 @@ gost_compress :: proc(h, m: []u32) {
|
||||
r := h[i]
|
||||
l := h[i + 1]
|
||||
t: u32
|
||||
l, r, t = GOST_ENCRYPT(l, r, 0, key[:])
|
||||
l, r, t = ENCRYPT(l, r, 0, key[:])
|
||||
|
||||
s[i] = r
|
||||
s[i + 1] = l
|
||||
@@ -380,78 +357,4 @@ gost_compress :: proc(h, m: []u32) {
|
||||
h[7] = v[0] ~ (v[0] >> 16) ~ (v[1] << 16) ~ (v[1] >> 16) ~ (v[2] << 16) ~
|
||||
(v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ v[4] ~ (v[5] >> 16) ~ v[5] ~
|
||||
(v[6] << 16) ~ (v[6] >> 16) ~ (v[7] << 16) ~ v[7]
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Gost_Context) {
|
||||
sbox: [8][16]u32 = {
|
||||
{ 10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15 },
|
||||
{ 5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8 },
|
||||
{ 7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13 },
|
||||
{ 4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3 },
|
||||
{ 7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5 },
|
||||
{ 7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3 },
|
||||
{ 13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11 },
|
||||
{ 1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12 },
|
||||
}
|
||||
|
||||
i := 0
|
||||
for a := 0; a < 16; a += 1 {
|
||||
ax := sbox[1][a] << 15
|
||||
bx := sbox[3][a] << 23
|
||||
cx := sbox[5][a]
|
||||
cx = (cx >> 1) | (cx << 31)
|
||||
dx := sbox[7][a] << 7
|
||||
for b := 0; b < 16; b, i = b + 1, i + 1 {
|
||||
SBOX_1[i] = ax | (sbox[0][b] << 11)
|
||||
SBOX_2[i] = bx | (sbox[2][b] << 19)
|
||||
SBOX_3[i] = cx | (sbox[4][b] << 27)
|
||||
SBOX_4[i] = dx | (sbox[6][b] << 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Gost_Context, data: []byte) {
|
||||
length := byte(len(data))
|
||||
j: byte
|
||||
|
||||
i := ctx.partial_bytes
|
||||
for i < 32 && j < length {
|
||||
ctx.partial[i] = data[j]
|
||||
i, j = i + 1, j + 1
|
||||
}
|
||||
|
||||
if i < 32 {
|
||||
ctx.partial_bytes = i
|
||||
return
|
||||
}
|
||||
gost_bytes(ctx, ctx.partial[:], 256)
|
||||
|
||||
for (j + 32) < length {
|
||||
gost_bytes(ctx, data[j:], 256)
|
||||
j += 32
|
||||
}
|
||||
|
||||
i = 0
|
||||
for j < length {
|
||||
ctx.partial[i] = data[j]
|
||||
i, j = i + 1, j + 1
|
||||
}
|
||||
ctx.partial_bytes = i
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Gost_Context, hash: []byte) {
|
||||
if ctx.partial_bytes > 0 {
|
||||
mem.set(&ctx.partial[ctx.partial_bytes], 0, 32 - int(ctx.partial_bytes))
|
||||
gost_bytes(ctx, ctx.partial[:], u32(ctx.partial_bytes) << 3)
|
||||
}
|
||||
|
||||
gost_compress(ctx.hash[:], ctx.len[:])
|
||||
gost_compress(ctx.hash[:], ctx.sum[:])
|
||||
|
||||
for i, j := 0, 0; i < 8; i, j = i + 1, j + 4 {
|
||||
hash[j] = byte(ctx.hash[i])
|
||||
hash[j + 1] = byte(ctx.hash[i] >> 8)
|
||||
hash[j + 2] = byte(ctx.hash[i] >> 16)
|
||||
hash[j + 3] = byte(ctx.hash[i] >> 24)
|
||||
}
|
||||
}
|
||||
+196
-362
@@ -6,7 +6,6 @@ package groestl
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the GROESTL hashing algorithm, as defined in <http://www.groestl.info/Groestl.zip>
|
||||
*/
|
||||
@@ -14,70 +13,6 @@ package groestl
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since GROESTL is not available in Botan
|
||||
@(warning="GROESTL is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_groestl_ctx :: #force_inline proc(size: _ctx.Hash_Size) {
|
||||
ctx: Groestl_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = size
|
||||
#partial switch size {
|
||||
case ._28: ctx.hashbitlen = 224
|
||||
case ._32: ctx.hashbitlen = 256
|
||||
case ._48: ctx.hashbitlen = 384
|
||||
case ._64: ctx.hashbitlen = 512
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
@@ -91,22 +26,46 @@ hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_groestl_ctx(._28)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
hash: [28]byte
|
||||
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 from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_groestl_ctx(._28)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
hash: [28]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
_create_groestl_ctx(._28)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -125,22 +84,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_groestl_ctx(._32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash: [32]byte
|
||||
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 from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_groestl_ctx(._32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash: [32]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_groestl_ctx(._32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -159,22 +142,46 @@ hash_string_384 :: proc(data: string) -> [48]byte {
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
|
||||
_create_groestl_ctx(._48)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
hash: [48]byte
|
||||
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 from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_groestl_ctx(._48)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
hash: [48]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
_create_groestl_ctx(._48)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -193,22 +200,46 @@ hash_string_512 :: proc(data: string) -> [64]byte {
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
|
||||
_create_groestl_ctx(._64)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash: [64]byte
|
||||
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 from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_groestl_ctx(._64)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash: [64]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_groestl_ctx(._64)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -222,201 +253,101 @@ hash_512 :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
init :: proc(ctx: ^Groestl_Context) {
|
||||
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
|
||||
ctx.columns = 8
|
||||
ctx.statesize = 64
|
||||
} 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) {
|
||||
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
|
||||
update :: 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]
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
if ctx.buf_ptr < ctx.statesize {
|
||||
if rem != 0 {
|
||||
ctx.bits_in_last_byte = rem
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
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 {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
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
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
for ctx.buf_ptr < ctx.statesize - 8 {
|
||||
ctx.buffer[ctx.buf_ptr] = 0
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
output_transformation(ctx)
|
||||
|
||||
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)
|
||||
for i, j := ctx.statesize - hashbytelen , 0; i < ctx.statesize; i, j = i + 1, j + 1 {
|
||||
hash[j] = ctx.chaining[i % 8][i / 8]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
}
|
||||
+584
-479
File diff suppressed because it is too large
Load Diff
+214
-380
@@ -6,7 +6,6 @@ package jh
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the JH hashing algorithm, as defined in <https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html>
|
||||
*/
|
||||
@@ -14,70 +13,6 @@ package jh
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since JH is not available in Botan
|
||||
@(warning="JH is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_jh_ctx :: #force_inline proc(size: _ctx.Hash_Size) {
|
||||
ctx: Jh_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = size
|
||||
#partial switch size {
|
||||
case ._28: ctx.hashbitlen = 224
|
||||
case ._32: ctx.hashbitlen = 256
|
||||
case ._48: ctx.hashbitlen = 384
|
||||
case ._64: ctx.hashbitlen = 512
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
@@ -91,22 +26,46 @@ hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_jh_ctx(._28)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
hash: [28]byte
|
||||
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 from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_jh_ctx(._28)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
hash: [28]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
_create_jh_ctx(._28)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -125,22 +84,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_jh_ctx(._32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash: [32]byte
|
||||
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 from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_jh_ctx(._32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash: [32]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_jh_ctx(._32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -159,22 +142,46 @@ hash_string_384 :: proc(data: string) -> [48]byte {
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
|
||||
_create_jh_ctx(._48)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
hash: [48]byte
|
||||
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 from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_jh_ctx(._48)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
hash: [48]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
_create_jh_ctx(._48)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -193,22 +200,46 @@ hash_string_512 :: proc(data: string) -> [64]byte {
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
|
||||
_create_jh_ctx(._64)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash: [64]byte
|
||||
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 from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_jh_ctx(._64)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash: [64]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_jh_ctx(._64)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -222,201 +253,98 @@ hash_512 :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
init :: proc(ctx: ^Jh_Context) {
|
||||
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) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
update :: proc(ctx: ^Jh_Context, data: []byte) {
|
||||
databitlen := u64(len(data)) * 8
|
||||
ctx.databitlen += databitlen
|
||||
i := u64(0)
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
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
|
||||
}
|
||||
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])
|
||||
}
|
||||
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)
|
||||
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 {
|
||||
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3) + 1])
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
ctx.buffer_size = databitlen
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
final :: proc(ctx: ^Jh_Context, hash: []byte) {
|
||||
if ctx.databitlen & 0x1ff == 0 {
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
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
|
||||
F8(ctx)
|
||||
} 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
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
ctx.buffer[(ctx.databitlen & 0x1ff) >> 3] |= 1 << (7 - (ctx.databitlen & 7))
|
||||
F8(ctx)
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
|
||||
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
|
||||
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
|
||||
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
|
||||
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
|
||||
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
|
||||
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
|
||||
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
|
||||
F8(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
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])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,7 +352,7 @@ _final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
JH implementation
|
||||
*/
|
||||
|
||||
JH_ROUNDCONSTANT_ZERO := [64]byte {
|
||||
ROUNDCONSTANT_ZERO := [64]byte {
|
||||
0x6, 0xa, 0x0, 0x9, 0xe, 0x6, 0x6, 0x7,
|
||||
0xf, 0x3, 0xb, 0xc, 0xc, 0x9, 0x0, 0x8,
|
||||
0xb, 0x2, 0xf, 0xb, 0x1, 0x3, 0x6, 0x6,
|
||||
@@ -435,7 +363,7 @@ JH_ROUNDCONSTANT_ZERO := [64]byte {
|
||||
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},
|
||||
{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,
|
||||
}
|
||||
|
||||
JH_E8_finaldegroup :: proc(ctx: ^Jh_Context) {
|
||||
E8_finaldegroup :: proc(ctx: ^Jh_Context) {
|
||||
t0,t1,t2,t3: byte
|
||||
tem: [256]byte
|
||||
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
|
||||
t: byte
|
||||
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 {
|
||||
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
|
||||
tem, roundconstant_expanded: [256]byte
|
||||
for i := u32(0); i < 256; i += 1 {
|
||||
roundconstant_expanded[i] = (ctx.roundconstant[i >> 2] >> (3 - (i & 3)) ) & 1
|
||||
}
|
||||
for i := 0; i < 256; i += 1 {
|
||||
tem[i] = JH_S[roundconstant_expanded[i]][ctx.A[i]]
|
||||
tem[i] = SBOX[roundconstant_expanded[i]][ctx.A[i]]
|
||||
}
|
||||
for i := 0; i < 256; i += 2 {
|
||||
tem[i+1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf
|
||||
@@ -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
|
||||
tem: [256]byte
|
||||
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 {
|
||||
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 {
|
||||
JH_R8(ctx)
|
||||
jh_update_roundconstant(ctx)
|
||||
R8(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 {
|
||||
ctx.H[i] ~= ctx.buffer[i]
|
||||
}
|
||||
JH_E8(ctx)
|
||||
E8(ctx)
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.H[i + 64] ~= ctx.buffer[i]
|
||||
}
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Jh_Context) {
|
||||
ctx.H[1] = byte(ctx.hashbitlen) & 0xff
|
||||
ctx.H[0] = byte(ctx.hashbitlen >> 8) & 0xff
|
||||
JH_F8(ctx)
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Jh_Context, data: []byte) {
|
||||
databitlen := u64(len(data)) * 8
|
||||
ctx.databitlen += databitlen
|
||||
i := u64(0)
|
||||
|
||||
if (ctx.buffer_size > 0) && ((ctx.buffer_size + databitlen) < 512) {
|
||||
if (databitlen & 7) == 0 {
|
||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
|
||||
} else {
|
||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3) + 1])
|
||||
}
|
||||
ctx.buffer_size += databitlen
|
||||
databitlen = 0
|
||||
}
|
||||
|
||||
if (ctx.buffer_size > 0 ) && ((ctx.buffer_size + databitlen) >= 512) {
|
||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
|
||||
i = 64 - (ctx.buffer_size >> 3)
|
||||
databitlen = databitlen - (512 - ctx.buffer_size)
|
||||
JH_F8(ctx)
|
||||
ctx.buffer_size = 0
|
||||
}
|
||||
|
||||
for databitlen >= 512 {
|
||||
copy(ctx.buffer[:], data[i:i + 64])
|
||||
JH_F8(ctx)
|
||||
i += 64
|
||||
databitlen -= 512
|
||||
}
|
||||
|
||||
if databitlen > 0 {
|
||||
if (databitlen & 7) == 0 {
|
||||
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3)])
|
||||
} else {
|
||||
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3) + 1])
|
||||
}
|
||||
ctx.buffer_size = databitlen
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Jh_Context, hash: []byte) {
|
||||
if ctx.databitlen & 0x1ff == 0 {
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
ctx.buffer[0] = 0x80
|
||||
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
|
||||
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
|
||||
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
|
||||
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
|
||||
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
|
||||
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
|
||||
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
|
||||
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
|
||||
JH_F8(ctx)
|
||||
} else {
|
||||
if ctx.buffer_size & 7 == 0 {
|
||||
for i := (ctx.databitlen & 0x1ff) >> 3; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
} else {
|
||||
for i := ((ctx.databitlen & 0x1ff) >> 3) + 1; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
}
|
||||
ctx.buffer[(ctx.databitlen & 0x1ff) >> 3] |= 1 << (7 - (ctx.databitlen & 7))
|
||||
JH_F8(ctx)
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
|
||||
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
|
||||
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
|
||||
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
|
||||
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
|
||||
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
|
||||
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
|
||||
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
|
||||
JH_F8(ctx)
|
||||
}
|
||||
switch ctx.hashbitlen {
|
||||
case 224: copy(hash[:], ctx.H[100:128])
|
||||
case 256: copy(hash[:], ctx.H[96:128])
|
||||
case 384: copy(hash[:], ctx.H[80:128])
|
||||
case 512: copy(hash[:], ctx.H[64:128])
|
||||
}
|
||||
}
|
||||
|
||||
+137
-285
@@ -6,7 +6,6 @@ package keccak
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the Keccak hashing algorithm.
|
||||
This is done because the padding in the SHA3 standard was changed by the NIST, resulting in a different output.
|
||||
@@ -15,57 +14,8 @@ package keccak
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_sha3"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_KECCAK)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
@@ -80,22 +30,48 @@ hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_keccak_ctx(28)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
hash: [28]byte
|
||||
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 from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_keccak_ctx(28)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
hash: [28]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
_create_keccak_ctx(28)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -114,22 +90,48 @@ hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_keccak_ctx(32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash: [32]byte
|
||||
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 from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_keccak_ctx(32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash: [32]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_keccak_ctx(32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -148,22 +150,48 @@ hash_string_384 :: proc(data: string) -> [48]byte {
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
|
||||
_create_keccak_ctx(48)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
hash: [48]byte
|
||||
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 from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_keccak_ctx(48)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
hash: [48]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
_create_keccak_ctx(48)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -182,22 +210,48 @@ hash_string_512 :: proc(data: string) -> [64]byte {
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
|
||||
_create_keccak_ctx(64)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash: [64]byte
|
||||
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 from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_keccak_ctx(64)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash: [64]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_keccak_ctx(64)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -211,219 +265,17 @@ hash_512 :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
Sha3_Context :: _sha3.Sha3_Context
|
||||
|
||||
init :: proc(ctx: ^_sha3.Sha3_Context) {
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
|
||||
_sha3.update(ctx, 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
|
||||
_hash_impl.internal_ctx = ctx
|
||||
switch mdlen {
|
||||
case 28: _hash_impl.hash_size = ._28
|
||||
case 32: _hash_impl.hash_size = ._32
|
||||
case 48: _hash_impl.hash_size = ._48
|
||||
case 64: _hash_impl.hash_size = ._64
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._28: _create_keccak_ctx(28)
|
||||
case ._32: _create_keccak_ctx(32)
|
||||
case ._48: _create_keccak_ctx(48)
|
||||
case ._64: _create_keccak_ctx(64)
|
||||
}
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.final_odin(&c, hash)
|
||||
}
|
||||
final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
|
||||
_sha3.final(ctx, hash)
|
||||
}
|
||||
|
||||
+49
-151
@@ -6,7 +6,6 @@ package md2
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the MD2 hashing algorithm, as defined in RFC 1319 <https://datatracker.ietf.org/doc/html/rfc1319>
|
||||
*/
|
||||
@@ -14,48 +13,6 @@ package md2
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin
|
||||
ctx.hash_file_16 = hash_file_odin
|
||||
ctx.hash_stream_16 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since MD2 is not available in Botan
|
||||
@(warning="MD2 is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
@@ -69,22 +26,44 @@ hash_string :: proc(data: string) -> [16]byte {
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [16]byte {
|
||||
_create_md2_ctx()
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
hash: [16]byte
|
||||
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 from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_md2_ctx()
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
hash: [16]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
_create_md2_ctx()
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -98,85 +77,32 @@ hash :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
@(warning="Init is a no-op for MD2")
|
||||
init :: proc(ctx: ^Md2_Context) {
|
||||
// No action needed here
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: 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 :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
final :: 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
|
||||
}
|
||||
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) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_md2_ctx :: #force_inline proc() {
|
||||
ctx: Md2_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._16
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_md2_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
transform(ctx, ctx.data[:])
|
||||
transform(ctx, ctx.checksum[:])
|
||||
for i := 0; i < 16; i += 1 {
|
||||
hash[i] = ctx.state[i]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,31 +158,3 @@ transform :: proc(ctx: ^Md2_Context, data: []byte) {
|
||||
t = ctx.checksum[j]
|
||||
}
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Md2_Context) {
|
||||
// No action needed here
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Md2_Context, data: []byte) {
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.data[ctx.datalen] = data[i]
|
||||
ctx.datalen += 1
|
||||
if (ctx.datalen == 16) {
|
||||
transform(ctx, ctx.data[:])
|
||||
ctx.datalen = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Md2_Context, hash: []byte) {
|
||||
to_pad := byte(16 - ctx.datalen)
|
||||
for ctx.datalen < 16 {
|
||||
ctx.data[ctx.datalen] = to_pad
|
||||
ctx.datalen += 1
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
transform(ctx, ctx.checksum[:])
|
||||
for i := 0; i < 16; i += 1 {
|
||||
hash[i] = ctx.state[i]
|
||||
}
|
||||
}
|
||||
+74
-175
@@ -16,47 +16,6 @@ import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin
|
||||
ctx.hash_file_16 = hash_file_odin
|
||||
ctx.hash_stream_16 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_MD4)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
@@ -71,22 +30,44 @@ hash_string :: proc(data: string) -> [16]byte {
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [16]byte {
|
||||
_create_md4_ctx()
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
hash: [16]byte
|
||||
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 from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_md4_ctx()
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
hash: [16]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
_create_md4_ctx()
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -100,85 +81,61 @@ hash :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
init :: proc(ctx: ^Md4_Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
ctx.state[2] = 0x98badcfe
|
||||
ctx.state[3] = 0x10325476
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
update :: 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
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_md4_ctx :: #force_inline proc() {
|
||||
ctx: Md4_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._16
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_md4_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
init_odin(&c)
|
||||
final :: 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)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
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[:])
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,61 +239,3 @@ transform :: proc(ctx: ^Md4_Context, data: []byte) {
|
||||
ctx.state[2] += c
|
||||
ctx.state[3] += d
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Md4_Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
ctx.state[2] = 0x98badcfe
|
||||
ctx.state[3] = 0x10325476
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Md4_Context, data: []byte) {
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.data[ctx.datalen] = data[i]
|
||||
ctx.datalen += 1
|
||||
if(ctx.datalen == BLOCK_SIZE) {
|
||||
transform(ctx, ctx.data[:])
|
||||
ctx.bitlen += 512
|
||||
ctx.datalen = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Md4_Context, hash: []byte) {
|
||||
i := ctx.datalen
|
||||
if ctx.datalen < 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < 56 {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
} else if ctx.datalen >= 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < BLOCK_SIZE {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
mem.set(&ctx.data, 0, 56)
|
||||
}
|
||||
|
||||
ctx.bitlen += u64(ctx.datalen * 8)
|
||||
ctx.data[56] = byte(ctx.bitlen)
|
||||
ctx.data[57] = byte(ctx.bitlen >> 8)
|
||||
ctx.data[58] = byte(ctx.bitlen >> 16)
|
||||
ctx.data[59] = byte(ctx.bitlen >> 24)
|
||||
ctx.data[60] = byte(ctx.bitlen >> 32)
|
||||
ctx.data[61] = byte(ctx.bitlen >> 40)
|
||||
ctx.data[62] = byte(ctx.bitlen >> 48)
|
||||
ctx.data[63] = byte(ctx.bitlen >> 56)
|
||||
transform(ctx, ctx.data[:])
|
||||
|
||||
for i = 0; i < 4; i += 1 {
|
||||
hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
|
||||
}
|
||||
}
|
||||
|
||||
+75
-177
@@ -6,7 +6,6 @@ package md5
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the MD5 hashing algorithm, as defined in RFC 1321 <https://datatracker.ietf.org/doc/html/rfc1321>
|
||||
*/
|
||||
@@ -16,47 +15,6 @@ import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin
|
||||
ctx.hash_file_16 = hash_file_odin
|
||||
ctx.hash_stream_16 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_MD5)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
@@ -71,22 +29,44 @@ hash_string :: proc(data: string) -> [16]byte {
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [16]byte {
|
||||
_create_md5_ctx()
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
hash: [16]byte
|
||||
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 from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_md5_ctx()
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
hash: [16]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
_create_md5_ctx()
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -100,85 +80,63 @@ hash :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
init :: proc(ctx: ^Md5_Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
ctx.state[2] = 0x98badcfe
|
||||
ctx.state[3] = 0x10325476
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
update :: 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
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_md5_ctx :: #force_inline proc() {
|
||||
ctx: Md5_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._16
|
||||
}
|
||||
final :: proc(ctx: ^Md5_Context, hash: []byte){
|
||||
i : u32
|
||||
i = ctx.datalen
|
||||
|
||||
@(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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
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[:])
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
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[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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package poly1305
|
||||
|
||||
import "core:crypto"
|
||||
import "core:crypto/util"
|
||||
import field "core:crypto/_fiat/field_poly1305"
|
||||
import "core:mem"
|
||||
|
||||
KEY_SIZE :: 32
|
||||
TAG_SIZE :: 16
|
||||
|
||||
_BLOCK_SIZE :: 16
|
||||
|
||||
sum :: proc (dst, msg, key: []byte) {
|
||||
ctx: Context = ---
|
||||
|
||||
init(&ctx, key)
|
||||
update(&ctx, msg)
|
||||
final(&ctx, dst)
|
||||
}
|
||||
|
||||
verify :: proc (tag, msg, key: []byte) -> bool {
|
||||
ctx: Context = ---
|
||||
derived_tag: [16]byte = ---
|
||||
|
||||
if len(tag) != TAG_SIZE {
|
||||
panic("crypto/poly1305: invalid tag size")
|
||||
}
|
||||
|
||||
init(&ctx, key)
|
||||
update(&ctx, msg)
|
||||
final(&ctx, derived_tag[:])
|
||||
|
||||
return crypto.compare_constant_time(derived_tag[:], tag) == 1
|
||||
}
|
||||
|
||||
Context :: struct {
|
||||
_r: field.Tight_Field_Element,
|
||||
_a: field.Tight_Field_Element,
|
||||
_s: field.Tight_Field_Element,
|
||||
|
||||
_buffer: [_BLOCK_SIZE]byte,
|
||||
_leftover: int,
|
||||
|
||||
_is_initialized: bool,
|
||||
}
|
||||
|
||||
init :: proc (ctx: ^Context, key: []byte) {
|
||||
if len(key) != KEY_SIZE {
|
||||
panic("crypto/poly1305: invalid key size")
|
||||
}
|
||||
|
||||
// r = le_bytes_to_num(key[0..15])
|
||||
// r = clamp(r) (r &= 0xffffffc0ffffffc0ffffffc0fffffff)
|
||||
tmp_lo := util.U64_LE(key[0:8]) & 0x0ffffffc0fffffff
|
||||
tmp_hi := util.U64_LE(key[8:16]) & 0xffffffc0ffffffc
|
||||
field.fe_from_u64s(&ctx._r, tmp_lo, tmp_hi)
|
||||
|
||||
// s = le_bytes_to_num(key[16..31])
|
||||
field.fe_from_bytes(&ctx._s, key[16:32], 0)
|
||||
|
||||
// a = 0
|
||||
field.fe_zero(&ctx._a)
|
||||
|
||||
// No leftover in buffer
|
||||
ctx._leftover = 0
|
||||
|
||||
ctx._is_initialized = true
|
||||
}
|
||||
|
||||
update :: proc (ctx: ^Context, data: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
msg := data
|
||||
msg_len := len(data)
|
||||
|
||||
// Handle leftover
|
||||
if ctx._leftover > 0 {
|
||||
want := min(_BLOCK_SIZE - ctx._leftover, msg_len)
|
||||
copy_slice(ctx._buffer[ctx._leftover:], msg[:want])
|
||||
msg_len = msg_len - want
|
||||
msg = msg[want:]
|
||||
ctx._leftover = ctx._leftover + want
|
||||
if ctx._leftover < _BLOCK_SIZE {
|
||||
return
|
||||
}
|
||||
_blocks(ctx, ctx._buffer[:])
|
||||
ctx._leftover = 0
|
||||
}
|
||||
|
||||
// Process full blocks
|
||||
if msg_len >= _BLOCK_SIZE {
|
||||
want := msg_len & (~int(_BLOCK_SIZE - 1))
|
||||
_blocks(ctx, msg[:want])
|
||||
msg = msg[want:]
|
||||
msg_len = msg_len - want
|
||||
}
|
||||
|
||||
// Store leftover
|
||||
if msg_len > 0 {
|
||||
// TODO: While -donna does it this way, I'm fairly sure that
|
||||
// `ctx._leftover == 0` is an invariant at this point.
|
||||
copy(ctx._buffer[ctx._leftover:], msg)
|
||||
ctx._leftover = ctx._leftover + msg_len
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc (ctx: ^Context, dst: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
if len(dst) != TAG_SIZE {
|
||||
panic("poly1305: invalid destination tag size")
|
||||
}
|
||||
|
||||
// Process remaining block
|
||||
if ctx._leftover > 0 {
|
||||
ctx._buffer[ctx._leftover] = 1
|
||||
for i := ctx._leftover + 1; i < _BLOCK_SIZE; i = i + 1 {
|
||||
ctx._buffer[i] = 0
|
||||
}
|
||||
_blocks(ctx, ctx._buffer[:], true)
|
||||
}
|
||||
|
||||
// a += s
|
||||
field.fe_add(field.fe_relax_cast(&ctx._a), &ctx._a, &ctx._s) // _a unreduced
|
||||
field.fe_carry(&ctx._a, field.fe_relax_cast(&ctx._a)) // _a reduced
|
||||
|
||||
// return num_to_16_le_bytes(a)
|
||||
tmp: [32]byte = ---
|
||||
field.fe_to_bytes(&tmp, &ctx._a)
|
||||
copy_slice(dst, tmp[0:16])
|
||||
|
||||
reset(ctx)
|
||||
}
|
||||
|
||||
reset :: proc (ctx: ^Context) {
|
||||
mem.zero_explicit(&ctx._r, size_of(ctx._r))
|
||||
mem.zero_explicit(&ctx._a, size_of(ctx._a))
|
||||
mem.zero_explicit(&ctx._s, size_of(ctx._s))
|
||||
mem.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
|
||||
|
||||
ctx._is_initialized = false
|
||||
}
|
||||
|
||||
_blocks :: proc (ctx: ^Context, msg: []byte, final := false) {
|
||||
n: field.Tight_Field_Element = ---
|
||||
final_byte := byte(!final)
|
||||
|
||||
data := msg
|
||||
data_len := len(data)
|
||||
for data_len >= _BLOCK_SIZE {
|
||||
// n = le_bytes_to_num(msg[((i-1)*16)..*i*16] | [0x01])
|
||||
field.fe_from_bytes(&n, data[:_BLOCK_SIZE], final_byte, false)
|
||||
|
||||
// a += n
|
||||
field.fe_add(field.fe_relax_cast(&ctx._a), &ctx._a, &n) // _a unreduced
|
||||
|
||||
// a = (r * a) % p
|
||||
field.fe_carry_mul(&ctx._a, field.fe_relax_cast(&ctx._a), field.fe_relax_cast(&ctx._r)) // _a reduced
|
||||
|
||||
data = data[_BLOCK_SIZE:]
|
||||
data_len = data_len - _BLOCK_SIZE
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package crypto
|
||||
|
||||
when ODIN_OS != "linux" {
|
||||
_rand_bytes :: proc (dst: []byte) {
|
||||
unimplemented("crypto: rand_bytes not supported on this OS")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package crypto
|
||||
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:sys/unix"
|
||||
|
||||
_MAX_PER_CALL_BYTES :: 33554431 // 2^25 - 1
|
||||
|
||||
_rand_bytes :: proc (dst: []byte) {
|
||||
dst := dst
|
||||
l := len(dst)
|
||||
|
||||
for l > 0 {
|
||||
to_read := min(l, _MAX_PER_CALL_BYTES)
|
||||
ret := unix.sys_getrandom(raw_data(dst), to_read, 0)
|
||||
if ret < 0 {
|
||||
switch os.Errno(-ret) {
|
||||
case os.EINTR:
|
||||
// Call interupted by a signal handler, just retry the
|
||||
// request.
|
||||
continue
|
||||
case os.ENOSYS:
|
||||
// The kernel is apparently prehistoric (< 3.17 circa 2014)
|
||||
// and does not support getrandom.
|
||||
panic("crypto: getrandom not available in kernel")
|
||||
case:
|
||||
// All other failures are things that should NEVER happen
|
||||
// unless the kernel interface changes (ie: the Linux
|
||||
// developers break userland).
|
||||
panic(fmt.tprintf("crypto: getrandom failed: %d", ret))
|
||||
}
|
||||
}
|
||||
|
||||
l -= ret
|
||||
dst = dst[ret:]
|
||||
}
|
||||
}
|
||||
+216
-430
@@ -6,7 +6,6 @@ package ripemd
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation for the RIPEMD hashing algorithm as defined in <https://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
|
||||
*/
|
||||
@@ -15,56 +14,6 @@ import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin_16
|
||||
ctx.hash_file_16 = hash_file_odin_16
|
||||
ctx.hash_stream_16 = hash_stream_odin_16
|
||||
ctx.hash_bytes_20 = hash_bytes_odin_20
|
||||
ctx.hash_file_20 = hash_file_odin_20
|
||||
ctx.hash_stream_20 = hash_stream_odin_20
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_40 = hash_bytes_odin_40
|
||||
ctx.hash_file_40 = hash_file_odin_40
|
||||
ctx.hash_stream_40 = hash_stream_odin_40
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_RIPEMD_160)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
@@ -79,22 +28,44 @@ hash_string_128 :: proc(data: string) -> [16]byte {
|
||||
// hash_bytes_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
|
||||
_create_ripemd_ctx(16)
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
hash: [16]byte
|
||||
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 from its contents
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_ripemd_ctx(16)
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
hash: [16]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
_create_ripemd_ctx(16)
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -113,22 +84,44 @@ hash_string_160 :: proc(data: string) -> [20]byte {
|
||||
// hash_bytes_160 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_160 :: proc(data: []byte) -> [20]byte {
|
||||
_create_ripemd_ctx(20)
|
||||
return _hash_impl->hash_bytes_20(data)
|
||||
hash: [20]byte
|
||||
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 from its contents
|
||||
hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
|
||||
_create_ripemd_ctx(20)
|
||||
return _hash_impl->hash_stream_20(s)
|
||||
hash: [20]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
_create_ripemd_ctx(20)
|
||||
return _hash_impl->hash_file_20(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -147,22 +140,44 @@ hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_ripemd_ctx(32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash: [32]byte
|
||||
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 from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_ripemd_ctx(32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash: [32]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_ripemd_ctx(32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -181,22 +196,44 @@ hash_string_320 :: proc(data: string) -> [40]byte {
|
||||
// hash_bytes_320 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_320 :: proc(data: []byte) -> [40]byte {
|
||||
_create_ripemd_ctx(40)
|
||||
return _hash_impl->hash_bytes_40(data)
|
||||
hash: [40]byte
|
||||
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 from its contents
|
||||
hash_stream_320 :: proc(s: io.Stream) -> ([40]byte, bool) {
|
||||
_create_ripemd_ctx(40)
|
||||
return _hash_impl->hash_stream_40(s)
|
||||
hash: [40]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_320 :: proc(hd: os.Handle, load_at_once := false) -> ([40]byte, bool) {
|
||||
_create_ripemd_ctx(40)
|
||||
return _hash_impl->hash_file_40(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -206,261 +243,122 @@ hash_320 :: proc {
|
||||
hash_string_320,
|
||||
}
|
||||
|
||||
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: 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
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
update :: proc(ctx: ^$T, data: []byte) {
|
||||
ctx.tc += u64(len(data))
|
||||
data := data
|
||||
if ctx.nx > 0 {
|
||||
n := len(data)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
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) {
|
||||
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
|
||||
for i := 0; i < n; i += 1 {
|
||||
ctx.x[ctx.nx + i] = data[i]
|
||||
}
|
||||
}
|
||||
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])
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
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 {
|
||||
return hash, false
|
||||
update(d, tmp[0:64 + 56 - tc % 64])
|
||||
}
|
||||
|
||||
tc <<= 3
|
||||
for i : u32 = 0; i < 8; i += 1 {
|
||||
tmp[i] = byte(tc >> (8 * i))
|
||||
}
|
||||
|
||||
update(d, tmp[0:8])
|
||||
|
||||
when T == Ripemd128_Context {
|
||||
size :: RIPEMD_128_SIZE
|
||||
} else when T == Ripemd160_Context {
|
||||
size :: RIPEMD_160_SIZE
|
||||
} else when T == Ripemd256_Context{
|
||||
size :: RIPEMD_256_SIZE
|
||||
} else when T == Ripemd320_Context{
|
||||
size :: RIPEMD_320_SIZE
|
||||
}
|
||||
|
||||
digest: [size]byte
|
||||
for s, i in d.s {
|
||||
digest[i * 4] = byte(s)
|
||||
digest[i * 4 + 1] = byte(s >> 8)
|
||||
digest[i * 4 + 2] = byte(s >> 16)
|
||||
digest[i * 4 + 3] = byte(s >> 24)
|
||||
}
|
||||
copy(hash[:], digest[:])
|
||||
}
|
||||
|
||||
hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_20(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_20(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [20]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [40]byte {
|
||||
hash: [40]byte
|
||||
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([40]byte, bool) {
|
||||
hash: [40]byte
|
||||
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([40]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_40(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_40(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [40]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_ripemd_ctx :: #force_inline proc(hash_size: int) {
|
||||
switch hash_size {
|
||||
case 16:
|
||||
ctx: Ripemd128_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._16
|
||||
case 20:
|
||||
ctx: Ripemd160_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._20
|
||||
case 32:
|
||||
ctx: Ripemd256_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._32
|
||||
case 40:
|
||||
ctx: Ripemd320_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._40
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._16:
|
||||
_create_ripemd_ctx(16)
|
||||
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
case ._20:
|
||||
_create_ripemd_ctx(20)
|
||||
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
case ._32:
|
||||
_create_ripemd_ctx(32)
|
||||
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
case ._40:
|
||||
_create_ripemd_ctx(40)
|
||||
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._16:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
case ._20:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
case ._32:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
case ._40:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._16:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
case ._20:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
case ._32:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
case ._40:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
RIPEMD implementation
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^$T) {
|
||||
when T == Ripemd128_Context {
|
||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
|
||||
} else when T == Ripemd160_Context {
|
||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
|
||||
} else when T == Ripemd256_Context {
|
||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
|
||||
ctx.s[4], ctx.s[5], ctx.s[6], ctx.s[7] = S5, S6, S7, S8
|
||||
} else when T == Ripemd320_Context {
|
||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
|
||||
ctx.s[5], ctx.s[6], ctx.s[7], ctx.s[8], ctx.s[9] = S5, S6, S7, S8, S9
|
||||
}
|
||||
}
|
||||
|
||||
block :: #force_inline proc (ctx: ^$T, p: []byte) -> int {
|
||||
when T == Ripemd128_Context {
|
||||
return ripemd_128_block(ctx, p)
|
||||
@@ -948,101 +832,3 @@ ripemd_320_block :: proc(ctx: ^$T, p: []byte) -> int {
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^$T, p: []byte) {
|
||||
ctx.tc += u64(len(p))
|
||||
p := p
|
||||
if ctx.nx > 0 {
|
||||
n := len(p)
|
||||
|
||||
when T == Ripemd128_Context {
|
||||
if n > RIPEMD_128_BLOCK_SIZE - ctx.nx {
|
||||
n = RIPEMD_128_BLOCK_SIZE - ctx.nx
|
||||
}
|
||||
} else when T == Ripemd160_Context {
|
||||
if n > RIPEMD_160_BLOCK_SIZE - ctx.nx {
|
||||
n = RIPEMD_160_BLOCK_SIZE - ctx.nx
|
||||
}
|
||||
} else when T == Ripemd256_Context{
|
||||
if n > RIPEMD_256_BLOCK_SIZE - ctx.nx {
|
||||
n = RIPEMD_256_BLOCK_SIZE - ctx.nx
|
||||
}
|
||||
} else when T == Ripemd320_Context{
|
||||
if n > RIPEMD_320_BLOCK_SIZE - ctx.nx {
|
||||
n = RIPEMD_320_BLOCK_SIZE - ctx.nx
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < n; i += 1 {
|
||||
ctx.x[ctx.nx + i] = p[i]
|
||||
}
|
||||
|
||||
ctx.nx += n
|
||||
when T == Ripemd128_Context {
|
||||
if ctx.nx == RIPEMD_128_BLOCK_SIZE {
|
||||
block(ctx, ctx.x[0:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
} else when T == Ripemd160_Context {
|
||||
if ctx.nx == RIPEMD_160_BLOCK_SIZE {
|
||||
block(ctx, ctx.x[0:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
} else when T == Ripemd256_Context{
|
||||
if ctx.nx == RIPEMD_256_BLOCK_SIZE {
|
||||
block(ctx, ctx.x[0:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
} else when T == Ripemd320_Context{
|
||||
if ctx.nx == RIPEMD_320_BLOCK_SIZE {
|
||||
block(ctx, ctx.x[0:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
n := block(ctx, p)
|
||||
p = p[n:]
|
||||
if len(p) > 0 {
|
||||
ctx.nx = copy(ctx.x[:], p)
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^$T, hash: []byte) {
|
||||
d := ctx
|
||||
tc := d.tc
|
||||
tmp: [64]byte
|
||||
tmp[0] = 0x80
|
||||
|
||||
if tc % 64 < 56 {
|
||||
update_odin(d, tmp[0:56 - tc % 64])
|
||||
} else {
|
||||
update_odin(d, tmp[0:64 + 56 - tc % 64])
|
||||
}
|
||||
|
||||
tc <<= 3
|
||||
for i : u32 = 0; i < 8; i += 1 {
|
||||
tmp[i] = byte(tc >> (8 * i))
|
||||
}
|
||||
|
||||
update_odin(d, tmp[0:8])
|
||||
|
||||
when T == Ripemd128_Context {
|
||||
size :: RIPEMD_128_SIZE
|
||||
} else when T == Ripemd160_Context {
|
||||
size :: RIPEMD_160_SIZE
|
||||
} else when T == Ripemd256_Context{
|
||||
size :: RIPEMD_256_SIZE
|
||||
} else when T == Ripemd320_Context{
|
||||
size :: RIPEMD_320_SIZE
|
||||
}
|
||||
|
||||
digest: [size]byte
|
||||
for s, i in d.s {
|
||||
digest[i * 4] = byte(s)
|
||||
digest[i * 4 + 1] = byte(s >> 8)
|
||||
digest[i * 4 + 2] = byte(s >> 16)
|
||||
digest[i * 4 + 3] = byte(s >> 24)
|
||||
}
|
||||
copy(hash[:], digest[:])
|
||||
}
|
||||
|
||||
+84
-187
@@ -6,7 +6,6 @@ package sha1
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the SHA1 hashing algorithm, as defined in RFC 3174 <https://datatracker.ietf.org/doc/html/rfc3174>
|
||||
*/
|
||||
@@ -16,52 +15,10 @@ import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_20 = hash_bytes_odin
|
||||
ctx.hash_file_20 = hash_file_odin
|
||||
ctx.hash_stream_20 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHA1)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [20]byte {
|
||||
@@ -71,22 +28,44 @@ hash_string :: proc(data: string) -> [20]byte {
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [20]byte {
|
||||
_create_sha1_ctx()
|
||||
return _hash_impl->hash_bytes_20(data)
|
||||
hash: [20]byte
|
||||
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 from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([20]byte, bool) {
|
||||
_create_sha1_ctx()
|
||||
return _hash_impl->hash_stream_20(s)
|
||||
hash: [20]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
_create_sha1_ctx()
|
||||
return _hash_impl->hash_file_20(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -100,86 +79,70 @@ hash :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
init :: 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 :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: 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 :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
final :: proc(ctx: ^Sha1_Context, hash: []byte) {
|
||||
i := ctx.datalen
|
||||
|
||||
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) {
|
||||
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
|
||||
if ctx.datalen < 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < 56 {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
return [20]byte{}, false
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < BLOCK_SIZE {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
mem.set(&ctx.data, 0, 56)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_sha1_ctx :: #force_inline proc() {
|
||||
ctx: Sha1_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._20
|
||||
}
|
||||
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[:])
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_sha1_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
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[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
|
||||
}
|
||||
}
|
||||
+222
-414
@@ -6,7 +6,6 @@ package sha2
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the SHA2 hashing algorithm, as defined in <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>
|
||||
@@ -17,72 +16,6 @@ import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHA2)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_sha256_ctx :: #force_inline proc(is224: bool) {
|
||||
ctx: Sha256_Context
|
||||
ctx.is224 = is224
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = is224 ? ._28 : ._32
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_sha512_ctx :: #force_inline proc(is384: bool) {
|
||||
ctx: Sha512_Context
|
||||
ctx.is384 = is384
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = is384 ? ._48 : ._64
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
@@ -97,22 +30,46 @@ hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_sha256_ctx(true)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
hash: [28]byte
|
||||
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 from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_sha256_ctx(true)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
hash: [28]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
_create_sha256_ctx(true)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -131,22 +88,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_sha256_ctx(false)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash: [32]byte
|
||||
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 from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_sha256_ctx(false)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash: [32]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_sha256_ctx(false)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -165,22 +146,46 @@ hash_string_384 :: proc(data: string) -> [48]byte {
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
|
||||
_create_sha512_ctx(true)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
hash: [48]byte
|
||||
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 from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_sha512_ctx(true)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
hash: [48]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
_create_sha512_ctx(true)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -199,22 +204,46 @@ hash_string_512 :: proc(data: string) -> [64]byte {
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
|
||||
_create_sha512_ctx(false)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash: [64]byte
|
||||
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 from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_sha512_ctx(false)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash: [64]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_sha512_ctx(false)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -228,225 +257,121 @@ hash_512 :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
init :: 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
|
||||
}
|
||||
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
|
||||
} 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
|
||||
}
|
||||
}
|
||||
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[:])
|
||||
update :: 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
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
tmp_len = CURR_BLOCK_SIZE - ctx.length
|
||||
rem_len = length < tmp_len ? length : tmp_len
|
||||
copy(ctx.block[ctx.length:], data[:rem_len])
|
||||
|
||||
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.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_48(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [48]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
if ctx.hash_size == ._28 || ctx.hash_size == ._32 {
|
||||
_create_sha256_ctx(ctx.hash_size == ._28)
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
if ctx.length + length < CURR_BLOCK_SIZE {
|
||||
ctx.length += length
|
||||
return
|
||||
}
|
||||
if ctx.hash_size == ._48 || ctx.hash_size == ._64 {
|
||||
_create_sha512_ctx(ctx.hash_size == ._48)
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._28, ._32:
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
case ._48, ._64:
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
final :: proc(ctx: ^$T, hash: []byte) {
|
||||
block_nb, pm_len, len_b: u32
|
||||
i: i32
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._28, ._32:
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
case ._48, ._64:
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
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])}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -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
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^$T) {
|
||||
when T == Sha256_Context {
|
||||
if ctx.is224 {
|
||||
ctx.h[0] = 0xc1059ed8
|
||||
ctx.h[1] = 0x367cd507
|
||||
ctx.h[2] = 0x3070dd17
|
||||
ctx.h[3] = 0xf70e5939
|
||||
ctx.h[4] = 0xffc00b31
|
||||
ctx.h[5] = 0x68581511
|
||||
ctx.h[6] = 0x64f98fa7
|
||||
ctx.h[7] = 0xbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667
|
||||
ctx.h[1] = 0xbb67ae85
|
||||
ctx.h[2] = 0x3c6ef372
|
||||
ctx.h[3] = 0xa54ff53a
|
||||
ctx.h[4] = 0x510e527f
|
||||
ctx.h[5] = 0x9b05688c
|
||||
ctx.h[6] = 0x1f83d9ab
|
||||
ctx.h[7] = 0x5be0cd19
|
||||
}
|
||||
} else when T == Sha512_Context {
|
||||
if ctx.is384 {
|
||||
ctx.h[0] = 0xcbbb9d5dc1059ed8
|
||||
ctx.h[1] = 0x629a292a367cd507
|
||||
ctx.h[2] = 0x9159015a3070dd17
|
||||
ctx.h[3] = 0x152fecd8f70e5939
|
||||
ctx.h[4] = 0x67332667ffc00b31
|
||||
ctx.h[5] = 0x8eb44a8768581511
|
||||
ctx.h[6] = 0xdb0c2e0d64f98fa7
|
||||
ctx.h[7] = 0x47b5481dbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667f3bcc908
|
||||
ctx.h[1] = 0xbb67ae8584caa73b
|
||||
ctx.h[2] = 0x3c6ef372fe94f82b
|
||||
ctx.h[3] = 0xa54ff53a5f1d36f1
|
||||
ctx.h[4] = 0x510e527fade682d1
|
||||
ctx.h[5] = 0x9b05688c2b3e6c1f
|
||||
ctx.h[6] = 0x1f83d9abfb41bd6b
|
||||
ctx.h[7] = 0x5be0cd19137e2179
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sha2_transf :: proc(ctx: ^$T, data: []byte, block_nb: uint) {
|
||||
when T == Sha256_Context {
|
||||
w: [64]u32
|
||||
@@ -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
@@ -6,7 +6,6 @@ package sha3
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the SHA3 hashing algorithm. The SHAKE functionality can be found in package shake.
|
||||
If you wish to compute a Keccak hash, you can use the keccak package, it will use the original padding.
|
||||
@@ -15,58 +14,8 @@ package sha3
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_sha3"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_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
|
||||
*/
|
||||
@@ -80,22 +29,46 @@ hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_sha3_ctx(28)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
hash: [28]byte
|
||||
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 from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_sha3_ctx(28)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
hash: [28]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
_create_sha3_ctx(28)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -114,22 +87,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_sha3_ctx(32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash: [32]byte
|
||||
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 from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_sha3_ctx(32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash: [32]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_sha3_ctx(32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -148,22 +145,46 @@ hash_string_384 :: proc(data: string) -> [48]byte {
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
|
||||
_create_sha3_ctx(48)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
hash: [48]byte
|
||||
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 from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_sha3_ctx(48)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
hash: [48]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
_create_sha3_ctx(48)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -182,22 +203,46 @@ hash_string_512 :: proc(data: string) -> [64]byte {
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
|
||||
_create_sha3_ctx(64)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash: [64]byte
|
||||
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 from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_sha3_ctx(64)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash: [64]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_sha3_ctx(64)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -211,218 +256,16 @@ hash_512 :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
Sha3_Context :: _sha3.Sha3_Context
|
||||
|
||||
init :: proc(ctx: ^_sha3.Sha3_Context) {
|
||||
_sha3.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
|
||||
_sha3.update(ctx, 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_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)
|
||||
}
|
||||
final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
|
||||
_sha3.final(ctx, hash)
|
||||
}
|
||||
|
||||
+73
-186
@@ -6,7 +6,6 @@ package shake
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the SHAKE hashing algorithm.
|
||||
The SHA3 functionality can be found in package sha3.
|
||||
@@ -15,52 +14,8 @@ package shake
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_sha3"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin_16
|
||||
ctx.hash_file_16 = hash_file_odin_16
|
||||
ctx.hash_stream_16 = hash_stream_odin_16
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHAKE)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
@@ -74,22 +29,48 @@ hash_string_128 :: proc(data: string) -> [16]byte {
|
||||
// hash_bytes_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
|
||||
_create_shake_ctx(16)
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
hash: [16]byte
|
||||
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 from its contents
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_shake_ctx(16)
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
hash: [16]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
_create_shake_ctx(16)
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -108,22 +89,48 @@ hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_shake_ctx(32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash: [32]byte
|
||||
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 from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_shake_ctx(32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash: [32]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_shake_ctx(32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -137,137 +144,17 @@ hash_256 :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
Sha3_Context :: _sha3.Sha3_Context
|
||||
|
||||
init :: proc(ctx: ^_sha3.Sha3_Context) {
|
||||
_sha3.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
|
||||
_sha3.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.shake_xof_odin(&c)
|
||||
_sha3.shake_out_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.shake_xof_odin(&c)
|
||||
_sha3.shake_out_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, 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[:])
|
||||
}
|
||||
final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
|
||||
_sha3.shake_xof(ctx)
|
||||
_sha3.shake_out(ctx, hash[:])
|
||||
}
|
||||
|
||||
@@ -1,487 +0,0 @@
|
||||
package skein
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the SKEIN hashing algorithm, as defined in <https://www.schneier.com/academic/skein/>
|
||||
|
||||
This package offers the internal state sizes of 256, 512 and 1024 bits and arbitrary output size.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
ctx.is_using_odin = false
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
ctx.is_using_odin = true
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
// @note(zh): Default to SKEIN-512
|
||||
ctx.hash_bytes_slice = hash_bytes_skein512_odin
|
||||
ctx.hash_file_slice = hash_file_skein512_odin
|
||||
ctx.hash_stream_slice = hash_stream_skein512_odin
|
||||
ctx.init = _init_skein512_odin
|
||||
ctx.update = _update_skein512_odin
|
||||
ctx.final = _final_skein512_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
_hash_impl.is_using_odin = false
|
||||
// @note(zh): Botan only supports SKEIN-512.
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SKEIN_512)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
@(warning="SKEIN is not yet implemented in Odin. Botan bindings will be used")
|
||||
use_odin :: #force_inline proc() {
|
||||
// _hash_impl.is_using_odin = true
|
||||
// _assign_hash_vtable(_hash_impl)
|
||||
use_botan()
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_skein256_ctx :: #force_inline proc(size: int) {
|
||||
_hash_impl.hash_size_val = size
|
||||
if _hash_impl.is_using_odin {
|
||||
ctx: Skein256_Context
|
||||
ctx.h.bit_length = u64(size)
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_bytes_slice = hash_bytes_skein256_odin
|
||||
_hash_impl.hash_file_slice = hash_file_skein256_odin
|
||||
_hash_impl.hash_stream_slice = hash_stream_skein256_odin
|
||||
_hash_impl.init = _init_skein256_odin
|
||||
_hash_impl.update = _update_skein256_odin
|
||||
_hash_impl.final = _final_skein256_odin
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_skein512_ctx :: #force_inline proc(size: int) {
|
||||
_hash_impl.hash_size_val = size
|
||||
if _hash_impl.is_using_odin {
|
||||
ctx: Skein512_Context
|
||||
ctx.h.bit_length = u64(size)
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_bytes_slice = hash_bytes_skein512_odin
|
||||
_hash_impl.hash_file_slice = hash_file_skein512_odin
|
||||
_hash_impl.hash_stream_slice = hash_stream_skein512_odin
|
||||
_hash_impl.init = _init_skein512_odin
|
||||
_hash_impl.update = _update_skein512_odin
|
||||
_hash_impl.final = _final_skein512_odin
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_skein1024_ctx :: #force_inline proc(size: int) {
|
||||
_hash_impl.hash_size_val = size
|
||||
if _hash_impl.is_using_odin {
|
||||
ctx: Skein1024_Context
|
||||
ctx.h.bit_length = u64(size)
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_bytes_slice = hash_bytes_skein1024_odin
|
||||
_hash_impl.hash_file_slice = hash_file_skein1024_odin
|
||||
_hash_impl.hash_stream_slice = hash_stream_skein1024_odin
|
||||
_hash_impl.init = _init_skein1024_odin
|
||||
_hash_impl.update = _update_skein1024_odin
|
||||
_hash_impl.final = _final_skein1024_odin
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_skein256_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein256_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
return hash_skein256_bytes(transmute([]byte)(data), bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein256_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein256_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
_create_skein256_ctx(bit_size)
|
||||
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein256_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_skein256_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein256_ctx(bit_size)
|
||||
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein256_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_skein256_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein256_ctx(bit_size)
|
||||
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
|
||||
}
|
||||
|
||||
hash_skein256 :: proc {
|
||||
hash_skein256_stream,
|
||||
hash_skein256_file,
|
||||
hash_skein256_bytes,
|
||||
hash_skein256_string,
|
||||
}
|
||||
|
||||
// hash_skein512_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein512_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
return hash_skein512_bytes(transmute([]byte)(data), bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein512_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein512_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
_create_skein512_ctx(bit_size)
|
||||
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein512_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_skein512_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein512_ctx(bit_size)
|
||||
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein512_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_skein512_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein512_ctx(bit_size)
|
||||
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
|
||||
}
|
||||
|
||||
hash_skein512 :: proc {
|
||||
hash_skein512_stream,
|
||||
hash_skein512_file,
|
||||
hash_skein512_bytes,
|
||||
hash_skein512_string,
|
||||
}
|
||||
|
||||
// hash_skein1024_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein1024_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
return hash_skein1024_bytes(transmute([]byte)(data), bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein1024_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein1024_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
_create_skein1024_ctx(bit_size)
|
||||
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein1024_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_skein1024_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein1024_ctx(bit_size)
|
||||
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein1024_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_skein1024_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein1024_ctx(bit_size)
|
||||
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
|
||||
}
|
||||
|
||||
hash_skein1024 :: proc {
|
||||
hash_skein1024_stream,
|
||||
hash_skein1024_file,
|
||||
hash_skein1024_bytes,
|
||||
hash_skein1024_string,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
return hash
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
hash_stream_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_skein256_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_skein256_odin(ctx, buf[:], bit_size, allocator), ok
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
hash_bytes_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
return hash
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
hash_stream_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_skein512_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_skein512_odin(ctx, buf[:], bit_size, allocator), ok
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
hash_bytes_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
return hash
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
hash_stream_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_skein512_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_skein512_odin(ctx, buf[:], bit_size, allocator), ok
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_skein256_ctx(ctx.hash_size_val)
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_skein512_ctx(ctx.hash_size_val)
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_skein1024_ctx(ctx.hash_size_val)
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
SKEIN implementation
|
||||
*/
|
||||
|
||||
STATE_WORDS_256 :: 4
|
||||
STATE_WORDS_512 :: 8
|
||||
STATE_WORDS_1024 :: 16
|
||||
|
||||
STATE_BYTES_256 :: 32
|
||||
STATE_BYTES_512 :: 64
|
||||
STATE_BYTES_1024 :: 128
|
||||
|
||||
Skein_Header :: struct {
|
||||
bit_length: u64,
|
||||
bcnt: u64,
|
||||
t: [2]u64,
|
||||
}
|
||||
|
||||
Skein256_Context :: struct {
|
||||
h: Skein_Header,
|
||||
x: [STATE_WORDS_256]u64,
|
||||
b: [STATE_BYTES_256]byte,
|
||||
}
|
||||
|
||||
Skein512_Context :: struct {
|
||||
h: Skein_Header,
|
||||
x: [STATE_WORDS_512]u64,
|
||||
b: [STATE_BYTES_512]byte,
|
||||
}
|
||||
|
||||
Skein1024_Context :: struct {
|
||||
h: Skein_Header,
|
||||
x: [STATE_WORDS_1024]u64,
|
||||
b: [STATE_BYTES_1024]byte,
|
||||
}
|
||||
|
||||
|
||||
init_odin :: proc(ctx: ^$T) {
|
||||
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^$T, data: []byte) {
|
||||
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^$T, hash: []byte) {
|
||||
|
||||
}
|
||||
+78
-184
@@ -6,7 +6,6 @@ package sm3
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the SM3 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02>
|
||||
*/
|
||||
@@ -15,51 +14,6 @@ import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_32 = hash_bytes_odin
|
||||
ctx.hash_file_32 = hash_file_odin
|
||||
ctx.hash_stream_32 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SM3)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
@@ -70,22 +24,44 @@ hash_string :: proc(data: string) -> [32]byte {
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [32]byte {
|
||||
_create_sm3_ctx()
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash: [32]byte
|
||||
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 from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_sm3_ctx()
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash: [32]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_sm3_ctx()
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -99,86 +75,64 @@ hash :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
init :: 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]
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
update :: proc(ctx: ^Sm3_Context, data: []byte) {
|
||||
data := data
|
||||
ctx.length += u64(len(data))
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
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
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
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 :: proc(ctx: ^Sm3_Context, hash: []byte) {
|
||||
length := ctx.length
|
||||
|
||||
pad: [64]byte
|
||||
pad[0] = 0x80
|
||||
if length % 64 < 56 {
|
||||
update(ctx, pad[0: 56 - length % 64])
|
||||
} else {
|
||||
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) {
|
||||
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
|
||||
}
|
||||
length <<= 3
|
||||
util.PUT_U64_BE(pad[:], length)
|
||||
update(ctx, pad[0: 8])
|
||||
assert(ctx.bitlength == 0)
|
||||
|
||||
@(private)
|
||||
_create_sm3_ctx :: #force_inline proc() {
|
||||
ctx: Sm3_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._32
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_sm3_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
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])
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -200,17 +154,6 @@ IV := [8]u32 {
|
||||
0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e,
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Sm3_Context) {
|
||||
ctx.state[0] = IV[0]
|
||||
ctx.state[1] = IV[1]
|
||||
ctx.state[2] = IV[2]
|
||||
ctx.state[3] = IV[3]
|
||||
ctx.state[4] = IV[4]
|
||||
ctx.state[5] = IV[5]
|
||||
ctx.state[6] = IV[6]
|
||||
ctx.state[7] = IV[7]
|
||||
}
|
||||
|
||||
block :: proc "contextless" (ctx: ^Sm3_Context, buf: []byte) {
|
||||
buf := buf
|
||||
|
||||
@@ -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[4], ctx.state[5], ctx.state[6], ctx.state[7] = state4, state5, state6, state7
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Sm3_Context, data: []byte) {
|
||||
data := data
|
||||
ctx.length += u64(len(data))
|
||||
|
||||
if ctx.bitlength > 0 {
|
||||
n := copy(ctx.x[ctx.bitlength:], data[:])
|
||||
ctx.bitlength += u64(n)
|
||||
if ctx.bitlength == 64 {
|
||||
block(ctx, ctx.x[:])
|
||||
ctx.bitlength = 0
|
||||
}
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) >= 64 {
|
||||
n := len(data) &~ (64 - 1)
|
||||
block(ctx, data[:n])
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) > 0 {
|
||||
ctx.bitlength = u64(copy(ctx.x[:], data[:]))
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Sm3_Context, hash: []byte) {
|
||||
length := ctx.length
|
||||
|
||||
pad: [64]byte
|
||||
pad[0] = 0x80
|
||||
if length % 64 < 56 {
|
||||
update_odin(ctx, pad[0: 56 - length % 64])
|
||||
} else {
|
||||
update_odin(ctx, pad[0: 64 + 56 - length % 64])
|
||||
}
|
||||
|
||||
length <<= 3
|
||||
util.PUT_U64_BE(pad[:], length)
|
||||
update_odin(ctx, pad[0: 8])
|
||||
assert(ctx.bitlength == 0)
|
||||
|
||||
util.PUT_U32_BE(hash[0:], ctx.state[0])
|
||||
util.PUT_U32_BE(hash[4:], ctx.state[1])
|
||||
util.PUT_U32_BE(hash[8:], ctx.state[2])
|
||||
util.PUT_U32_BE(hash[12:], ctx.state[3])
|
||||
util.PUT_U32_BE(hash[16:], ctx.state[4])
|
||||
util.PUT_U32_BE(hash[20:], ctx.state[5])
|
||||
util.PUT_U32_BE(hash[24:], ctx.state[6])
|
||||
util.PUT_U32_BE(hash[28:], ctx.state[7])
|
||||
}
|
||||
|
||||
+107
-230
@@ -6,7 +6,6 @@ package streebog
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the Streebog hashing algorithm, standardized as GOST R 34.11-2012 in RFC 6986 <https://datatracker.ietf.org/doc/html/rfc6986>
|
||||
*/
|
||||
@@ -15,58 +14,6 @@ import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_STREEBOG)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_streebog_ctx :: #force_inline proc(is256: bool) {
|
||||
ctx: Streebog_Context
|
||||
ctx.is256 = is256
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = is256 ? ._32 : ._64
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
@@ -81,22 +28,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_streebog_ctx(true)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash: [32]byte
|
||||
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 from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_streebog_ctx(true)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash: [32]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_streebog_ctx(true)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -115,22 +86,44 @@ hash_string_512 :: proc(data: string) -> [64]byte {
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
|
||||
_create_streebog_ctx(false)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash: [64]byte
|
||||
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 from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_streebog_ctx(false)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash: [64]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_streebog_ctx(false)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -144,120 +137,64 @@ hash_512 :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
init :: 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 :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
final :: 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
|
||||
|
||||
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
|
||||
}
|
||||
padding(ctx)
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
G(ctx.h[:], ctx.n[:], ctx.buffer[:])
|
||||
|
||||
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
|
||||
}
|
||||
add_mod_512(ctx.n[:], t[:], ctx.n[:])
|
||||
add_mod_512(ctx.sigma[:], ctx.buffer[:], ctx.sigma[:])
|
||||
|
||||
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
|
||||
}
|
||||
G(ctx.h[:], ctx.v_0[:], ctx.n[:])
|
||||
G(ctx.h[:], ctx.v_0[:], ctx.sigma[:])
|
||||
|
||||
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)
|
||||
}
|
||||
if ctx.is256 {
|
||||
copy(hash[:], ctx.h[32:])
|
||||
} else {
|
||||
copy(hash[:], ctx.h[:])
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -534,63 +471,3 @@ padding :: proc(ctx: ^Streebog_Context) {
|
||||
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[:])
|
||||
}
|
||||
}
|
||||
+101
-219
@@ -6,7 +6,6 @@ package tiger
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the Tiger1 variant of the Tiger hashing algorithm as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
|
||||
*/
|
||||
@@ -14,55 +13,8 @@ package tiger
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_tiger"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin_16
|
||||
ctx.hash_file_16 = hash_file_odin_16
|
||||
ctx.hash_stream_16 = hash_stream_odin_16
|
||||
ctx.hash_bytes_20 = hash_bytes_odin_20
|
||||
ctx.hash_file_20 = hash_file_odin_20
|
||||
ctx.hash_stream_20 = hash_stream_odin_20
|
||||
ctx.hash_bytes_24 = hash_bytes_odin_24
|
||||
ctx.hash_file_24 = hash_file_odin_24
|
||||
ctx.hash_stream_24 = hash_stream_odin_24
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_TIGER)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
@@ -76,22 +28,46 @@ hash_string_128 :: proc(data: string) -> [16]byte {
|
||||
// hash_bytes_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
|
||||
_create_tiger_ctx(16)
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
hash: [16]byte
|
||||
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 from its contents
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_tiger_ctx(16)
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
hash: [16]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
_create_tiger_ctx(16)
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -110,22 +86,46 @@ hash_string_160 :: proc(data: string) -> [20]byte {
|
||||
// hash_bytes_160 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_160 :: proc(data: []byte) -> [20]byte {
|
||||
_create_tiger_ctx(20)
|
||||
return _hash_impl->hash_bytes_20(data)
|
||||
hash: [20]byte
|
||||
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 from its contents
|
||||
hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
|
||||
_create_tiger_ctx(20)
|
||||
return _hash_impl->hash_stream_20(s)
|
||||
hash: [20]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
_create_tiger_ctx(20)
|
||||
return _hash_impl->hash_file_20(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -144,22 +144,46 @@ hash_string_192 :: proc(data: string) -> [24]byte {
|
||||
// hash_bytes_192 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_192 :: proc(data: []byte) -> [24]byte {
|
||||
_create_tiger_ctx(24)
|
||||
return _hash_impl->hash_bytes_24(data)
|
||||
hash: [24]byte
|
||||
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 from its contents
|
||||
hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
|
||||
_create_tiger_ctx(24)
|
||||
return _hash_impl->hash_stream_24(s)
|
||||
hash: [24]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
|
||||
_create_tiger_ctx(24)
|
||||
return _hash_impl->hash_file_24(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -169,163 +193,21 @@ hash_192 :: proc {
|
||||
hash_string_192,
|
||||
}
|
||||
|
||||
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
_tiger.update_odin(&c, data)
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
Tiger_Context :: _tiger.Tiger_Context
|
||||
|
||||
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.(_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
|
||||
init :: proc(ctx: ^_tiger.Tiger_Context) {
|
||||
ctx.ver = 1
|
||||
_hash_impl.internal_ctx = ctx
|
||||
switch hash_size {
|
||||
case 16: _hash_impl.hash_size = ._16
|
||||
case 20: _hash_impl.hash_size = ._20
|
||||
case 24: _hash_impl.hash_size = ._24
|
||||
}
|
||||
_tiger.init(ctx)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#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)
|
||||
}
|
||||
update :: proc(ctx: ^_tiger.Tiger_Context, data: []byte) {
|
||||
_tiger.update(ctx, data)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
final :: proc(ctx: ^_tiger.Tiger_Context, hash: []byte) {
|
||||
_tiger.final(ctx, hash)
|
||||
}
|
||||
+100
-218
@@ -6,7 +6,6 @@ package tiger2
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the Tiger2 variant of the Tiger hashing algorithm as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
|
||||
*/
|
||||
@@ -14,55 +13,8 @@ package tiger2
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
import "../_tiger"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin_16
|
||||
ctx.hash_file_16 = hash_file_odin_16
|
||||
ctx.hash_stream_16 = hash_stream_odin_16
|
||||
ctx.hash_bytes_20 = hash_bytes_odin_20
|
||||
ctx.hash_file_20 = hash_file_odin_20
|
||||
ctx.hash_stream_20 = hash_stream_odin_20
|
||||
ctx.hash_bytes_24 = hash_bytes_odin_24
|
||||
ctx.hash_file_24 = hash_file_odin_24
|
||||
ctx.hash_stream_24 = hash_stream_odin_24
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since Tiger2 is not available in Botan
|
||||
@(warning="Tiger2 is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
@@ -76,22 +28,46 @@ hash_string_128 :: proc(data: string) -> [16]byte {
|
||||
// hash_bytes_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
|
||||
_create_tiger2_ctx(16)
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
hash: [16]byte
|
||||
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 from its contents
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_tiger2_ctx(16)
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
hash: [16]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
_create_tiger2_ctx(16)
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -110,22 +86,46 @@ hash_string_160 :: proc(data: string) -> [20]byte {
|
||||
// hash_bytes_160 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_160 :: proc(data: []byte) -> [20]byte {
|
||||
_create_tiger2_ctx(20)
|
||||
return _hash_impl->hash_bytes_20(data)
|
||||
hash: [20]byte
|
||||
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 from its contents
|
||||
hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
|
||||
_create_tiger2_ctx(20)
|
||||
return _hash_impl->hash_stream_20(s)
|
||||
hash: [20]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
_create_tiger2_ctx(20)
|
||||
return _hash_impl->hash_file_20(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -144,22 +144,46 @@ hash_string_192 :: proc(data: string) -> [24]byte {
|
||||
// hash_bytes_192 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_192 :: proc(data: []byte) -> [24]byte {
|
||||
_create_tiger2_ctx(24)
|
||||
return _hash_impl->hash_bytes_24(data)
|
||||
hash: [24]byte
|
||||
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 from its contents
|
||||
hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
|
||||
_create_tiger2_ctx(24)
|
||||
return _hash_impl->hash_stream_24(s)
|
||||
hash: [24]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
|
||||
_create_tiger2_ctx(24)
|
||||
return _hash_impl->hash_file_24(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -169,163 +193,21 @@ hash_192 :: proc {
|
||||
hash_string_192,
|
||||
}
|
||||
|
||||
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
_tiger.update_odin(&c, data)
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
Tiger_Context :: _tiger.Tiger_Context
|
||||
|
||||
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.(_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
|
||||
init :: proc(ctx: ^_tiger.Tiger_Context) {
|
||||
ctx.ver = 2
|
||||
_hash_impl.internal_ctx = ctx
|
||||
switch hash_size {
|
||||
case 16: _hash_impl.hash_size = ._16
|
||||
case 20: _hash_impl.hash_size = ._20
|
||||
case 24: _hash_impl.hash_size = ._24
|
||||
}
|
||||
_tiger.init(ctx)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#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)
|
||||
}
|
||||
update :: proc(ctx: ^_tiger.Tiger_Context, data: []byte) {
|
||||
_tiger.update(ctx, data)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.final_odin(&c, hash)
|
||||
}
|
||||
final :: proc(ctx: ^_tiger.Tiger_Context, hash: []byte) {
|
||||
_tiger.final(ctx, hash)
|
||||
}
|
||||
@@ -6,7 +6,6 @@ package whirlpool
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the Whirlpool hashing algorithm, as defined in <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:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../util"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_64 = hash_bytes_odin
|
||||
ctx.hash_file_64 = hash_file_odin
|
||||
ctx.hash_stream_64 = hash_stream_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_WHIRLPOOL)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
@@ -69,22 +28,44 @@ hash_string :: proc(data: string) -> [64]byte {
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [64]byte {
|
||||
_create_whirlpool_ctx()
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash: [64]byte
|
||||
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 from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_whirlpool_ctx()
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash: [64]byte
|
||||
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
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_whirlpool_ctx()
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
if !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 {
|
||||
@@ -98,76 +79,103 @@ hash :: proc {
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
@(warning="Init is a no-op for Whirlpool")
|
||||
init :: proc(ctx: ^Whirlpool_Context) {
|
||||
// No action needed here
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: 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 :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
final :: proc(ctx: ^Whirlpool_Context, hash: []byte) {
|
||||
n := ctx
|
||||
n.buffer[n.buffer_pos] |= 0x80 >> (uint(n.buffer_bits) & 7)
|
||||
n.buffer_pos += 1
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
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
|
||||
}
|
||||
for i := 0; i < 32; i += 1 {
|
||||
n.buffer[n.buffer_pos + i] = n.bitlength[i]
|
||||
}
|
||||
transform(ctx)
|
||||
|
||||
@(private)
|
||||
_create_whirlpool_ctx :: #force_inline proc() {
|
||||
ctx: Whirlpool_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._64
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
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]}
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package x25519
|
||||
|
||||
import field "core:crypto/_fiat/field_curve25519"
|
||||
import "core:mem"
|
||||
|
||||
SCALAR_SIZE :: 32
|
||||
POINT_SIZE :: 32
|
||||
|
||||
_BASE_POINT: [32]byte = {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
_scalar_bit :: #force_inline proc "contextless" (s: ^[32]byte, i: int) -> u8 {
|
||||
if i < 0 {
|
||||
return 0
|
||||
}
|
||||
return (s[i>>3] >> uint(i&7)) & 1
|
||||
}
|
||||
|
||||
_scalarmult :: proc (out, scalar, point: ^[32]byte) {
|
||||
// Montgomery pseduo-multiplication taken from Monocypher.
|
||||
|
||||
// computes the scalar product
|
||||
x1: field.Tight_Field_Element = ---
|
||||
field.fe_from_bytes(&x1, point)
|
||||
|
||||
// computes the actual scalar product (the result is in x2 and z2)
|
||||
x2, x3, z2, z3: field.Tight_Field_Element = ---, ---, ---, ---
|
||||
t0, t1: field.Loose_Field_Element = ---, ---
|
||||
|
||||
// Montgomery ladder
|
||||
// In projective coordinates, to avoid divisions: x = X / Z
|
||||
// We don't care about the y coordinate, it's only 1 bit of information
|
||||
field.fe_one(&x2) // "zero" point
|
||||
field.fe_zero(&z2)
|
||||
field.fe_set(&x3, &x1) // "one" point
|
||||
field.fe_one(&z3)
|
||||
|
||||
swap: int
|
||||
for pos := 255-1; pos >= 0; pos = pos - 1 {
|
||||
// constant time conditional swap before ladder step
|
||||
b := int(_scalar_bit(scalar, pos))
|
||||
swap ~= b // xor trick avoids swapping at the end of the loop
|
||||
field.fe_cond_swap(&x2, &x3, swap)
|
||||
field.fe_cond_swap(&z2, &z3, swap)
|
||||
swap = b // anticipates one last swap after the loop
|
||||
|
||||
// Montgomery ladder step: replaces (P2, P3) by (P2*2, P2+P3)
|
||||
// with differential addition
|
||||
//
|
||||
// Note: This deliberately omits reductions after add/sub operations
|
||||
// if the result is only ever used as the input to a mul/square since
|
||||
// the implementations of those can deal with non-reduced inputs.
|
||||
//
|
||||
// fe_tighten_cast is only used to store a fully reduced
|
||||
// output in a Loose_Field_Element, or to provide such a
|
||||
// Loose_Field_Element as a Tight_Field_Element argument.
|
||||
field.fe_sub(&t0, &x3, &z3)
|
||||
field.fe_sub(&t1, &x2, &z2)
|
||||
field.fe_add(field.fe_relax_cast(&x2), &x2, &z2) // x2 - unreduced
|
||||
field.fe_add(field.fe_relax_cast(&z2), &x3, &z3) // z2 - unreduced
|
||||
field.fe_carry_mul(&z3, &t0, field.fe_relax_cast(&x2))
|
||||
field.fe_carry_mul(&z2, field.fe_relax_cast(&z2), &t1) // z2 - reduced
|
||||
field.fe_carry_square(field.fe_tighten_cast(&t0), &t1) // t0 - reduced
|
||||
field.fe_carry_square(field.fe_tighten_cast(&t1), field.fe_relax_cast(&x2)) // t1 - reduced
|
||||
field.fe_add(field.fe_relax_cast(&x3), &z3, &z2) // x3 - unreduced
|
||||
field.fe_sub(field.fe_relax_cast(&z2), &z3, &z2) // z2 - unreduced
|
||||
field.fe_carry_mul(&x2, &t1, &t0) // x2 - reduced
|
||||
field.fe_sub(&t1, field.fe_tighten_cast(&t1), field.fe_tighten_cast(&t0)) // safe - t1/t0 is reduced
|
||||
field.fe_carry_square(&z2, field.fe_relax_cast(&z2)) // z2 - reduced
|
||||
field.fe_carry_scmul_121666(&z3, &t1)
|
||||
field.fe_carry_square(&x3, field.fe_relax_cast(&x3)) // x3 - reduced
|
||||
field.fe_add(&t0, field.fe_tighten_cast(&t0), &z3) // safe - t0 is reduced
|
||||
field.fe_carry_mul(&z3, field.fe_relax_cast(&x1), field.fe_relax_cast(&z2))
|
||||
field.fe_carry_mul(&z2, &t1, &t0)
|
||||
}
|
||||
// last swap is necessary to compensate for the xor trick
|
||||
// Note: after this swap, P3 == P2 + P1.
|
||||
field.fe_cond_swap(&x2, &x3, swap)
|
||||
field.fe_cond_swap(&z2, &z3, swap)
|
||||
|
||||
// normalises the coordinates: x == X / Z
|
||||
field.fe_carry_inv(&z2, field.fe_relax_cast(&z2))
|
||||
field.fe_carry_mul(&x2, field.fe_relax_cast(&x2), field.fe_relax_cast(&z2))
|
||||
field.fe_to_bytes(out, &x2)
|
||||
|
||||
mem.zero_explicit(&x1, size_of(x1))
|
||||
mem.zero_explicit(&x2, size_of(x2))
|
||||
mem.zero_explicit(&x3, size_of(x3))
|
||||
mem.zero_explicit(&z2, size_of(z2))
|
||||
mem.zero_explicit(&z3, size_of(z3))
|
||||
mem.zero_explicit(&t0, size_of(t0))
|
||||
mem.zero_explicit(&t1, size_of(t1))
|
||||
}
|
||||
|
||||
scalarmult :: proc (dst, scalar, point: []byte) {
|
||||
if len(scalar) != SCALAR_SIZE {
|
||||
panic("crypto/x25519: invalid scalar size")
|
||||
}
|
||||
if len(point) != POINT_SIZE {
|
||||
panic("crypto/x25519: invalid point size")
|
||||
}
|
||||
if len(dst) != POINT_SIZE {
|
||||
panic("crypto/x25519: invalid destination point size")
|
||||
}
|
||||
|
||||
// "clamp" the scalar
|
||||
e: [32]byte = ---
|
||||
copy_slice(e[:], scalar)
|
||||
e[0] &= 248
|
||||
e[31] &= 127
|
||||
e[31] |= 64
|
||||
|
||||
p: [32]byte = ---
|
||||
copy_slice(p[:], point)
|
||||
|
||||
d: [32]byte = ---
|
||||
_scalarmult(&d, &e, &p)
|
||||
copy_slice(dst, d[:])
|
||||
|
||||
mem.zero_explicit(&e, size_of(e))
|
||||
mem.zero_explicit(&d, size_of(d))
|
||||
}
|
||||
|
||||
scalarmult_basepoint :: proc (dst, scalar: []byte) {
|
||||
// TODO/perf: Switch to using a precomputed table.
|
||||
scalarmult(dst, scalar, _BASE_POINT[:])
|
||||
}
|
||||
@@ -18,7 +18,7 @@ Marshal_Error :: union {
|
||||
|
||||
marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
|
||||
b := strings.make_builder(allocator)
|
||||
defer if err != nil || data == nil {
|
||||
defer if err != .None {
|
||||
strings.destroy_builder(&b)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: M
|
||||
if len(b.buf) != 0 {
|
||||
data = b.buf[:]
|
||||
}
|
||||
return
|
||||
return data, .None
|
||||
}
|
||||
|
||||
marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
|
||||
@@ -106,6 +106,7 @@ parse_comma :: proc(p: ^Parser) -> (do_break: bool) {
|
||||
}
|
||||
|
||||
parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
err = .None
|
||||
token := p.curr_token
|
||||
#partial switch token.kind {
|
||||
case .Null:
|
||||
@@ -175,6 +176,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
}
|
||||
|
||||
parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
err = .None
|
||||
expect_token(p, .Open_Bracket) or_return
|
||||
|
||||
array: Array
|
||||
@@ -266,15 +268,14 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> (obj: Object, er
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
return obj, .None
|
||||
}
|
||||
|
||||
parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
expect_token(p, .Open_Brace) or_return
|
||||
obj := parse_object_body(p, .Close_Brace) or_return
|
||||
expect_token(p, .Close_Brace) or_return
|
||||
value = obj
|
||||
return
|
||||
return obj, .None
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -222,6 +222,7 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
|
||||
advance_token(p)
|
||||
return
|
||||
case .False, .True:
|
||||
advance_token(p)
|
||||
if assign_bool(v, token.kind == .True) {
|
||||
return
|
||||
}
|
||||
|
||||
+7
-4
@@ -1927,8 +1927,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
fi.indent += 1
|
||||
|
||||
if fi.hash {
|
||||
// Printed as it is written
|
||||
io.write_byte(fi.writer, '\n')
|
||||
// TODO(bill): Should this render it like in written form? e.g. tranposed
|
||||
for row in 0..<info.row_count {
|
||||
fmt_write_indent(fi)
|
||||
for col in 0..<info.column_count {
|
||||
@@ -1939,13 +1939,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
data := uintptr(v.data) + uintptr(offset)
|
||||
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
|
||||
}
|
||||
io.write_string(fi.writer, ";\n")
|
||||
io.write_string(fi.writer, ",\n")
|
||||
}
|
||||
} else {
|
||||
// Printed in Row-Major layout to match text layout
|
||||
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, "; ") }
|
||||
if col > 0 { io.write_string(fi.writer, ", ") }
|
||||
|
||||
offset := (row + col*info.elem_stride)*info.elem_size
|
||||
|
||||
@@ -2075,9 +2076,11 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
|
||||
case f32be: fmt_float(fi, f64(a), 32, verb)
|
||||
case f64be: fmt_float(fi, f64(a), 64, verb)
|
||||
|
||||
case complex32: fmt_complex(fi, complex128(a), 32, verb)
|
||||
case complex64: fmt_complex(fi, complex128(a), 64, verb)
|
||||
case complex128: fmt_complex(fi, a, 128, verb)
|
||||
|
||||
case quaternion64: fmt_quaternion(fi, quaternion256(a), 64, verb)
|
||||
case quaternion128: fmt_quaternion(fi, quaternion256(a), 128, verb)
|
||||
case quaternion256: fmt_quaternion(fi, a, 256, verb)
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
//+build js
|
||||
package fmt
|
||||
|
||||
import "core:io"
|
||||
|
||||
foreign import "odin_env"
|
||||
|
||||
@(private="file")
|
||||
foreign odin_env {
|
||||
write :: proc "c" (fd: u32, p: []byte) ---
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
write_vtable := &io.Stream_VTable{
|
||||
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
fd := u32(uintptr(s.stream_data))
|
||||
write(fd, p)
|
||||
return len(p), nil
|
||||
},
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
stdout := io.Writer{
|
||||
stream = {
|
||||
stream_vtable = write_vtable,
|
||||
stream_data = rawptr(uintptr(1)),
|
||||
},
|
||||
}
|
||||
@(private="file")
|
||||
stderr := io.Writer{
|
||||
stream = {
|
||||
stream_vtable = write_vtable,
|
||||
stream_data = rawptr(uintptr(2)),
|
||||
},
|
||||
}
|
||||
|
||||
// print* procedures return the number of bytes written
|
||||
print :: proc(args: ..any, sep := " ") -> int { return wprint(w=stdout, args=args, sep=sep) }
|
||||
println :: proc(args: ..any, sep := " ") -> int { return wprintln(w=stdout, args=args, sep=sep) }
|
||||
printf :: proc(fmt: string, args: ..any) -> int { return wprintf(stdout, fmt, ..args) }
|
||||
|
||||
eprint :: proc(args: ..any, sep := " ") -> int { return wprint(w=stderr, args=args, sep=sep) }
|
||||
eprintln :: proc(args: ..any, sep := " ") -> int { return wprintln(w=stderr, args=args, sep=sep) }
|
||||
eprintf :: proc(fmt: string, args: ..any) -> int { return wprintf(stderr, fmt, ..args) }
|
||||
@@ -1,4 +1,4 @@
|
||||
//+build !freestanding
|
||||
//+build !freestanding !js
|
||||
package fmt
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
+20
-22
@@ -47,8 +47,8 @@ adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
djb2 :: proc(data: []byte) -> u32 {
|
||||
hash: u32 = 5381
|
||||
djb2 :: proc(data: []byte, seed := u32(5381)) -> u32 {
|
||||
hash: u32 = seed
|
||||
for b in data {
|
||||
hash = (hash << 5) + hash + u32(b) // hash * 33 + u32(b)
|
||||
}
|
||||
@@ -56,8 +56,8 @@ djb2 :: proc(data: []byte) -> u32 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv32 :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5
|
||||
fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
for b in data {
|
||||
h = (h * 0x01000193) ~ u32(b)
|
||||
}
|
||||
@@ -65,8 +65,8 @@ fnv32 :: proc(data: []byte) -> u32 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv64 :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325
|
||||
fnv64 :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
h: u64 = seed
|
||||
for b in data {
|
||||
h = (h * 0x100000001b3) ~ u64(b)
|
||||
}
|
||||
@@ -74,8 +74,8 @@ fnv64 :: proc(data: []byte) -> u64 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv32a :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5
|
||||
fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
for b in data {
|
||||
h = (h ~ u32(b)) * 0x01000193
|
||||
}
|
||||
@@ -83,8 +83,8 @@ fnv32a :: proc(data: []byte) -> u32 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv64a :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325
|
||||
fnv64a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
h: u64 = seed
|
||||
for b in data {
|
||||
h = (h ~ u64(b)) * 0x100000001b3
|
||||
}
|
||||
@@ -92,8 +92,8 @@ fnv64a :: proc(data: []byte) -> u64 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
jenkins :: proc(data: []byte) -> u32 {
|
||||
hash: u32 = 0
|
||||
jenkins :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
hash: u32 = seed
|
||||
for b in data {
|
||||
hash += u32(b)
|
||||
hash += hash << 10
|
||||
@@ -106,11 +106,11 @@ jenkins :: proc(data: []byte) -> u32 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
murmur32 :: proc(data: []byte) -> u32 {
|
||||
murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
c1_32: u32 : 0xcc9e2d51
|
||||
c2_32: u32 : 0x1b873593
|
||||
|
||||
h1: u32 = 0
|
||||
h1: u32 = seed
|
||||
nblocks := len(data)/4
|
||||
p := raw_data(data)
|
||||
p1 := mem.ptr_offset(p, 4*nblocks)
|
||||
@@ -156,14 +156,12 @@ murmur32 :: proc(data: []byte) -> u32 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
murmur64 :: proc(data: []byte) -> u64 {
|
||||
SEED :: 0x9747b28c
|
||||
|
||||
murmur64 :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
when size_of(int) == 8 {
|
||||
m :: 0xc6a4a7935bd1e995
|
||||
r :: 47
|
||||
|
||||
h: u64 = SEED ~ (u64(len(data)) * m)
|
||||
h: u64 = seed ~ (u64(len(data)) * m)
|
||||
data64 := mem.slice_ptr(cast(^u64)raw_data(data), len(data)/size_of(u64))
|
||||
|
||||
for _, i in data64 {
|
||||
@@ -198,8 +196,8 @@ murmur64 :: proc(data: []byte) -> u64 {
|
||||
m :: 0x5bd1e995
|
||||
r :: 24
|
||||
|
||||
h1 := u32(SEED) ~ u32(len(data))
|
||||
h2 := u32(SEED) >> 32
|
||||
h1 := u32(seed) ~ u32(len(data))
|
||||
h2 := u32(seed) >> 32
|
||||
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
|
||||
len := len(data)
|
||||
i := 0
|
||||
@@ -262,8 +260,8 @@ murmur64 :: proc(data: []byte) -> u64 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
sdbm :: proc(data: []byte) -> u32 {
|
||||
hash: u32 = 0
|
||||
sdbm :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
hash: u32 = seed
|
||||
for b in data {
|
||||
hash = u32(b) + (hash<<6) + (hash<<16) - hash
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ internal_rat_to_float :: proc($T: typeid, z: ^Rat, allocator := context.allocato
|
||||
|
||||
mantissa >>= 1
|
||||
|
||||
f = T(math.ldexp(f64(mantissa), i32(exp-MSIZE1)))
|
||||
f = T(math.ldexp(f64(mantissa), exp-MSIZE1))
|
||||
if math.is_inf(f, 0) {
|
||||
exact = false
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
package math_linalg_glsl
|
||||
|
||||
import "core:math"
|
||||
|
||||
cos_f32 :: proc "c" (x: f32) -> f32 { return math.cos(x) }
|
||||
sin_f32 :: proc "c" (x: f32) -> f32 { return math.sin(x) }
|
||||
tan_f32 :: proc "c" (x: f32) -> f32 { return math.tan(x) }
|
||||
acos_f32 :: proc "c" (x: f32) -> f32 { return math.acos(x) }
|
||||
asin_f32 :: proc "c" (x: f32) -> f32 { return math.asin(x) }
|
||||
atan_f32 :: proc "c" (x: f32) -> f32 { return math.atan(x) }
|
||||
atan2_f32 :: proc "c" (y, x: f32) -> f32 { return math.atan2(y, x) }
|
||||
cosh_f32 :: proc "c" (x: f32) -> f32 { return math.cosh(x) }
|
||||
sinh_f32 :: proc "c" (x: f32) -> f32 { return math.sinh(x) }
|
||||
tanh_f32 :: proc "c" (x: f32) -> f32 { return math.tanh(x) }
|
||||
acosh_f32 :: proc "c" (x: f32) -> f32 { return math.acosh(x) }
|
||||
asinh_f32 :: proc "c" (x: f32) -> f32 { return math.asinh(x) }
|
||||
atanh_f32 :: proc "c" (x: f32) -> f32 { return math.atanh(x) }
|
||||
sqrt_f32 :: proc "c" (x: f32) -> f32 { return math.sqrt(x) }
|
||||
inversesqrt_f32 :: proc "c" (x: f32) -> f32 { return 1.0/math.sqrt(x) }
|
||||
pow_f32 :: proc "c" (x, y: f32) -> f32 { return math.pow(x, y) }
|
||||
exp_f32 :: proc "c" (x: f32) -> f32 { return math.exp(x) }
|
||||
log_f32 :: proc "c" (x: f32) -> f32 { return math.ln(x) }
|
||||
exp2_f32 :: proc "c" (x: f32) -> f32 { return math.pow(f32(2), x) }
|
||||
sign_f32 :: proc "c" (x: f32) -> f32 { return math.sign(x) }
|
||||
floor_f32 :: proc "c" (x: f32) -> f32 { return math.floor(x) }
|
||||
ceil_f32 :: proc "c" (x: f32) -> f32 { return math.ceil(x) }
|
||||
mod_f32 :: proc "c" (x, y: f32) -> f32 { return math.mod(x, y) }
|
||||
fract_f32 :: proc "c" (x: f32) -> f32 {
|
||||
if x >= 0 {
|
||||
return x - math.trunc(x)
|
||||
}
|
||||
return math.trunc(-x) + x
|
||||
}
|
||||
|
||||
cos_f64 :: proc "c" (x: f64) -> f64 { return math.cos(x) }
|
||||
sin_f64 :: proc "c" (x: f64) -> f64 { return math.sin(x) }
|
||||
tan_f64 :: proc "c" (x: f64) -> f64 { return math.tan(x) }
|
||||
acos_f64 :: proc "c" (x: f64) -> f64 { return math.acos(x) }
|
||||
asin_f64 :: proc "c" (x: f64) -> f64 { return math.asin(x) }
|
||||
atan_f64 :: proc "c" (x: f64) -> f64 { return math.atan(x) }
|
||||
atan2_f64 :: proc "c" (y, x: f64) -> f64 { return math.atan2(y, x) }
|
||||
cosh_f64 :: proc "c" (x: f64) -> f64 { return math.cosh(x) }
|
||||
sinh_f64 :: proc "c" (x: f64) -> f64 { return math.sinh(x) }
|
||||
tanh_f64 :: proc "c" (x: f64) -> f64 { return math.tanh(x) }
|
||||
acosh_f64 :: proc "c" (x: f64) -> f64 { return math.acosh(x) }
|
||||
asinh_f64 :: proc "c" (x: f64) -> f64 { return math.asinh(x) }
|
||||
atanh_f64 :: proc "c" (x: f64) -> f64 { return math.atanh(x) }
|
||||
sqrt_f64 :: proc "c" (x: f64) -> f64 { return math.sqrt(x) }
|
||||
inversesqrt_f64 :: proc "c" (x: f64) -> f64 { return 1.0/math.sqrt(x) }
|
||||
pow_f64 :: proc "c" (x, y: f64) -> f64 { return math.pow(x, y) }
|
||||
exp_f64 :: proc "c" (x: f64) -> f64 { return math.exp(x) }
|
||||
log_f64 :: proc "c" (x: f64) -> f64 { return math.ln(x) }
|
||||
exp2_f64 :: proc "c" (x: f64) -> f64 { return math.pow(f64(2), x) }
|
||||
sign_f64 :: proc "c" (x: f64) -> f64 { return math.sign(x) }
|
||||
floor_f64 :: proc "c" (x: f64) -> f64 { return math.floor(x) }
|
||||
ceil_f64 :: proc "c" (x: f64) -> f64 { return math.ceil(x) }
|
||||
mod_f64 :: proc "c" (x, y: f64) -> f64 { return math.mod(x, y) }
|
||||
fract_f64 :: proc "c" (x: f64) -> f64 {
|
||||
if x >= 0 {
|
||||
return x - math.trunc(x)
|
||||
}
|
||||
return math.trunc(-x) + x
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,69 @@
|
||||
package math_linalg_hlsl
|
||||
|
||||
import "core:math"
|
||||
|
||||
cos_float :: proc "c" (x: float) -> float { return math.cos(x) }
|
||||
sin_float :: proc "c" (x: float) -> float { return math.sin(x) }
|
||||
tan_float :: proc "c" (x: float) -> float { return math.tan(x) }
|
||||
acos_float :: proc "c" (x: float) -> float { return math.acos(x) }
|
||||
asin_float :: proc "c" (x: float) -> float { return math.asin(x) }
|
||||
atan_float :: proc "c" (x: float) -> float { return math.atan(x) }
|
||||
atan2_float :: proc "c" (y, x: float) -> float { return math.atan2(y, x) }
|
||||
cosh_float :: proc "c" (x: float) -> float { return math.cosh(x) }
|
||||
sinh_float :: proc "c" (x: float) -> float { return math.sinh(x) }
|
||||
tanh_float :: proc "c" (x: float) -> float { return math.tanh(x) }
|
||||
acosh_float :: proc "c" (x: float) -> float { return math.acosh(x) }
|
||||
asinh_float :: proc "c" (x: float) -> float { return math.asinh(x) }
|
||||
atanh_float :: proc "c" (x: float) -> float { return math.atanh(x) }
|
||||
sqrt_float :: proc "c" (x: float) -> float { return math.sqrt(x) }
|
||||
rsqrt_float :: proc "c" (x: float) -> float { return 1.0/math.sqrt(x) }
|
||||
rcp_float :: proc "c" (x: float) -> float { return 1.0/x }
|
||||
pow_float :: proc "c" (x, y: float) -> float { return math.pow(x, y) }
|
||||
exp_float :: proc "c" (x: float) -> float { return math.exp(x) }
|
||||
log_float :: proc "c" (x: float) -> float { return math.ln(x) }
|
||||
log2_float :: proc "c" (x: float) -> float { return math.log(x, 2) }
|
||||
log10_float :: proc "c" (x: float) -> float { return math.log(x, 10) }
|
||||
exp2_float :: proc "c" (x: float) -> float { return math.pow(float(2), x) }
|
||||
sign_float :: proc "c" (x: float) -> float { return math.sign(x) }
|
||||
floor_float :: proc "c" (x: float) -> float { return math.floor(x) }
|
||||
ceil_float :: proc "c" (x: float) -> float { return math.ceil(x) }
|
||||
fmod_float :: proc "c" (x, y: float) -> float { return math.mod(x, y) }
|
||||
frac_float :: proc "c" (x: float) -> float {
|
||||
if x >= 0 {
|
||||
return x - math.trunc(x)
|
||||
}
|
||||
return math.trunc(-x) + x
|
||||
}
|
||||
|
||||
cos_double :: proc "c" (x: double) -> double { return math.cos(x) }
|
||||
sin_double :: proc "c" (x: double) -> double { return math.sin(x) }
|
||||
tan_double :: proc "c" (x: double) -> double { return math.tan(x) }
|
||||
acos_double :: proc "c" (x: double) -> double { return math.acos(x) }
|
||||
asin_double :: proc "c" (x: double) -> double { return math.asin(x) }
|
||||
atan_double :: proc "c" (x: double) -> double { return math.atan(x) }
|
||||
atan2_double :: proc "c" (y, x: double) -> double { return math.atan2(y, x) }
|
||||
cosh_double :: proc "c" (x: double) -> double { return math.cosh(x) }
|
||||
sinh_double :: proc "c" (x: double) -> double { return math.sinh(x) }
|
||||
tanh_double :: proc "c" (x: double) -> double { return math.tanh(x) }
|
||||
acosh_double :: proc "c" (x: double) -> double { return math.acosh(x) }
|
||||
asinh_double :: proc "c" (x: double) -> double { return math.asinh(x) }
|
||||
atanh_double :: proc "c" (x: double) -> double { return math.atanh(x) }
|
||||
sqrt_double :: proc "c" (x: double) -> double { return math.sqrt(x) }
|
||||
rsqrt_double :: proc "c" (x: double) -> double { return 1.0/math.sqrt(x) }
|
||||
rcp_double :: proc "c" (x: double) -> double { return 1.0/x }
|
||||
pow_double :: proc "c" (x, y: double) -> double { return math.pow(x, y) }
|
||||
exp_double :: proc "c" (x: double) -> double { return math.exp(x) }
|
||||
log_double :: proc "c" (x: double) -> double { return math.ln(x) }
|
||||
log2_double :: proc "c" (x: double) -> double { return math.log(x, 2) }
|
||||
log10_double :: proc "c" (x: double) -> double { return math.log(x, 10) }
|
||||
exp2_double :: proc "c" (x: double) -> double { return math.pow(double(2), x) }
|
||||
sign_double :: proc "c" (x: double) -> double { return math.sign(x) }
|
||||
floor_double :: proc "c" (x: double) -> double { return math.floor(x) }
|
||||
ceil_double :: proc "c" (x: double) -> double { return math.ceil(x) }
|
||||
fmod_double :: proc "c" (x, y: double) -> double { return math.mod(x, y) }
|
||||
frac_double :: proc "c" (x: double) -> double {
|
||||
if x >= 0 {
|
||||
return x - math.trunc(x)
|
||||
}
|
||||
return math.trunc(-x) + x
|
||||
}
|
||||
@@ -2033,6 +2033,55 @@ matrix4_look_at :: proc{
|
||||
}
|
||||
|
||||
|
||||
matrix4_look_at_from_fru_f16 :: proc(eye, f, r, u: Vector3f16, flip_z_axis := true) -> (m: Matrix4f16) {
|
||||
f, s, u := f, r, u
|
||||
f = normalize(f)
|
||||
s = normalize(s)
|
||||
u = normalize(u)
|
||||
fe := dot(f, eye)
|
||||
|
||||
return {
|
||||
{+s.x, +u.x, -f.x, 0},
|
||||
{+s.y, +u.y, -f.y, 0},
|
||||
{+s.z, +u.z, -f.z, 0},
|
||||
{-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
|
||||
}
|
||||
}
|
||||
matrix4_look_at_from_fru_f32 :: proc(eye, f, r, u: Vector3f32, flip_z_axis := true) -> (m: Matrix4f32) {
|
||||
f, s, u := f, r, u
|
||||
f = normalize(f)
|
||||
s = normalize(s)
|
||||
u = normalize(u)
|
||||
fe := dot(f, eye)
|
||||
|
||||
return {
|
||||
{+s.x, +u.x, -f.x, 0},
|
||||
{+s.y, +u.y, -f.y, 0},
|
||||
{+s.z, +u.z, -f.z, 0},
|
||||
{-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
|
||||
}
|
||||
}
|
||||
matrix4_look_at_from_fru_f64 :: proc(eye, f, r, u: Vector3f64, flip_z_axis := true) -> (m: Matrix4f64) {
|
||||
f, s, u := f, r, u
|
||||
f = normalize(f)
|
||||
s = normalize(s)
|
||||
u = normalize(u)
|
||||
fe := dot(f, eye)
|
||||
|
||||
return {
|
||||
{+s.x, +u.x, -f.x, 0},
|
||||
{+s.y, +u.y, -f.y, 0},
|
||||
{+s.z, +u.z, -f.z, 0},
|
||||
{-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
|
||||
}
|
||||
}
|
||||
matrix4_look_at_from_fru :: proc{
|
||||
matrix4_look_at_from_fru_f16,
|
||||
matrix4_look_at_from_fru_f32,
|
||||
matrix4_look_at_from_fru_f64,
|
||||
}
|
||||
|
||||
|
||||
matrix4_perspective_f16 :: proc(fovy, aspect, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) {
|
||||
tan_half_fovy := math.tan(0.5 * fovy)
|
||||
m[0][0] = 1 / (aspect*tan_half_fovy)
|
||||
|
||||
+722
-407
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,169 @@
|
||||
//+build !js
|
||||
package math
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign _ {
|
||||
@(link_name="llvm.sin.f16")
|
||||
sin_f16 :: proc(θ: f16) -> f16 ---
|
||||
@(link_name="llvm.sin.f32")
|
||||
sin_f32 :: proc(θ: f32) -> f32 ---
|
||||
@(link_name="llvm.sin.f64")
|
||||
sin_f64 :: proc(θ: f64) -> f64 ---
|
||||
|
||||
@(link_name="llvm.cos.f16")
|
||||
cos_f16 :: proc(θ: f16) -> f16 ---
|
||||
@(link_name="llvm.cos.f32")
|
||||
cos_f32 :: proc(θ: f32) -> f32 ---
|
||||
@(link_name="llvm.cos.f64")
|
||||
cos_f64 :: proc(θ: f64) -> f64 ---
|
||||
|
||||
@(link_name="llvm.pow.f16")
|
||||
pow_f16 :: proc(x, power: f16) -> f16 ---
|
||||
@(link_name="llvm.pow.f32")
|
||||
pow_f32 :: proc(x, power: f32) -> f32 ---
|
||||
@(link_name="llvm.pow.f64")
|
||||
pow_f64 :: proc(x, power: f64) -> f64 ---
|
||||
|
||||
@(link_name="llvm.fmuladd.f16")
|
||||
fmuladd_f16 :: proc(a, b, c: f16) -> f16 ---
|
||||
@(link_name="llvm.fmuladd.f32")
|
||||
fmuladd_f32 :: proc(a, b, c: f32) -> f32 ---
|
||||
@(link_name="llvm.fmuladd.f64")
|
||||
fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---
|
||||
|
||||
@(link_name="llvm.exp.f16")
|
||||
exp_f16 :: proc(x: f16) -> f16 ---
|
||||
@(link_name="llvm.exp.f32")
|
||||
exp_f32 :: proc(x: f32) -> f32 ---
|
||||
@(link_name="llvm.exp.f64")
|
||||
exp_f64 :: proc(x: f64) -> f64 ---
|
||||
}
|
||||
|
||||
sqrt_f16 :: proc "contextless" (x: f16) -> f16 {
|
||||
return intrinsics.sqrt(x)
|
||||
}
|
||||
sqrt_f32 :: proc "contextless" (x: f32) -> f32 {
|
||||
return intrinsics.sqrt(x)
|
||||
}
|
||||
sqrt_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
return intrinsics.sqrt(x)
|
||||
}
|
||||
|
||||
|
||||
|
||||
ln_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
// The original C code, the long comment, and the constants
|
||||
// below are from FreeBSD's /usr/src/lib/msun/src/e_log.c
|
||||
// and came with this notice.
|
||||
//
|
||||
// ====================================================
|
||||
// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
//
|
||||
// Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
// Permission to use, copy, modify, and distribute this
|
||||
// software is freely granted, provided that this notice
|
||||
// is preserved.
|
||||
// ====================================================
|
||||
//
|
||||
// __ieee754_log(x)
|
||||
// Return the logarithm of x
|
||||
//
|
||||
// Method :
|
||||
// 1. Argument Reduction: find k and f such that
|
||||
// x = 2**k * (1+f),
|
||||
// where sqrt(2)/2 < 1+f < sqrt(2) .
|
||||
//
|
||||
// 2. Approximation of log(1+f).
|
||||
// Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
|
||||
// = 2s + 2/3 s**3 + 2/5 s**5 + .....,
|
||||
// = 2s + s*R
|
||||
// We use a special Reme algorithm on [0,0.1716] to generate
|
||||
// a polynomial of degree 14 to approximate R. The maximum error
|
||||
// of this polynomial approximation is bounded by 2**-58.45. In
|
||||
// other words,
|
||||
// 2 4 6 8 10 12 14
|
||||
// R(z) ~ L1*s +L2*s +L3*s +L4*s +L5*s +L6*s +L7*s
|
||||
// (the values of L1 to L7 are listed in the program) and
|
||||
// | 2 14 | -58.45
|
||||
// | L1*s +...+L7*s - R(z) | <= 2
|
||||
// | |
|
||||
// Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
|
||||
// In order to guarantee error in log below 1ulp, we compute log by
|
||||
// log(1+f) = f - s*(f - R) (if f is not too large)
|
||||
// log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy)
|
||||
//
|
||||
// 3. Finally, log(x) = k*Ln2 + log(1+f).
|
||||
// = k*Ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*Ln2_lo)))
|
||||
// Here Ln2 is split into two floating point number:
|
||||
// Ln2_hi + Ln2_lo,
|
||||
// where n*Ln2_hi is always exact for |n| < 2000.
|
||||
//
|
||||
// Special cases:
|
||||
// log(x) is NaN with signal if x < 0 (including -INF) ;
|
||||
// log(+INF) is +INF; log(0) is -INF with signal;
|
||||
// log(NaN) is that NaN with no signal.
|
||||
//
|
||||
// Accuracy:
|
||||
// according to an error analysis, the error is always less than
|
||||
// 1 ulp (unit in the last place).
|
||||
//
|
||||
// Constants:
|
||||
// The hexadecimal values are the intended ones for the following
|
||||
// constants. The decimal values may be used, provided that the
|
||||
// compiler will convert from decimal to binary accurately enough
|
||||
// to produce the hexadecimal values shown.
|
||||
|
||||
LN2_HI :: 0h3fe62e42_fee00000 // 6.93147180369123816490e-01
|
||||
LN2_LO :: 0h3dea39ef_35793c76 // 1.90821492927058770002e-10
|
||||
L1 :: 0h3fe55555_55555593 // 6.666666666666735130e-01
|
||||
L2 :: 0h3fd99999_9997fa04 // 3.999999999940941908e-01
|
||||
L3 :: 0h3fd24924_94229359 // 2.857142874366239149e-01
|
||||
L4 :: 0h3fcc71c5_1d8e78af // 2.222219843214978396e-01
|
||||
L5 :: 0h3fc74664_96cb03de // 1.818357216161805012e-01
|
||||
L6 :: 0h3fc39a09_d078c69f // 1.531383769920937332e-01
|
||||
L7 :: 0h3fc2f112_df3e5244 // 1.479819860511658591e-01
|
||||
|
||||
switch {
|
||||
case is_nan(x) || is_inf(x, 1):
|
||||
return x
|
||||
case x < 0:
|
||||
return nan_f64()
|
||||
case x == 0:
|
||||
return inf_f64(-1)
|
||||
}
|
||||
|
||||
// reduce
|
||||
f1, ki := frexp(x)
|
||||
if f1 < SQRT_TWO/2 {
|
||||
f1 *= 2
|
||||
ki -= 1
|
||||
}
|
||||
f := f1 - 1
|
||||
k := f64(ki)
|
||||
|
||||
// compute
|
||||
s := f / (2 + f)
|
||||
s2 := s * s
|
||||
s4 := s2 * s2
|
||||
t1 := s2 * (L1 + s4*(L3+s4*(L5+s4*L7)))
|
||||
t2 := s4 * (L2 + s4*(L4+s4*L6))
|
||||
R := t1 + t2
|
||||
hfsq := 0.5 * f * f
|
||||
return k*LN2_HI - ((hfsq - (s*(hfsq+R) + k*LN2_LO)) - f)
|
||||
}
|
||||
|
||||
ln_f16 :: proc "contextless" (x: f16) -> f16 { return #force_inline f16(ln_f64(f64(x))) }
|
||||
ln_f32 :: proc "contextless" (x: f32) -> f32 { return #force_inline f32(ln_f64(f64(x))) }
|
||||
ln_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(ln_f64(f64(x))) }
|
||||
ln_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(ln_f64(f64(x))) }
|
||||
ln_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(ln_f64(f64(x))) }
|
||||
ln_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(ln_f64(f64(x))) }
|
||||
ln_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(ln_f64(f64(x))) }
|
||||
ln_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(ln_f64(f64(x))) }
|
||||
ln :: proc{
|
||||
ln_f16, ln_f16le, ln_f16be,
|
||||
ln_f32, ln_f32le, ln_f32be,
|
||||
ln_f64, ln_f64le, ln_f64be,
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//+build js
|
||||
package math
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
foreign import "odin_env"
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign odin_env {
|
||||
@(link_name="sin")
|
||||
sin_f64 :: proc(θ: f64) -> f64 ---
|
||||
@(link_name="cos")
|
||||
cos_f64 :: proc(θ: f64) -> f64 ---
|
||||
@(link_name="pow")
|
||||
pow_f64 :: proc(x, power: f64) -> f64 ---
|
||||
@(link_name="fmuladd")
|
||||
fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---
|
||||
@(link_name="ln")
|
||||
ln_f64 :: proc(x: f64) -> f64 ---
|
||||
@(link_name="exp")
|
||||
exp_f64 :: proc(x: f64) -> f64 ---
|
||||
}
|
||||
|
||||
sqrt_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
return intrinsics.sqrt(x)
|
||||
}
|
||||
|
||||
sqrt_f16 :: proc "c" (x: f16) -> f16 { return f16(sqrt_f64(f64(x))) }
|
||||
sin_f16 :: proc "c" (θ: f16) -> f16 { return f16(sin_f64(f64(θ))) }
|
||||
cos_f16 :: proc "c" (θ: f16) -> f16 { return f16(cos_f64(f64(θ))) }
|
||||
pow_f16 :: proc "c" (x, power: f16) -> f16 { return f16(pow_f64(f64(x), f64(power))) }
|
||||
fmuladd_f16 :: proc "c" (a, b, c: f16) -> f16 { return f16(fmuladd_f64(f64(a), f64(a), f64(c))) }
|
||||
ln_f16 :: proc "c" (x: f16) -> f16 { return f16(ln_f64(f64(x))) }
|
||||
exp_f16 :: proc "c" (x: f16) -> f16 { return f16(exp_f64(f64(x))) }
|
||||
|
||||
sqrt_f32 :: proc "c" (x: f32) -> f32 { return f32(sqrt_f64(f64(x))) }
|
||||
sin_f32 :: proc "c" (θ: f32) -> f32 { return f32(sin_f64(f64(θ))) }
|
||||
cos_f32 :: proc "c" (θ: f32) -> f32 { return f32(cos_f64(f64(θ))) }
|
||||
pow_f32 :: proc "c" (x, power: f32) -> f32 { return f32(pow_f64(f64(x), f64(power))) }
|
||||
fmuladd_f32 :: proc "c" (a, b, c: f32) -> f32 { return f32(fmuladd_f64(f64(a), f64(a), f64(c))) }
|
||||
ln_f32 :: proc "c" (x: f32) -> f32 { return f32(ln_f64(f64(x))) }
|
||||
exp_f32 :: proc "c" (x: f32) -> f32 { return f32(exp_f64(f64(x))) }
|
||||
|
||||
ln_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(ln_f64(f64(x))) }
|
||||
ln_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(ln_f64(f64(x))) }
|
||||
ln_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(ln_f64(f64(x))) }
|
||||
ln_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(ln_f64(f64(x))) }
|
||||
ln_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(ln_f64(f64(x))) }
|
||||
ln_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(ln_f64(f64(x))) }
|
||||
ln :: proc{
|
||||
ln_f16, ln_f16le, ln_f16be,
|
||||
ln_f32, ln_f32le, ln_f32be,
|
||||
ln_f64, ln_f64le, ln_f64be,
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
package math
|
||||
|
||||
// The original C code and the long comment below are
|
||||
// from FreeBSD's /usr/src/lib/msun/src/s_erf.c and
|
||||
// came with this notice.
|
||||
//
|
||||
// ====================================================
|
||||
// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
//
|
||||
// Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
// Permission to use, copy, modify, and distribute this
|
||||
// software is freely granted, provided that this notice
|
||||
// is preserved.
|
||||
// ====================================================
|
||||
//
|
||||
//
|
||||
// double erf(double x)
|
||||
// double erfc(double x)
|
||||
// x
|
||||
// 2 |\
|
||||
// erf(x) = --------- | exp(-t*t)dt
|
||||
// sqrt(pi) \|
|
||||
// 0
|
||||
//
|
||||
// erfc(x) = 1-erf(x)
|
||||
// Note that
|
||||
// erf(-x) = -erf(x)
|
||||
// erfc(-x) = 2 - erfc(x)
|
||||
//
|
||||
// Method:
|
||||
// 1. For |x| in [0, 0.84375]
|
||||
// erf(x) = x + x*R(x**2)
|
||||
// erfc(x) = 1 - erf(x) if x in [-.84375,0.25]
|
||||
// = 0.5 + ((0.5-x)-x*R) if x in [0.25,0.84375]
|
||||
// where R = P/Q where P is an odd poly of degree 8 and
|
||||
// Q is an odd poly of degree 10.
|
||||
// -57.90
|
||||
// | R - (erf(x)-x)/x | <= 2
|
||||
//
|
||||
//
|
||||
// Remark. The formula is derived by noting
|
||||
// erf(x) = (2/sqrt(pi))*(x - x**3/3 + x**5/10 - x**7/42 + ....)
|
||||
// and that
|
||||
// 2/sqrt(pi) = 1.128379167095512573896158903121545171688
|
||||
// is close to one. The interval is chosen because the fix
|
||||
// point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is
|
||||
// near 0.6174), and by some experiment, 0.84375 is chosen to
|
||||
// guarantee the error is less than one ulp for erf.
|
||||
//
|
||||
// 2. For |x| in [0.84375,1.25], let s = |x| - 1, and
|
||||
// c = 0.84506291151 rounded to single (24 bits)
|
||||
// erf(x) = sign(x) * (c + P1(s)/Q1(s))
|
||||
// erfc(x) = (1-c) - P1(s)/Q1(s) if x > 0
|
||||
// 1+(c+P1(s)/Q1(s)) if x < 0
|
||||
// |P1/Q1 - (erf(|x|)-c)| <= 2**-59.06
|
||||
// Remark: here we use the taylor series expansion at x=1.
|
||||
// erf(1+s) = erf(1) + s*Poly(s)
|
||||
// = 0.845.. + P1(s)/Q1(s)
|
||||
// That is, we use rational approximation to approximate
|
||||
// erf(1+s) - (c = (single)0.84506291151)
|
||||
// Note that |P1/Q1|< 0.078 for x in [0.84375,1.25]
|
||||
// where
|
||||
// P1(s) = degree 6 poly in s
|
||||
// Q1(s) = degree 6 poly in s
|
||||
//
|
||||
// 3. For x in [1.25,1/0.35(~2.857143)],
|
||||
// erfc(x) = (1/x)*exp(-x*x-0.5625+R1/S1)
|
||||
// erf(x) = 1 - erfc(x)
|
||||
// where
|
||||
// R1(z) = degree 7 poly in z, (z=1/x**2)
|
||||
// S1(z) = degree 8 poly in z
|
||||
//
|
||||
// 4. For x in [1/0.35,28]
|
||||
// erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0
|
||||
// = 2.0 - (1/x)*exp(-x*x-0.5625+R2/S2) if -6<x<0
|
||||
// = 2.0 - tiny (if x <= -6)
|
||||
// erf(x) = sign(x)*(1.0 - erfc(x)) if x < 6, else
|
||||
// erf(x) = sign(x)*(1.0 - tiny)
|
||||
// where
|
||||
// R2(z) = degree 6 poly in z, (z=1/x**2)
|
||||
// S2(z) = degree 7 poly in z
|
||||
//
|
||||
// Note1:
|
||||
// To compute exp(-x*x-0.5625+R/S), let s be a single
|
||||
// precision number and s := x; then
|
||||
// -x*x = -s*s + (s-x)*(s+x)
|
||||
// exp(-x*x-0.5626+R/S) =
|
||||
// exp(-s*s-0.5625)*exp((s-x)*(s+x)+R/S);
|
||||
// Note2:
|
||||
// Here 4 and 5 make use of the asymptotic series
|
||||
// exp(-x*x)
|
||||
// erfc(x) ~ ---------- * ( 1 + Poly(1/x**2) )
|
||||
// x*sqrt(pi)
|
||||
// We use rational approximation to approximate
|
||||
// g(s)=f(1/x**2) = log(erfc(x)*x) - x*x + 0.5625
|
||||
// Here is the error bound for R1/S1 and R2/S2
|
||||
// |R1/S1 - f(x)| < 2**(-62.57)
|
||||
// |R2/S2 - f(x)| < 2**(-61.52)
|
||||
//
|
||||
// 5. For inf > x >= 28
|
||||
// erf(x) = sign(x) *(1 - tiny) (raise inexact)
|
||||
// erfc(x) = tiny*tiny (raise underflow) if x > 0
|
||||
// = 2 - tiny if x<0
|
||||
//
|
||||
// 7. Special case:
|
||||
// erf(0) = 0, erf(inf) = 1, erf(-inf) = -1,
|
||||
// erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2,
|
||||
// erfc/erf(NaN) is NaN
|
||||
|
||||
erf :: proc{
|
||||
erf_f16,
|
||||
erf_f16le,
|
||||
erf_f16be,
|
||||
erf_f32,
|
||||
erf_f32le,
|
||||
erf_f32be,
|
||||
erf_f64,
|
||||
}
|
||||
|
||||
erf_f16 :: proc "contextless" (x: f16) -> f16 { return f16(erf_f64(f64(x))) }
|
||||
erf_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(erf_f64(f64(x))) }
|
||||
erf_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(erf_f64(f64(x))) }
|
||||
erf_f32 :: proc "contextless" (x: f32) -> f32 { return f32(erf_f64(f64(x))) }
|
||||
erf_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(erf_f64(f64(x))) }
|
||||
erf_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(erf_f64(f64(x))) }
|
||||
|
||||
erf_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
erx :: 0h3FEB0AC160000000
|
||||
// Coefficients for approximation to erf in [0, 0.84375]
|
||||
efx :: 0h3FC06EBA8214DB69
|
||||
efx8 :: 0h3FF06EBA8214DB69
|
||||
pp0 :: 0h3FC06EBA8214DB68
|
||||
pp1 :: 0hBFD4CD7D691CB913
|
||||
pp2 :: 0hBF9D2A51DBD7194F
|
||||
pp3 :: 0hBF77A291236668E4
|
||||
pp4 :: 0hBEF8EAD6120016AC
|
||||
qq1 :: 0h3FD97779CDDADC09
|
||||
qq2 :: 0h3FB0A54C5536CEBA
|
||||
qq3 :: 0h3F74D022C4D36B0F
|
||||
qq4 :: 0h3F215DC9221C1A10
|
||||
qq5 :: 0hBED09C4342A26120
|
||||
// Coefficients for approximation to erf in [0.84375, 1.25]
|
||||
pa0 :: 0hBF6359B8BEF77538
|
||||
pa1 :: 0h3FDA8D00AD92B34D
|
||||
pa2 :: 0hBFD7D240FBB8C3F1
|
||||
pa3 :: 0h3FD45FCA805120E4
|
||||
pa4 :: 0hBFBC63983D3E28EC
|
||||
pa5 :: 0h3FA22A36599795EB
|
||||
pa6 :: 0hBF61BF380A96073F
|
||||
qa1 :: 0h3FBB3E6618EEE323
|
||||
qa2 :: 0h3FE14AF092EB6F33
|
||||
qa3 :: 0h3FB2635CD99FE9A7
|
||||
qa4 :: 0h3FC02660E763351F
|
||||
qa5 :: 0h3F8BEDC26B51DD1C
|
||||
qa6 :: 0h3F888B545735151D
|
||||
// Coefficients for approximation to erfc in [1.25, 1/0.35]
|
||||
ra0 :: 0hBF843412600D6435
|
||||
ra1 :: 0hBFE63416E4BA7360
|
||||
ra2 :: 0hC0251E0441B0E726
|
||||
ra3 :: 0hC04F300AE4CBA38D
|
||||
ra4 :: 0hC0644CB184282266
|
||||
ra5 :: 0hC067135CEBCCABB2
|
||||
ra6 :: 0hC054526557E4D2F2
|
||||
ra7 :: 0hC023A0EFC69AC25C
|
||||
sa1 :: 0h4033A6B9BD707687
|
||||
sa2 :: 0h4061350C526AE721
|
||||
sa3 :: 0h407B290DD58A1A71
|
||||
sa4 :: 0h40842B1921EC2868
|
||||
sa5 :: 0h407AD02157700314
|
||||
sa6 :: 0h405B28A3EE48AE2C
|
||||
sa7 :: 0h401A47EF8E484A93
|
||||
sa8 :: 0hBFAEEFF2EE749A62
|
||||
// Coefficients for approximation to erfc in [1/.35, 28]
|
||||
rb0 :: 0hBF84341239E86F4A
|
||||
rb1 :: 0hBFE993BA70C285DE
|
||||
rb2 :: 0hC031C209555F995A
|
||||
rb3 :: 0hC064145D43C5ED98
|
||||
rb4 :: 0hC083EC881375F228
|
||||
rb5 :: 0hC09004616A2E5992
|
||||
rb6 :: 0hC07E384E9BDC383F
|
||||
sb1 :: 0h403E568B261D5190
|
||||
sb2 :: 0h40745CAE221B9F0A
|
||||
sb3 :: 0h409802EB189D5118
|
||||
sb4 :: 0h40A8FFB7688C246A
|
||||
sb5 :: 0h40A3F219CEDF3BE6
|
||||
sb6 :: 0h407DA874E79FE763
|
||||
sb7 :: 0hC03670E242712D62
|
||||
|
||||
|
||||
VERY_TINY :: 0h0080000000000000
|
||||
SMALL :: 1.0 / (1 << 28) // 2**-28
|
||||
|
||||
// special cases
|
||||
switch {
|
||||
case is_nan(x):
|
||||
return nan_f64()
|
||||
case is_inf(x, 1):
|
||||
return 1
|
||||
case is_inf(x, -1):
|
||||
return -1
|
||||
}
|
||||
x := x
|
||||
sign := false
|
||||
if x < 0 {
|
||||
x = -x
|
||||
sign = true
|
||||
}
|
||||
if x < 0.84375 { // |x| < 0.84375
|
||||
temp: f64
|
||||
if x < SMALL { // |x| < 2**-28
|
||||
if x < VERY_TINY {
|
||||
temp = 0.125 * (8.0*x + efx8*x) // avoid underflow
|
||||
} else {
|
||||
temp = x + efx*x
|
||||
}
|
||||
} else {
|
||||
z := x * x
|
||||
r := pp0 + z*(pp1+z*(pp2+z*(pp3+z*pp4)))
|
||||
s := 1 + z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5))))
|
||||
y := r / s
|
||||
temp = x + x*y
|
||||
}
|
||||
if sign {
|
||||
return -temp
|
||||
}
|
||||
return temp
|
||||
}
|
||||
if x < 1.25 { // 0.84375 <= |x| < 1.25
|
||||
s := x - 1
|
||||
P := pa0 + s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6)))))
|
||||
Q := 1 + s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6)))))
|
||||
if sign {
|
||||
return -erx - P/Q
|
||||
}
|
||||
return erx + P/Q
|
||||
}
|
||||
if x >= 6 { // inf > |x| >= 6
|
||||
if sign {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
s := 1 / (x * x)
|
||||
R, S: f64
|
||||
if x < 1/0.35 { // |x| < 1 / 0.35 ~ 2.857143
|
||||
R = ra0 + s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*(ra5+s*(ra6+s*ra7))))))
|
||||
S = 1 + s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(sa5+s*(sa6+s*(sa7+s*sa8)))))))
|
||||
} else { // |x| >= 1 / 0.35 ~ 2.857143
|
||||
R = rb0 + s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(rb5+s*rb6)))))
|
||||
S = 1 + s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*(sb5+s*(sb6+s*sb7))))))
|
||||
}
|
||||
z := transmute(f64)(0xffffffff00000000 & transmute(u64)x) // pseudo-single (20-bit) precision x
|
||||
r := exp(-z*z-0.5625) * exp((z-x)*(z+x)+R/S)
|
||||
if sign {
|
||||
return r/x - 1
|
||||
}
|
||||
return 1 - r/x
|
||||
}
|
||||
|
||||
|
||||
erfc :: proc{
|
||||
erfc_f16,
|
||||
erfc_f16le,
|
||||
erfc_f16be,
|
||||
erfc_f32,
|
||||
erfc_f32le,
|
||||
erfc_f32be,
|
||||
erfc_f64,
|
||||
}
|
||||
|
||||
erfc_f16 :: proc "contextless" (x: f16) -> f16 { return f16(erfc_f64(f64(x))) }
|
||||
erfc_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(erfc_f64(f64(x))) }
|
||||
erfc_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(erfc_f64(f64(x))) }
|
||||
erfc_f32 :: proc "contextless" (x: f32) -> f32 { return f32(erfc_f64(f64(x))) }
|
||||
erfc_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(erfc_f64(f64(x))) }
|
||||
erfc_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(erfc_f64(f64(x))) }
|
||||
|
||||
erfc_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
erx :: 0h3FEB0AC160000000
|
||||
// Coefficients for approximation to erf in [0, 0.84375]
|
||||
efx :: 0h3FC06EBA8214DB69
|
||||
efx8 :: 0h3FF06EBA8214DB69
|
||||
pp0 :: 0h3FC06EBA8214DB68
|
||||
pp1 :: 0hBFD4CD7D691CB913
|
||||
pp2 :: 0hBF9D2A51DBD7194F
|
||||
pp3 :: 0hBF77A291236668E4
|
||||
pp4 :: 0hBEF8EAD6120016AC
|
||||
qq1 :: 0h3FD97779CDDADC09
|
||||
qq2 :: 0h3FB0A54C5536CEBA
|
||||
qq3 :: 0h3F74D022C4D36B0F
|
||||
qq4 :: 0h3F215DC9221C1A10
|
||||
qq5 :: 0hBED09C4342A26120
|
||||
// Coefficients for approximation to erf in [0.84375, 1.25]
|
||||
pa0 :: 0hBF6359B8BEF77538
|
||||
pa1 :: 0h3FDA8D00AD92B34D
|
||||
pa2 :: 0hBFD7D240FBB8C3F1
|
||||
pa3 :: 0h3FD45FCA805120E4
|
||||
pa4 :: 0hBFBC63983D3E28EC
|
||||
pa5 :: 0h3FA22A36599795EB
|
||||
pa6 :: 0hBF61BF380A96073F
|
||||
qa1 :: 0h3FBB3E6618EEE323
|
||||
qa2 :: 0h3FE14AF092EB6F33
|
||||
qa3 :: 0h3FB2635CD99FE9A7
|
||||
qa4 :: 0h3FC02660E763351F
|
||||
qa5 :: 0h3F8BEDC26B51DD1C
|
||||
qa6 :: 0h3F888B545735151D
|
||||
// Coefficients for approximation to erfc in [1.25, 1/0.35]
|
||||
ra0 :: 0hBF843412600D6435
|
||||
ra1 :: 0hBFE63416E4BA7360
|
||||
ra2 :: 0hC0251E0441B0E726
|
||||
ra3 :: 0hC04F300AE4CBA38D
|
||||
ra4 :: 0hC0644CB184282266
|
||||
ra5 :: 0hC067135CEBCCABB2
|
||||
ra6 :: 0hC054526557E4D2F2
|
||||
ra7 :: 0hC023A0EFC69AC25C
|
||||
sa1 :: 0h4033A6B9BD707687
|
||||
sa2 :: 0h4061350C526AE721
|
||||
sa3 :: 0h407B290DD58A1A71
|
||||
sa4 :: 0h40842B1921EC2868
|
||||
sa5 :: 0h407AD02157700314
|
||||
sa6 :: 0h405B28A3EE48AE2C
|
||||
sa7 :: 0h401A47EF8E484A93
|
||||
sa8 :: 0hBFAEEFF2EE749A62
|
||||
// Coefficients for approximation to erfc in [1/.35, 28]
|
||||
rb0 :: 0hBF84341239E86F4A
|
||||
rb1 :: 0hBFE993BA70C285DE
|
||||
rb2 :: 0hC031C209555F995A
|
||||
rb3 :: 0hC064145D43C5ED98
|
||||
rb4 :: 0hC083EC881375F228
|
||||
rb5 :: 0hC09004616A2E5992
|
||||
rb6 :: 0hC07E384E9BDC383F
|
||||
sb1 :: 0h403E568B261D5190
|
||||
sb2 :: 0h40745CAE221B9F0A
|
||||
sb3 :: 0h409802EB189D5118
|
||||
sb4 :: 0h40A8FFB7688C246A
|
||||
sb5 :: 0h40A3F219CEDF3BE6
|
||||
sb6 :: 0h407DA874E79FE763
|
||||
sb7 :: 0hC03670E242712D62
|
||||
|
||||
TINY :: 1.0 / (1 << 56) // 2**-56
|
||||
// special cases
|
||||
switch {
|
||||
case is_nan(x):
|
||||
return nan_f64()
|
||||
case is_inf(x, 1):
|
||||
return 0
|
||||
case is_inf(x, -1):
|
||||
return 2
|
||||
}
|
||||
x := x
|
||||
sign := false
|
||||
if x < 0 {
|
||||
x = -x
|
||||
sign = true
|
||||
}
|
||||
if x < 0.84375 { // |x| < 0.84375
|
||||
temp: f64
|
||||
if x < TINY { // |x| < 2**-56
|
||||
temp = x
|
||||
} else {
|
||||
z := x * x
|
||||
r := pp0 + z*(pp1+z*(pp2+z*(pp3+z*pp4)))
|
||||
s := 1 + z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5))))
|
||||
y := r / s
|
||||
if x < 0.25 { // |x| < 1/4
|
||||
temp = x + x*y
|
||||
} else {
|
||||
temp = 0.5 + (x*y + (x - 0.5))
|
||||
}
|
||||
}
|
||||
if sign {
|
||||
return 1 + temp
|
||||
}
|
||||
return 1 - temp
|
||||
}
|
||||
if x < 1.25 { // 0.84375 <= |x| < 1.25
|
||||
s := x - 1
|
||||
P := pa0 + s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6)))))
|
||||
Q := 1 + s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6)))))
|
||||
if sign {
|
||||
return 1 + erx + P/Q
|
||||
}
|
||||
return 1 - erx - P/Q
|
||||
|
||||
}
|
||||
if x < 28 { // |x| < 28
|
||||
s := 1 / (x * x)
|
||||
R, S: f64
|
||||
if x < 1/0.35 { // |x| < 1 / 0.35 ~ 2.857143
|
||||
R = ra0 + s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*(ra5+s*(ra6+s*ra7))))))
|
||||
S = 1 + s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(sa5+s*(sa6+s*(sa7+s*sa8)))))))
|
||||
} else { // |x| >= 1 / 0.35 ~ 2.857143
|
||||
if sign && x > 6 {
|
||||
return 2 // x < -6
|
||||
}
|
||||
R = rb0 + s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(rb5+s*rb6)))))
|
||||
S = 1 + s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*(sb5+s*(sb6+s*sb7))))))
|
||||
}
|
||||
z := transmute(f64)(0xffffffff00000000 & transmute(u64)x) // pseudo-single (20-bit) precision x
|
||||
r := exp(-z*z-0.5625) * exp((z-x)*(z+x)+R/S)
|
||||
if sign {
|
||||
return 2 - r/x
|
||||
}
|
||||
return r / x
|
||||
}
|
||||
if sign {
|
||||
return 2
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
package math
|
||||
|
||||
// The original C code, the long comment, and the constants
|
||||
// below are from http://netlib.sandia.gov/cephes/cprob/gamma.c.
|
||||
//
|
||||
// tgamma.c
|
||||
//
|
||||
// Gamma function
|
||||
//
|
||||
// SYNOPSIS:
|
||||
//
|
||||
// double x, y, tgamma();
|
||||
// extern int signgam;
|
||||
//
|
||||
// y = tgamma( x );
|
||||
//
|
||||
// DESCRIPTION:
|
||||
//
|
||||
// Returns gamma function of the argument. The result is
|
||||
// correctly signed, and the sign (+1 or -1) is also
|
||||
// returned in a global (extern) variable named signgam.
|
||||
// This variable is also filled in by the logarithmic gamma
|
||||
// function lgamma().
|
||||
//
|
||||
// Arguments |x| <= 34 are reduced by recurrence and the function
|
||||
// approximated by a rational function of degree 6/7 in the
|
||||
// interval (2,3). Large arguments are handled by Stirling's
|
||||
// formula. Large negative arguments are made positive using
|
||||
// a reflection formula.
|
||||
//
|
||||
// ACCURACY:
|
||||
//
|
||||
// Relative error:
|
||||
// arithmetic domain # trials peak rms
|
||||
// DEC -34, 34 10000 1.3e-16 2.5e-17
|
||||
// IEEE -170,-33 20000 2.3e-15 3.3e-16
|
||||
// IEEE -33, 33 20000 9.4e-16 2.2e-16
|
||||
// IEEE 33, 171.6 20000 2.3e-15 3.2e-16
|
||||
//
|
||||
// Error for arguments outside the test range will be larger
|
||||
// owing to error amplification by the exponential function.
|
||||
//
|
||||
// Cephes Math Library Release 2.8: June, 2000
|
||||
// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
|
||||
//
|
||||
// The readme file at http://netlib.sandia.gov/cephes/ says:
|
||||
// Some software in this archive may be from the book _Methods and
|
||||
// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
|
||||
// International, 1989) or from the Cephes Mathematical Library, a
|
||||
// commercial product. In either event, it is copyrighted by the author.
|
||||
// What you see here may be used freely but it comes with no support or
|
||||
// guarantee.
|
||||
//
|
||||
// The two known misprints in the book are repaired here in the
|
||||
// source listings for the gamma function and the incomplete beta
|
||||
// integral.
|
||||
//
|
||||
// Stephen L. Moshier
|
||||
// moshier@na-net.ornl.gov
|
||||
|
||||
// Gamma function computed by Stirling's formula.
|
||||
// The pair of results must be multiplied together to get the actual answer.
|
||||
// The multiplication is left to the caller so that, if careful, the caller can avoid
|
||||
// infinity for 172 <= x <= 180.
|
||||
// The polynomial is valid for 33 <= x <= 172; larger values are only used
|
||||
// in reciprocal and produce denormalized floats. The lower precision there
|
||||
// masks any imprecision in the polynomial.
|
||||
@(private="file")
|
||||
stirling :: proc "contextless" (x: f64) -> (f64, f64) {
|
||||
@(static) gamS := [?]f64{
|
||||
+7.87311395793093628397e-04,
|
||||
-2.29549961613378126380e-04,
|
||||
-2.68132617805781232825e-03,
|
||||
+3.47222221605458667310e-03,
|
||||
+8.33333333333482257126e-02,
|
||||
}
|
||||
|
||||
if x > 200 {
|
||||
return inf_f64(1), 1
|
||||
}
|
||||
SQRT_TWO_PI :: 0h40040d931ff62706 // 2.506628274631000502417
|
||||
MAX_STIRLING :: 143.01608
|
||||
w := 1 / x
|
||||
w = 1 + w*((((gamS[0]*w+gamS[1])*w+gamS[2])*w+gamS[3])*w+gamS[4])
|
||||
y1 := exp(x)
|
||||
y2 := 1.0
|
||||
if x > MAX_STIRLING { // avoid pow() overflow
|
||||
v := pow(x, 0.5*x-0.25)
|
||||
y1, y2 = v, v/y1
|
||||
} else {
|
||||
y1 = pow(x, x-0.5) / y1
|
||||
}
|
||||
return y1, SQRT_TWO_PI * w * y2
|
||||
}
|
||||
|
||||
gamma_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
is_neg_int :: proc "contextless" (x: f64) -> bool {
|
||||
if x < 0 {
|
||||
_, xf := modf(x)
|
||||
return xf == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@(static) gamP := [?]f64{
|
||||
1.60119522476751861407e-04,
|
||||
1.19135147006586384913e-03,
|
||||
1.04213797561761569935e-02,
|
||||
4.76367800457137231464e-02,
|
||||
2.07448227648435975150e-01,
|
||||
4.94214826801497100753e-01,
|
||||
9.99999999999999996796e-01,
|
||||
}
|
||||
@(static) gamQ := [?]f64{
|
||||
-2.31581873324120129819e-05,
|
||||
+5.39605580493303397842e-04,
|
||||
-4.45641913851797240494e-03,
|
||||
+1.18139785222060435552e-02,
|
||||
+3.58236398605498653373e-02,
|
||||
-2.34591795718243348568e-01,
|
||||
+7.14304917030273074085e-02,
|
||||
+1.00000000000000000320e+00,
|
||||
}
|
||||
|
||||
|
||||
EULER :: 0.57721566490153286060651209008240243104215933593992 // A001620
|
||||
|
||||
switch {
|
||||
case is_neg_int(x) || is_inf(x, -1) || is_nan(x):
|
||||
return nan_f64()
|
||||
case is_inf(x, 1):
|
||||
return inf_f64(1)
|
||||
case x == 0:
|
||||
if signbit(x) {
|
||||
return inf_f64(-1)
|
||||
}
|
||||
return inf_f64(1)
|
||||
}
|
||||
|
||||
x := x
|
||||
q := abs(x)
|
||||
p := floor(q)
|
||||
if q > 33 {
|
||||
if x >= 0 {
|
||||
y1, y2 := stirling(x)
|
||||
return y1 * y2
|
||||
}
|
||||
// Note: x is negative but (checked above) not a negative integer,
|
||||
// so x must be small enough to be in range for conversion to i64.
|
||||
// If |x| were >= 2⁶³ it would have to be an integer.
|
||||
signgam := 1
|
||||
if ip := i64(p); ip&1 == 0 {
|
||||
signgam = -1
|
||||
}
|
||||
z := q - p
|
||||
if z > 0.5 {
|
||||
p = p + 1
|
||||
z = q - p
|
||||
}
|
||||
z = q * sin(PI*z)
|
||||
if z == 0 {
|
||||
return inf_f64(signgam)
|
||||
}
|
||||
sq1, sq2 := stirling(q)
|
||||
absz := abs(z)
|
||||
d := absz * sq1 * sq2
|
||||
if is_inf(d, 0) {
|
||||
z = PI / absz / sq1 / sq2
|
||||
} else {
|
||||
z = PI / d
|
||||
}
|
||||
return f64(signgam) * z
|
||||
}
|
||||
|
||||
// Reduce argument
|
||||
z := 1.0
|
||||
for x >= 3 {
|
||||
x = x - 1
|
||||
z = z * x
|
||||
}
|
||||
for x < 0 {
|
||||
if x > -1e-09 {
|
||||
if x == 0 {
|
||||
return inf_f64(1)
|
||||
}
|
||||
return z / ((1 + EULER*x) * x)
|
||||
}
|
||||
z = z / x
|
||||
x = x + 1
|
||||
}
|
||||
for x < 2 {
|
||||
if x < 1e-09 {
|
||||
if x == 0 {
|
||||
return inf_f64(1)
|
||||
}
|
||||
return z / ((1 + EULER*x) * x)
|
||||
}
|
||||
z = z / x
|
||||
x = x + 1
|
||||
}
|
||||
|
||||
if x == 2 {
|
||||
return z
|
||||
}
|
||||
|
||||
x = x - 2
|
||||
p = (((((x*gamP[0]+gamP[1])*x+gamP[2])*x+gamP[3])*x+gamP[4])*x+gamP[5])*x + gamP[6]
|
||||
q = ((((((x*gamQ[0]+gamQ[1])*x+gamQ[2])*x+gamQ[3])*x+gamQ[4])*x+gamQ[5])*x+gamQ[6])*x + gamQ[7]
|
||||
return z * p / q
|
||||
}
|
||||
|
||||
|
||||
gamma_f16 :: proc "contextless" (x: f16) -> f16 { return f16(gamma_f64(f64(x))) }
|
||||
gamma_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(gamma_f64(f64(x))) }
|
||||
gamma_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(gamma_f64(f64(x))) }
|
||||
gamma_f32 :: proc "contextless" (x: f32) -> f32 { return f32(gamma_f64(f64(x))) }
|
||||
gamma_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(gamma_f64(f64(x))) }
|
||||
gamma_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(gamma_f64(f64(x))) }
|
||||
gamma_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(gamma_f64(f64(x))) }
|
||||
gamma_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(gamma_f64(f64(x))) }
|
||||
|
||||
gamma :: proc{
|
||||
gamma_f16, gamma_f16le, gamma_f16be,
|
||||
gamma_f32, gamma_f32le, gamma_f32be,
|
||||
gamma_f64, gamma_f64le, gamma_f64be,
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
package math
|
||||
|
||||
// The original C code and the long comment below are
|
||||
// from FreeBSD's /usr/src/lib/msun/src/e_lgamma_r.c and
|
||||
// came with this notice.
|
||||
//
|
||||
// ====================================================
|
||||
// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
//
|
||||
// Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
// Permission to use, copy, modify, and distribute this
|
||||
// software is freely granted, provided that this notice
|
||||
// is preserved.
|
||||
// ====================================================
|
||||
//
|
||||
// __ieee754_lgamma_r(x, signgamp)
|
||||
// Reentrant version of the logarithm of the Gamma function
|
||||
// with user provided pointer for the sign of Gamma(x).
|
||||
//
|
||||
// Method:
|
||||
// 1. Argument Reduction for 0 < x <= 8
|
||||
// Since gamma(1+s)=s*gamma(s), for x in [0,8], we may
|
||||
// reduce x to a number in [1.5,2.5] by
|
||||
// lgamma(1+s) = log(s) + lgamma(s)
|
||||
// for example,
|
||||
// lgamma(7.3) = log(6.3) + lgamma(6.3)
|
||||
// = log(6.3*5.3) + lgamma(5.3)
|
||||
// = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3)
|
||||
// 2. Polynomial approximation of lgamma around its
|
||||
// minimum (ymin=1.461632144968362245) to maintain monotonicity.
|
||||
// On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use
|
||||
// Let z = x-ymin;
|
||||
// lgamma(x) = -1.214862905358496078218 + z**2*poly(z)
|
||||
// poly(z) is a 14 degree polynomial.
|
||||
// 2. Rational approximation in the primary interval [2,3]
|
||||
// We use the following approximation:
|
||||
// s = x-2.0;
|
||||
// lgamma(x) = 0.5*s + s*P(s)/Q(s)
|
||||
// with accuracy
|
||||
// |P/Q - (lgamma(x)-0.5s)| < 2**-61.71
|
||||
// Our algorithms are based on the following observation
|
||||
//
|
||||
// zeta(2)-1 2 zeta(3)-1 3
|
||||
// lgamma(2+s) = s*(1-Euler) + --------- * s - --------- * s + ...
|
||||
// 2 3
|
||||
//
|
||||
// where Euler = 0.5772156649... is the Euler constant, which
|
||||
// is very close to 0.5.
|
||||
//
|
||||
// 3. For x>=8, we have
|
||||
// lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+....
|
||||
// (better formula:
|
||||
// lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...)
|
||||
// Let z = 1/x, then we approximation
|
||||
// f(z) = lgamma(x) - (x-0.5)(log(x)-1)
|
||||
// by
|
||||
// 3 5 11
|
||||
// w = w0 + w1*z + w2*z + w3*z + ... + w6*z
|
||||
// where
|
||||
// |w - f(z)| < 2**-58.74
|
||||
//
|
||||
// 4. For negative x, since (G is gamma function)
|
||||
// -x*G(-x)*G(x) = pi/sin(pi*x),
|
||||
// we have
|
||||
// G(x) = pi/(sin(pi*x)*(-x)*G(-x))
|
||||
// since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0
|
||||
// Hence, for x<0, signgam = sign(sin(pi*x)) and
|
||||
// lgamma(x) = log(|Gamma(x)|)
|
||||
// = log(pi/(|x*sin(pi*x)|)) - lgamma(-x);
|
||||
// Note: one should avoid computing pi*(-x) directly in the
|
||||
// computation of sin(pi*(-x)).
|
||||
//
|
||||
// 5. Special Cases
|
||||
// lgamma(2+s) ~ s*(1-Euler) for tiny s
|
||||
// lgamma(1)=lgamma(2)=0
|
||||
// lgamma(x) ~ -log(x) for tiny x
|
||||
// lgamma(0) = lgamma(inf) = inf
|
||||
// lgamma(-integer) = +-inf
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
|
||||
sin_pi :: proc "contextless" (x: f64) -> f64 {
|
||||
if x < 0.25 {
|
||||
return -sin(PI * x)
|
||||
}
|
||||
x := x
|
||||
|
||||
// argument reduction
|
||||
z := floor(x)
|
||||
n: int
|
||||
if z != x { // inexact
|
||||
x = mod(x, 2)
|
||||
n = int(x * 4)
|
||||
} else {
|
||||
if x >= TWO_53 { // x must be even
|
||||
x = 0
|
||||
n = 0
|
||||
} else {
|
||||
if x < TWO_52 {
|
||||
z = x + TWO_52 // exact
|
||||
}
|
||||
n = int(1 & transmute(u64)z)
|
||||
x = f64(n)
|
||||
n <<= 2
|
||||
}
|
||||
}
|
||||
switch n {
|
||||
case 0:
|
||||
x = sin(PI * x)
|
||||
case 1, 2:
|
||||
x = cos(PI * (0.5 - x))
|
||||
case 3, 4:
|
||||
x = sin(PI * (1 - x))
|
||||
case 5, 6:
|
||||
x = -cos(PI * (x - 1.5))
|
||||
case:
|
||||
x = sin(PI * (x - 2))
|
||||
}
|
||||
return -x
|
||||
}
|
||||
|
||||
@static lgamA := [?]f64{
|
||||
0h3FB3C467E37DB0C8,
|
||||
0h3FD4A34CC4A60FAD,
|
||||
0h3FB13E001A5562A7,
|
||||
0h3F951322AC92547B,
|
||||
0h3F7E404FB68FEFE8,
|
||||
0h3F67ADD8CCB7926B,
|
||||
0h3F538A94116F3F5D,
|
||||
0h3F40B6C689B99C00,
|
||||
0h3F2CF2ECED10E54D,
|
||||
0h3F1C5088987DFB07,
|
||||
0h3EFA7074428CFA52,
|
||||
0h3F07858E90A45837,
|
||||
}
|
||||
@static lgamR := [?]f64{
|
||||
1.0,
|
||||
0h3FF645A762C4AB74,
|
||||
0h3FE71A1893D3DCDC,
|
||||
0h3FC601EDCCFBDF27,
|
||||
0h3F9317EA742ED475,
|
||||
0h3F497DDACA41A95B,
|
||||
0h3EDEBAF7A5B38140,
|
||||
}
|
||||
@static lgamS := [?]f64{
|
||||
0hBFB3C467E37DB0C8,
|
||||
0h3FCB848B36E20878,
|
||||
0h3FD4D98F4F139F59,
|
||||
0h3FC2BB9CBEE5F2F7,
|
||||
0h3F9B481C7E939961,
|
||||
0h3F5E26B67368F239,
|
||||
0h3F00BFECDD17E945,
|
||||
}
|
||||
@static lgamT := [?]f64{
|
||||
0h3FDEF72BC8EE38A2,
|
||||
0hBFC2E4278DC6C509,
|
||||
0h3FB08B4294D5419B,
|
||||
0hBFA0C9A8DF35B713,
|
||||
0h3F9266E7970AF9EC,
|
||||
0hBF851F9FBA91EC6A,
|
||||
0h3F78FCE0E370E344,
|
||||
0hBF6E2EFFB3E914D7,
|
||||
0h3F6282D32E15C915,
|
||||
0hBF56FE8EBF2D1AF1,
|
||||
0h3F4CDF0CEF61A8E9,
|
||||
0hBF41A6109C73E0EC,
|
||||
0h3F34AF6D6C0EBBF7,
|
||||
0hBF347F24ECC38C38,
|
||||
0h3F35FD3EE8C2D3F4,
|
||||
}
|
||||
@static lgamU := [?]f64{
|
||||
0hBFB3C467E37DB0C8,
|
||||
0h3FE4401E8B005DFF,
|
||||
0h3FF7475CD119BD6F,
|
||||
0h3FEF497644EA8450,
|
||||
0h3FCD4EAEF6010924,
|
||||
0h3F8B678BBF2BAB09,
|
||||
}
|
||||
@static lgamV := [?]f64{
|
||||
1.0,
|
||||
0h4003A5D7C2BD619C,
|
||||
0h40010725A42B18F5,
|
||||
0h3FE89DFBE45050AF,
|
||||
0h3FBAAE55D6537C88,
|
||||
0h3F6A5ABB57D0CF61,
|
||||
}
|
||||
@static lgamW := [?]f64{
|
||||
0h3FDACFE390C97D69,
|
||||
0h3FB555555555553B,
|
||||
0hBF66C16C16B02E5C,
|
||||
0h3F4A019F98CF38B6,
|
||||
0hBF4380CB8C0FE741,
|
||||
0h3F4B67BA4CDAD5D1,
|
||||
0hBF5AB89D0B9E43E4,
|
||||
}
|
||||
|
||||
|
||||
Y_MIN :: 0h3ff762d86356be3f // 1.461632144968362245
|
||||
TWO_52 :: 0h4330000000000000 // ~4.5036e+15
|
||||
TWO_53 :: 0h4340000000000000 // ~9.0072e+15
|
||||
TWO_58 :: 0h4390000000000000 // ~2.8823e+17
|
||||
TINY :: 0h3b90000000000000 // ~8.47033e-22
|
||||
Tc :: 0h3FF762D86356BE3F
|
||||
Tf :: 0hBFBF19B9BCC38A42
|
||||
Tt :: 0hBC50C7CAA48A971F
|
||||
|
||||
// special cases
|
||||
sign = 1
|
||||
switch {
|
||||
case is_nan(x):
|
||||
lgamma = x
|
||||
return
|
||||
case is_inf(x):
|
||||
lgamma = x
|
||||
return
|
||||
case x == 0:
|
||||
lgamma = inf_f64(1)
|
||||
return
|
||||
}
|
||||
|
||||
x := x
|
||||
neg := false
|
||||
if x < 0 {
|
||||
x = -x
|
||||
neg = true
|
||||
}
|
||||
|
||||
if x < TINY { // if |x| < 2**-70, return -log(|x|)
|
||||
if neg {
|
||||
sign = -1
|
||||
}
|
||||
lgamma = -ln(x)
|
||||
return
|
||||
}
|
||||
nadj: f64
|
||||
if neg {
|
||||
if x >= TWO_52 { // |x| >= 2**52, must be -integer
|
||||
lgamma = inf_f64(1)
|
||||
return
|
||||
}
|
||||
t := sin_pi(x)
|
||||
if t == 0 {
|
||||
lgamma = inf_f64(1) // -integer
|
||||
return
|
||||
}
|
||||
nadj = ln(PI / abs(t*x))
|
||||
if t < 0 {
|
||||
sign = -1
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case x == 1 || x == 2: // purge off 1 and 2
|
||||
lgamma = 0
|
||||
return
|
||||
case x < 2: // use lgamma(x) = lgamma(x+1) - log(x)
|
||||
y: f64
|
||||
i: int
|
||||
if x <= 0.9 {
|
||||
lgamma = -ln(x)
|
||||
switch {
|
||||
case x >= (Y_MIN - 1 + 0.27): // 0.7316 <= x <= 0.9
|
||||
y = 1 - x
|
||||
i = 0
|
||||
case x >= (Y_MIN - 1 - 0.27): // 0.2316 <= x < 0.7316
|
||||
y = x - (Tc - 1)
|
||||
i = 1
|
||||
case: // 0 < x < 0.2316
|
||||
y = x
|
||||
i = 2
|
||||
}
|
||||
} else {
|
||||
lgamma = 0
|
||||
switch {
|
||||
case x >= (Y_MIN + 0.27): // 1.7316 <= x < 2
|
||||
y = 2 - x
|
||||
i = 0
|
||||
case x >= (Y_MIN - 0.27): // 1.2316 <= x < 1.7316
|
||||
y = x - Tc
|
||||
i = 1
|
||||
case: // 0.9 < x < 1.2316
|
||||
y = x - 1
|
||||
i = 2
|
||||
}
|
||||
}
|
||||
switch i {
|
||||
case 0:
|
||||
z := y * y
|
||||
p1 := lgamA[0] + z*(lgamA[2]+z*(lgamA[4]+z*(lgamA[6]+z*(lgamA[8]+z*lgamA[10]))))
|
||||
p2 := z * (lgamA[1] + z*(+lgamA[3]+z*(lgamA[5]+z*(lgamA[7]+z*(lgamA[9]+z*lgamA[11])))))
|
||||
p := y*p1 + p2
|
||||
lgamma += (p - 0.5*y)
|
||||
case 1:
|
||||
z := y * y
|
||||
w := z * y
|
||||
p1 := lgamT[0] + w*(lgamT[3]+w*(lgamT[6]+w*(lgamT[9]+w*lgamT[12]))) // parallel comp
|
||||
p2 := lgamT[1] + w*(lgamT[4]+w*(lgamT[7]+w*(lgamT[10]+w*lgamT[13])))
|
||||
p3 := lgamT[2] + w*(lgamT[5]+w*(lgamT[8]+w*(lgamT[11]+w*lgamT[14])))
|
||||
p := z*p1 - (Tt - w*(p2+y*p3))
|
||||
lgamma += (Tf + p)
|
||||
case 2:
|
||||
p1 := y * (lgamU[0] + y*(lgamU[1]+y*(lgamU[2]+y*(lgamU[3]+y*(lgamU[4]+y*lgamU[5])))))
|
||||
p2 := 1 + y*(lgamV[1]+y*(lgamV[2]+y*(lgamV[3]+y*(lgamV[4]+y*lgamV[5]))))
|
||||
lgamma += (-0.5*y + p1/p2)
|
||||
}
|
||||
case x < 8: // 2 <= x < 8
|
||||
i := int(x)
|
||||
y := x - f64(i)
|
||||
p := y * (lgamS[0] + y*(lgamS[1]+y*(lgamS[2]+y*(lgamS[3]+y*(lgamS[4]+y*(lgamS[5]+y*lgamS[6]))))))
|
||||
q := 1 + y*(lgamR[1]+y*(lgamR[2]+y*(lgamR[3]+y*(lgamR[4]+y*(lgamR[5]+y*lgamR[6])))))
|
||||
lgamma = 0.5*y + p/q
|
||||
z := 1.0 // lgamma(1+s) = ln(s) + lgamma(s)
|
||||
switch i {
|
||||
case 7:
|
||||
z *= (y + 6)
|
||||
fallthrough
|
||||
case 6:
|
||||
z *= (y + 5)
|
||||
fallthrough
|
||||
case 5:
|
||||
z *= (y + 4)
|
||||
fallthrough
|
||||
case 4:
|
||||
z *= (y + 3)
|
||||
fallthrough
|
||||
case 3:
|
||||
z *= (y + 2)
|
||||
lgamma += ln(z)
|
||||
}
|
||||
case x < TWO_58: // 8 <= x < 2**58
|
||||
t := ln(x)
|
||||
z := 1 / x
|
||||
y := z * z
|
||||
w := lgamW[0] + z*(lgamW[1]+y*(lgamW[2]+y*(lgamW[3]+y*(lgamW[4]+y*(lgamW[5]+y*lgamW[6])))))
|
||||
lgamma = (x-0.5)*(t-1) + w
|
||||
case: // 2**58 <= x <= Inf
|
||||
lgamma = x * (ln(x) - 1)
|
||||
}
|
||||
if neg {
|
||||
lgamma = nadj - lgamma
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
lgamma_f16 :: proc "contextless" (x: f16) -> (lgamma: f16, sign: int) { r, s := lgamma_f64(f64(x)); return f16(r), s }
|
||||
lgamma_f32 :: proc "contextless" (x: f32) -> (lgamma: f32, sign: int) { r, s := lgamma_f64(f64(x)); return f32(r), s }
|
||||
lgamma_f16le :: proc "contextless" (x: f16le) -> (lgamma: f16le, sign: int) { r, s := lgamma_f64(f64(x)); return f16le(r), s }
|
||||
lgamma_f16be :: proc "contextless" (x: f16be) -> (lgamma: f16be, sign: int) { r, s := lgamma_f64(f64(x)); return f16be(r), s }
|
||||
lgamma_f32le :: proc "contextless" (x: f32le) -> (lgamma: f32le, sign: int) { r, s := lgamma_f64(f64(x)); return f32le(r), s }
|
||||
lgamma_f32be :: proc "contextless" (x: f32be) -> (lgamma: f32be, sign: int) { r, s := lgamma_f64(f64(x)); return f32be(r), s }
|
||||
lgamma_f64le :: proc "contextless" (x: f64le) -> (lgamma: f64le, sign: int) { r, s := lgamma_f64(f64(x)); return f64le(r), s }
|
||||
lgamma_f64be :: proc "contextless" (x: f64be) -> (lgamma: f64be, sign: int) { r, s := lgamma_f64(f64(x)); return f64be(r), s }
|
||||
|
||||
lgamma :: proc{
|
||||
lgamma_f16, lgamma_f16le, lgamma_f16be,
|
||||
lgamma_f32, lgamma_f32le, lgamma_f32be,
|
||||
lgamma_f64, lgamma_f64le, lgamma_f64be,
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
package math
|
||||
|
||||
// The original C code, the long comment, and the constants
|
||||
// below are from FreeBSD's /usr/src/lib/msun/src/s_log1p.c
|
||||
// and came with this notice. The go code is a simplified
|
||||
// version of the original C.
|
||||
//
|
||||
// ====================================================
|
||||
// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
//
|
||||
// Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
// Permission to use, copy, modify, and distribute this
|
||||
// software is freely granted, provided that this notice
|
||||
// is preserved.
|
||||
// ====================================================
|
||||
//
|
||||
//
|
||||
// double log1p(double x)
|
||||
//
|
||||
// Method :
|
||||
// 1. Argument Reduction: find k and f such that
|
||||
// 1+x = 2**k * (1+f),
|
||||
// where sqrt(2)/2 < 1+f < sqrt(2) .
|
||||
//
|
||||
// Note. If k=0, then f=x is exact. However, if k!=0, then f
|
||||
// may not be representable exactly. In that case, a correction
|
||||
// term is need. Let u=1+x rounded. Let c = (1+x)-u, then
|
||||
// log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u),
|
||||
// and add back the correction term c/u.
|
||||
// (Note: when x > 2**53, one can simply return log(x))
|
||||
//
|
||||
// 2. Approximation of log1p(f).
|
||||
// Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
|
||||
// = 2s + 2/3 s**3 + 2/5 s**5 + .....,
|
||||
// = 2s + s*R
|
||||
// We use a special Reme algorithm on [0,0.1716] to generate
|
||||
// a polynomial of degree 14 to approximate R The maximum error
|
||||
// of this polynomial approximation is bounded by 2**-58.45. In
|
||||
// other words,
|
||||
// 2 4 6 8 10 12 14
|
||||
// R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s +Lp6*s +Lp7*s
|
||||
// (the values of Lp1 to Lp7 are listed in the program)
|
||||
// and
|
||||
// | 2 14 | -58.45
|
||||
// | Lp1*s +...+Lp7*s - R(z) | <= 2
|
||||
// | |
|
||||
// Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
|
||||
// In order to guarantee error in log below 1ulp, we compute log
|
||||
// by
|
||||
// log1p(f) = f - (hfsq - s*(hfsq+R)).
|
||||
//
|
||||
// 3. Finally, log1p(x) = k*ln2 + log1p(f).
|
||||
// = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
|
||||
// Here ln2 is split into two floating point number:
|
||||
// ln2_hi + ln2_lo,
|
||||
// where n*ln2_hi is always exact for |n| < 2000.
|
||||
//
|
||||
// Special cases:
|
||||
// log1p(x) is NaN with signal if x < -1 (including -INF) ;
|
||||
// log1p(+INF) is +INF; log1p(-1) is -INF with signal;
|
||||
// log1p(NaN) is that NaN with no signal.
|
||||
//
|
||||
// Accuracy:
|
||||
// according to an error analysis, the error is always less than
|
||||
// 1 ulp (unit in the last place).
|
||||
//
|
||||
// Constants:
|
||||
// The hexadecimal values are the intended ones for the following
|
||||
// constants. The decimal values may be used, provided that the
|
||||
// compiler will convert from decimal to binary accurately enough
|
||||
// to produce the hexadecimal values shown.
|
||||
//
|
||||
// Note: Assuming log() return accurate answer, the following
|
||||
// algorithm can be used to compute log1p(x) to within a few ULP:
|
||||
//
|
||||
// u = 1+x;
|
||||
// if(u==1.0) return x ; else
|
||||
// return log(u)*(x/(u-1.0));
|
||||
//
|
||||
// See HP-15C Advanced Functions Handbook, p.193.
|
||||
|
||||
log1p :: proc {
|
||||
log1p_f16,
|
||||
log1p_f32,
|
||||
log1p_f64,
|
||||
log1p_f16le,
|
||||
log1p_f16be,
|
||||
log1p_f32le,
|
||||
log1p_f32be,
|
||||
log1p_f64le,
|
||||
log1p_f64be,
|
||||
}
|
||||
log1p_f16 :: proc "contextless" (x: f16) -> f16 { return f16(log1p_f64(f64(x))) }
|
||||
log1p_f32 :: proc "contextless" (x: f32) -> f32 { return f32(log1p_f64(f64(x))) }
|
||||
log1p_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log1p_f64(f64(x))) }
|
||||
log1p_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log1p_f64(f64(x))) }
|
||||
log1p_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log1p_f64(f64(x))) }
|
||||
log1p_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log1p_f64(f64(x))) }
|
||||
log1p_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log1p_f64(f64(x))) }
|
||||
log1p_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log1p_f64(f64(x))) }
|
||||
|
||||
log1p_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
SQRT2_M1 :: 0h3fda827999fcef34 // sqrt(2)-1
|
||||
SQRT2_HALF_M1 :: 0hbfd2bec333018866 // sqrt(2)/2-1
|
||||
SMALL :: 0h3e20000000000000 // 2**-29
|
||||
TINY :: 0h3c90000000000000 // 2**-54
|
||||
TWO53 :: 0h4340000000000000 // 2**53
|
||||
LN2HI :: 0h3fe62e42fee00000
|
||||
LN2LO :: 0h3dea39ef35793c76
|
||||
LP1 :: 0h3FE5555555555593
|
||||
LP2 :: 0h3FD999999997FA04
|
||||
LP3 :: 0h3FD2492494229359
|
||||
LP4 :: 0h3FCC71C51D8E78AF
|
||||
LP5 :: 0h3FC7466496CB03DE
|
||||
LP6 :: 0h3FC39A09D078C69F
|
||||
LP7 :: 0h3FC2F112DF3E5244
|
||||
|
||||
switch {
|
||||
case x < -1 || is_nan(x):
|
||||
return nan_f64()
|
||||
case x == -1:
|
||||
return inf_f64(-1)
|
||||
case is_inf(x, 1):
|
||||
return inf_f64(+1)
|
||||
}
|
||||
absx := abs(x)
|
||||
|
||||
f: f64
|
||||
iu: u64
|
||||
k := 1
|
||||
if absx < SQRT2_M1 { // |x| < sqrt(2)-1
|
||||
if absx < SMALL { // |x| < 2**-29
|
||||
if absx < TINY { // |x| < 2**-54
|
||||
return x
|
||||
}
|
||||
return x - x*x*0.5
|
||||
}
|
||||
if x > SQRT2_HALF_M1 { // sqrt(2)/2-1 < x
|
||||
// (sqrt(2)/2-1) < x < (sqrt(2)-1)
|
||||
k = 0
|
||||
f = x
|
||||
iu = 1
|
||||
}
|
||||
}
|
||||
c: f64
|
||||
if k != 0 {
|
||||
u: f64
|
||||
if absx < TWO53 { // 1<<53
|
||||
u = 1.0 + x
|
||||
iu = transmute(u64)u
|
||||
k = int((iu >> 52) - 1023)
|
||||
// correction term
|
||||
if k > 0 {
|
||||
c = 1.0 - (u - x)
|
||||
} else {
|
||||
c = x - (u - 1.0)
|
||||
}
|
||||
c /= u
|
||||
} else {
|
||||
u = x
|
||||
iu = transmute(u64)u
|
||||
k = int((iu >> 52) - 1023)
|
||||
c = 0
|
||||
}
|
||||
iu &= 0x000fffffffffffff
|
||||
if iu < 0x0006a09e667f3bcd { // mantissa of sqrt(2)
|
||||
u = transmute(f64)(iu | 0x3ff0000000000000) // normalize u
|
||||
} else {
|
||||
k += 1
|
||||
u = transmute(f64)(iu | 0x3fe0000000000000) // normalize u/2
|
||||
iu = (0x0010000000000000 - iu) >> 2
|
||||
}
|
||||
f = u - 1.0 // sqrt(2)/2 < u < sqrt(2)
|
||||
}
|
||||
hfsq := 0.5 * f * f
|
||||
s, R, z: f64
|
||||
if iu == 0 { // |f| < 2**-20
|
||||
if f == 0 {
|
||||
if k == 0 {
|
||||
return 0
|
||||
}
|
||||
c += f64(k) * LN2LO
|
||||
return f64(k)*LN2HI + c
|
||||
}
|
||||
R = hfsq * (1.0 - 0.66666666666666666*f) // avoid division
|
||||
if k == 0 {
|
||||
return f - R
|
||||
}
|
||||
return f64(k)*LN2HI - ((R - (f64(k)*LN2LO + c)) - f)
|
||||
}
|
||||
s = f / (2.0 + f)
|
||||
z = s * s
|
||||
R = z * (LP1 + z*(LP2+z*(LP3+z*(LP4+z*(LP5+z*(LP6+z*LP7))))))
|
||||
if k == 0 {
|
||||
return f - (hfsq - s*(hfsq+R))
|
||||
}
|
||||
return f64(k)*LN2HI - ((hfsq - (s*(hfsq+R) + (f64(k)*LN2LO + c))) - f)
|
||||
}
|
||||
@@ -4,64 +4,56 @@ package mem_virtual
|
||||
|
||||
import "core:c"
|
||||
import "core:intrinsics"
|
||||
import "core:sys/unix"
|
||||
|
||||
when ODIN_ARCH == "amd64" {
|
||||
SYS_mmap :: 9
|
||||
SYS_mprotect :: 10
|
||||
SYS_munmap :: 11
|
||||
SYS_madvise :: 28
|
||||
|
||||
PROT_NONE :: 0x0
|
||||
PROT_READ :: 0x1
|
||||
PROT_WRITE :: 0x2
|
||||
PROT_EXEC :: 0x4
|
||||
PROT_GROWSDOWN :: 0x01000000
|
||||
PROT_GROWSUP :: 0x02000000
|
||||
PROT_NONE :: 0x0
|
||||
PROT_READ :: 0x1
|
||||
PROT_WRITE :: 0x2
|
||||
PROT_EXEC :: 0x4
|
||||
PROT_GROWSDOWN :: 0x01000000
|
||||
PROT_GROWSUP :: 0x02000000
|
||||
|
||||
MAP_FIXED :: 0x1
|
||||
MAP_PRIVATE :: 0x2
|
||||
MAP_SHARED :: 0x4
|
||||
MAP_ANONYMOUS :: 0x20
|
||||
|
||||
MADV_NORMAL :: 0
|
||||
MADV_RANDOM :: 1
|
||||
MADV_SEQUENTIAL :: 2
|
||||
MADV_WILLNEED :: 3
|
||||
MADV_DONTNEED :: 4
|
||||
MADV_FREE :: 8
|
||||
MADV_REMOVE :: 9
|
||||
MADV_DONTFORK :: 10
|
||||
MADV_DOFORK :: 11
|
||||
MADV_MERGEABLE :: 12
|
||||
MADV_UNMERGEABLE :: 13
|
||||
MADV_HUGEPAGE :: 14
|
||||
MADV_NOHUGEPAGE :: 15
|
||||
MADV_DONTDUMP :: 16
|
||||
MADV_DODUMP :: 17
|
||||
MADV_WIPEONFORK :: 18
|
||||
MADV_KEEPONFORK :: 19
|
||||
MADV_HWPOISON :: 100
|
||||
} else {
|
||||
#panic("Unsupported architecture")
|
||||
}
|
||||
MAP_FIXED :: 0x1
|
||||
MAP_PRIVATE :: 0x2
|
||||
MAP_SHARED :: 0x4
|
||||
MAP_ANONYMOUS :: 0x20
|
||||
|
||||
MADV_NORMAL :: 0
|
||||
MADV_RANDOM :: 1
|
||||
MADV_SEQUENTIAL :: 2
|
||||
MADV_WILLNEED :: 3
|
||||
MADV_DONTNEED :: 4
|
||||
MADV_FREE :: 8
|
||||
MADV_REMOVE :: 9
|
||||
MADV_DONTFORK :: 10
|
||||
MADV_DOFORK :: 11
|
||||
MADV_MERGEABLE :: 12
|
||||
MADV_UNMERGEABLE :: 13
|
||||
MADV_HUGEPAGE :: 14
|
||||
MADV_NOHUGEPAGE :: 15
|
||||
MADV_DONTDUMP :: 16
|
||||
MADV_DODUMP :: 17
|
||||
MADV_WIPEONFORK :: 18
|
||||
MADV_KEEPONFORK :: 19
|
||||
MADV_HWPOISON :: 100
|
||||
|
||||
mmap :: proc "contextless" (addr: rawptr, length: uint, prot: c.int, flags: c.int, fd: c.int, offset: uintptr) -> rawptr {
|
||||
res := intrinsics.syscall(SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset)
|
||||
res := intrinsics.syscall(unix.SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset)
|
||||
return rawptr(res)
|
||||
}
|
||||
|
||||
munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int {
|
||||
res := intrinsics.syscall(SYS_munmap, uintptr(addr), uintptr(length))
|
||||
res := intrinsics.syscall(unix.SYS_munmap, uintptr(addr), uintptr(length))
|
||||
return c.int(res)
|
||||
}
|
||||
|
||||
mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: c.int) -> c.int {
|
||||
res := intrinsics.syscall(SYS_mprotect, uintptr(addr), uintptr(length), uint(prot))
|
||||
res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uint(prot))
|
||||
return c.int(res)
|
||||
}
|
||||
|
||||
madvise :: proc "contextless" (addr: rawptr, length: uint, advice: c.int) -> c.int {
|
||||
res := intrinsics.syscall(SYS_madvise, uintptr(addr), uintptr(length), uintptr(advice))
|
||||
res := intrinsics.syscall(unix.SYS_madvise, uintptr(addr), uintptr(length), uintptr(advice))
|
||||
return c.int(res)
|
||||
}
|
||||
|
||||
|
||||
@@ -579,6 +579,7 @@ Field_Flags_Signature :: Field_Flags{
|
||||
.No_Alias,
|
||||
.C_Vararg,
|
||||
.Auto_Cast,
|
||||
.Any_Int,
|
||||
.Default_Parameters,
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ String :: distinct Array(byte)
|
||||
|
||||
Version_Type_Major :: 0
|
||||
Version_Type_Minor :: 2
|
||||
Version_Type_Patch :: 0
|
||||
Version_Type_Patch :: 1
|
||||
|
||||
Version_Type :: struct {
|
||||
major, minor, patch: u8,
|
||||
|
||||
@@ -2825,6 +2825,7 @@ is_literal_type :: proc(expr: ^ast.Expr) -> bool {
|
||||
ast.Dynamic_Array_Type,
|
||||
ast.Map_Type,
|
||||
ast.Bit_Set_Type,
|
||||
ast.Matrix_Type,
|
||||
ast.Call_Expr:
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package os
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:intrinsics"
|
||||
import "core:unicode/utf16"
|
||||
|
||||
is_path_separator :: proc(c: byte) -> bool {
|
||||
return c == '/' || c == '\\'
|
||||
@@ -96,26 +97,78 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
return int(total_write), ERROR_NONE
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
|
||||
if len(b) == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
BUF_SIZE :: 386
|
||||
buf16: [BUF_SIZE]u16
|
||||
buf8: [4*BUF_SIZE]u8
|
||||
|
||||
for n < len(b) && err == 0 {
|
||||
max_read := u32(min(BUF_SIZE, len(b)/4))
|
||||
|
||||
single_read_length: u32
|
||||
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
|
||||
if !ok {
|
||||
err = Errno(win32.GetLastError())
|
||||
}
|
||||
|
||||
buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
|
||||
src := buf8[:buf8_len]
|
||||
|
||||
ctrl_z := false
|
||||
for i := 0; i < len(src) && n+i < len(b); i += 1 {
|
||||
x := src[i]
|
||||
if x == 0x1a { // ctrl-z
|
||||
ctrl_z = true
|
||||
break
|
||||
}
|
||||
b[n] = x
|
||||
n += 1
|
||||
}
|
||||
if ctrl_z || single_read_length < len(buf16) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE
|
||||
}
|
||||
|
||||
handle := win32.HANDLE(fd)
|
||||
|
||||
m: u32
|
||||
is_console := win32.GetConsoleMode(handle, &m)
|
||||
|
||||
single_read_length: win32.DWORD
|
||||
total_read: i64
|
||||
length := i64(len(data))
|
||||
total_read: int
|
||||
length := len(data)
|
||||
|
||||
for total_read < length {
|
||||
remaining := length - total_read
|
||||
to_read := min(win32.DWORD(remaining), MAX_RW)
|
||||
to_read := min(win32.DWORD(length), MAX_RW)
|
||||
|
||||
e := win32.ReadFile(win32.HANDLE(fd), &data[total_read], to_read, &single_read_length, nil)
|
||||
if single_read_length <= 0 || !e {
|
||||
err := Errno(win32.GetLastError())
|
||||
e: win32.BOOL
|
||||
if is_console {
|
||||
n, err := read_console(handle, data[total_read:][:to_read])
|
||||
total_read += n
|
||||
if err != 0 {
|
||||
return int(total_read), err
|
||||
}
|
||||
total_read += i64(single_read_length)
|
||||
} else {
|
||||
e = win32.ReadFile(handle, &data[total_read], to_read, &single_read_length, nil)
|
||||
}
|
||||
if single_read_length <= 0 || !e {
|
||||
err := Errno(win32.GetLastError())
|
||||
return int(total_read), err
|
||||
}
|
||||
total_read += int(single_read_length)
|
||||
|
||||
return int(total_read), ERROR_NONE
|
||||
}
|
||||
|
||||
@@ -172,6 +225,8 @@ pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
||||
Offset = u32(offset),
|
||||
}
|
||||
|
||||
// TODO(bill): Determine the correct behaviour for consoles
|
||||
|
||||
h := win32.HANDLE(fd)
|
||||
done: win32.DWORD
|
||||
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
|
||||
+37
-18
@@ -55,6 +55,25 @@ write_encoded_rune :: proc(fd: Handle, r: rune) {
|
||||
write_byte(fd, '\'')
|
||||
}
|
||||
|
||||
read_at_least :: proc(fd: Handle, buf: []byte, min: int) -> (n: int, err: Errno) {
|
||||
if len(buf) < min {
|
||||
return 0, -1
|
||||
}
|
||||
for n < min && err == 0 {
|
||||
nn: int
|
||||
nn, err = read(fd, buf[n:])
|
||||
n += nn
|
||||
}
|
||||
if n >= min {
|
||||
err = 0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
read_full :: proc(fd: Handle, buf: []byte) -> (n: int, err: Errno) {
|
||||
return read_at_least(fd, buf, len(buf))
|
||||
}
|
||||
|
||||
|
||||
file_size_from_path :: proc(path: string) -> i64 {
|
||||
fd, err := open(path, O_RDONLY, 0)
|
||||
@@ -85,27 +104,27 @@ read_entire_file_from_filename :: proc(name: string, allocator := context.alloca
|
||||
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator) -> (data: []byte, success: bool) {
|
||||
context.allocator = allocator
|
||||
|
||||
length: i64
|
||||
err: Errno
|
||||
if length, err = file_size(fd); err != 0 {
|
||||
return nil, false
|
||||
}
|
||||
length: i64
|
||||
err: Errno
|
||||
if length, err = file_size(fd); err != 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if length <= 0 {
|
||||
return nil, true
|
||||
}
|
||||
if length <= 0 {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
data = make([]byte, int(length), allocator)
|
||||
if data == nil {
|
||||
return nil, false
|
||||
}
|
||||
data = make([]byte, int(length), allocator)
|
||||
if data == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
bytes_read, read_err := read(fd, data)
|
||||
if read_err != ERROR_NONE {
|
||||
delete(data)
|
||||
return nil, false
|
||||
}
|
||||
return data[:bytes_read], true
|
||||
bytes_read, read_err := read_full(fd, data)
|
||||
if read_err != ERROR_NONE {
|
||||
delete(data)
|
||||
return nil, false
|
||||
}
|
||||
return data[:bytes_read], true
|
||||
}
|
||||
|
||||
read_entire_file :: proc {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package os2
|
||||
|
||||
import "core:mem"
|
||||
import "core:io"
|
||||
import "core:strconv"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:runtime"
|
||||
import "core:mem"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
//+freestanding
|
||||
package os
|
||||
|
||||
#panic("package os does not support a freestanding target")
|
||||
@@ -0,0 +1,4 @@
|
||||
//+js
|
||||
package os
|
||||
|
||||
#panic("package os does not support a js target")
|
||||
@@ -8,6 +8,7 @@ import "core:strings"
|
||||
import "core:c"
|
||||
import "core:strconv"
|
||||
import "core:intrinsics"
|
||||
import "core:sys/unix"
|
||||
|
||||
Handle :: distinct i32
|
||||
File_Time :: distinct u64
|
||||
@@ -265,8 +266,6 @@ X_OK :: 1 // Test for execute permission
|
||||
W_OK :: 2 // Test for write permission
|
||||
R_OK :: 4 // Test for read permission
|
||||
|
||||
SYS_GETTID :: 186
|
||||
|
||||
foreign libc {
|
||||
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---
|
||||
|
||||
@@ -594,7 +593,7 @@ exit :: proc "contextless" (code: int) -> ! {
|
||||
}
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return cast(int)intrinsics.syscall(SYS_GETTID)
|
||||
return unix.sys_gettid()
|
||||
}
|
||||
|
||||
dlopen :: proc(filename: string, flags: int) -> rawptr {
|
||||
|
||||
@@ -115,12 +115,16 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
|
||||
}
|
||||
buf = buf[:N]
|
||||
|
||||
if len(buf) >= 4 {
|
||||
if buf[0] == '\\' &&
|
||||
buf[1] == '\\' &&
|
||||
buf[2] == '?' &&
|
||||
buf[3] == '\\' {
|
||||
buf = buf[4:]
|
||||
if len(buf) >= 4 && buf[0] == '\\' && buf[1] == '\\' && buf[2] == '?' && buf[3] == '\\' {
|
||||
buf = buf[4:]
|
||||
|
||||
/*
|
||||
NOTE(Jeroen): Properly handle UNC paths.
|
||||
We need to turn `\\?\UNC\synology.local` into `\\synology.local`.
|
||||
*/
|
||||
if len(buf) >= 3 && buf[0] == 'U' && buf[1] == 'N' && buf[2] == 'C' {
|
||||
buf = buf[2:]
|
||||
buf[0] = '\\'
|
||||
}
|
||||
}
|
||||
return buf
|
||||
|
||||
+1
-1
@@ -150,7 +150,7 @@ join :: proc(elems: ..string, allocator := context.allocator) -> string {
|
||||
context.allocator = allocator
|
||||
for elem, i in elems {
|
||||
if elem != "" {
|
||||
s := strings.join(elems[i:], "/")
|
||||
s := strings.join(elems[i:], "/", context.temp_allocator)
|
||||
return clean(s)
|
||||
}
|
||||
}
|
||||
|
||||
+16
-11
@@ -506,22 +506,27 @@ __init_context :: proc "contextless" (c: ^Context) {
|
||||
|
||||
c.temp_allocator.procedure = default_temp_allocator_proc
|
||||
c.temp_allocator.data = &global_default_temp_allocator_data
|
||||
|
||||
c.assertion_failure_proc = default_assertion_failure_proc
|
||||
|
||||
when !ODIN_DISABLE_ASSERT {
|
||||
c.assertion_failure_proc = default_assertion_failure_proc
|
||||
}
|
||||
|
||||
c.logger.procedure = default_logger_proc
|
||||
c.logger.data = nil
|
||||
}
|
||||
|
||||
default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {
|
||||
print_caller_location(loc)
|
||||
print_string(" ")
|
||||
print_string(prefix)
|
||||
if len(message) > 0 {
|
||||
print_string(": ")
|
||||
print_string(message)
|
||||
when ODIN_OS == "freestanding" {
|
||||
// Do nothing
|
||||
} else {
|
||||
print_caller_location(loc)
|
||||
print_string(" ")
|
||||
print_string(prefix)
|
||||
if len(message) > 0 {
|
||||
print_string(": ")
|
||||
print_string(message)
|
||||
}
|
||||
print_byte('\n')
|
||||
}
|
||||
print_byte('\n')
|
||||
// intrinsics.debug_trap();
|
||||
intrinsics.trap()
|
||||
trap()
|
||||
}
|
||||
|
||||
@@ -38,12 +38,12 @@ inverse :: proc{
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
hermitian_adjoint :: proc(m: $M/matrix[$N, N]$T) -> M where intrinsics.type_is_complex(T), N >= 1 {
|
||||
hermitian_adjoint :: proc "contextless" (m: $M/matrix[$N, N]$T) -> M where intrinsics.type_is_complex(T), N >= 1 {
|
||||
return conj(transpose(m))
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix_trace :: proc(m: $M/matrix[$N, N]$T) -> (trace: T) {
|
||||
matrix_trace :: proc "contextless" (m: $M/matrix[$N, N]$T) -> (trace: T) {
|
||||
for i in 0..<N {
|
||||
trace += m[i, i]
|
||||
}
|
||||
@@ -51,7 +51,7 @@ matrix_trace :: proc(m: $M/matrix[$N, N]$T) -> (trace: T) {
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix_minor :: proc(m: $M/matrix[$N, N]$T, row, column: int) -> (minor: T) where N > 1 {
|
||||
matrix_minor :: proc "contextless" (m: $M/matrix[$N, N]$T, row, column: int) -> (minor: T) where N > 1 {
|
||||
K :: N-1
|
||||
cut_down: matrix[K, K]T
|
||||
for col_idx in 0..<K {
|
||||
@@ -67,23 +67,23 @@ matrix_minor :: proc(m: $M/matrix[$N, N]$T, row, column: int) -> (minor: T) wher
|
||||
|
||||
|
||||
@(builtin)
|
||||
matrix1x1_determinant :: proc(m: $M/matrix[1, 1]$T) -> (det: T) {
|
||||
matrix1x1_determinant :: proc "contextless" (m: $M/matrix[1, 1]$T) -> (det: T) {
|
||||
return m[0, 0]
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix2x2_determinant :: proc(m: $M/matrix[2, 2]$T) -> (det: T) {
|
||||
matrix2x2_determinant :: proc "contextless" (m: $M/matrix[2, 2]$T) -> (det: T) {
|
||||
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
|
||||
}
|
||||
@(builtin)
|
||||
matrix3x3_determinant :: proc(m: $M/matrix[3, 3]$T) -> (det: T) {
|
||||
matrix3x3_determinant :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) {
|
||||
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
|
||||
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
|
||||
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
|
||||
return a + b + c
|
||||
}
|
||||
@(builtin)
|
||||
matrix4x4_determinant :: proc(m: $M/matrix[4, 4]$T) -> (det: T) {
|
||||
matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
|
||||
a := adjugate(m)
|
||||
#no_bounds_check for i in 0..<4 {
|
||||
det += m[0, i] * a[0, i]
|
||||
@@ -95,13 +95,13 @@ matrix4x4_determinant :: proc(m: $M/matrix[4, 4]$T) -> (det: T) {
|
||||
|
||||
|
||||
@(builtin)
|
||||
matrix1x1_adjugate :: proc(x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
matrix1x1_adjugate :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
y = x
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix2x2_adjugate :: proc(x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
y[0, 0] = +x[1, 1]
|
||||
y[0, 1] = -x[1, 0]
|
||||
y[1, 0] = -x[0, 1]
|
||||
@@ -110,7 +110,7 @@ matrix2x2_adjugate :: proc(x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix3x3_adjugate :: proc(m: $M/matrix[3, 3]$T) -> (y: M) {
|
||||
matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
|
||||
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
|
||||
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
|
||||
y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
|
||||
@@ -125,7 +125,7 @@ matrix3x3_adjugate :: proc(m: $M/matrix[3, 3]$T) -> (y: M) {
|
||||
|
||||
|
||||
@(builtin)
|
||||
matrix4x4_adjugate :: proc(x: $M/matrix[4, 4]$T) -> (y: M) {
|
||||
matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
sign: T = 1 if (i + j) % 2 == 0 else -1
|
||||
@@ -136,13 +136,13 @@ matrix4x4_adjugate :: proc(x: $M/matrix[4, 4]$T) -> (y: M) {
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix1x1_inverse_transpose :: proc(x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
matrix1x1_inverse_transpose :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
y[0, 0] = 1/x[0, 0]
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix2x2_inverse_transpose :: proc(x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
|
||||
when intrinsics.type_is_integer(T) {
|
||||
y[0, 0] = +x[1, 1] / d
|
||||
@@ -160,7 +160,7 @@ matrix2x2_inverse_transpose :: proc(x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix3x3_inverse_transpose :: proc(x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
|
||||
matrix3x3_inverse_transpose :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
|
||||
a := adjugate(x)
|
||||
d := determinant(x)
|
||||
when intrinsics.type_is_integer(T) {
|
||||
@@ -181,7 +181,7 @@ matrix3x3_inverse_transpose :: proc(x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_c
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix4x4_inverse_transpose :: proc(x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
|
||||
matrix4x4_inverse_transpose :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
|
||||
a := adjugate(x)
|
||||
d: T
|
||||
for i in 0..<4 {
|
||||
@@ -205,13 +205,13 @@ matrix4x4_inverse_transpose :: proc(x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_c
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix1x1_inverse :: proc(x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
matrix1x1_inverse :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
y[0, 0] = 1/x[0, 0]
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix2x2_inverse :: proc(x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
|
||||
when intrinsics.type_is_integer(T) {
|
||||
y[0, 0] = x[1, 1] / d
|
||||
@@ -229,7 +229,7 @@ matrix2x2_inverse :: proc(x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix3x3_inverse :: proc(x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
|
||||
matrix3x3_inverse :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
|
||||
a := adjugate(x)
|
||||
d := determinant(x)
|
||||
when intrinsics.type_is_integer(T) {
|
||||
@@ -250,7 +250,7 @@ matrix3x3_inverse :: proc(x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix4x4_inverse :: proc(x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
|
||||
matrix4x4_inverse :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
|
||||
a := adjugate(x)
|
||||
d: T
|
||||
for i in 0..<4 {
|
||||
|
||||
@@ -1,23 +1,13 @@
|
||||
//+build !windows
|
||||
//+build !freestanding
|
||||
//+build !wasi
|
||||
//+build !js
|
||||
package runtime
|
||||
|
||||
when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
// mem.nil_allocator reimplementation
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
return nil, .None
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_allocator_proc,
|
||||
data = nil,
|
||||
}
|
||||
}
|
||||
default_allocator_proc :: nil_allocator_proc
|
||||
default_allocator :: nil_allocator
|
||||
} else {
|
||||
// TODO(bill): reimplement these procedures in the os_specific stuff
|
||||
import "core:os"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
//+build js
|
||||
package runtime
|
||||
|
||||
default_allocator_proc :: nil_allocator_proc
|
||||
default_allocator :: nil_allocator
|
||||
@@ -1,17 +1,38 @@
|
||||
//+build freestanding
|
||||
package runtime
|
||||
|
||||
// mem.nil_allocator reimplementation
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return nil, .Out_Of_Memory
|
||||
case .Free:
|
||||
return nil, .None
|
||||
case .Free_All:
|
||||
return nil, .Mode_Not_Implemented
|
||||
case .Resize:
|
||||
if size == 0 {
|
||||
return nil, .None
|
||||
}
|
||||
return nil, .Out_Of_Memory
|
||||
case .Query_Features:
|
||||
return nil, .Mode_Not_Implemented
|
||||
case .Query_Info:
|
||||
return nil, .Mode_Not_Implemented
|
||||
}
|
||||
return nil, .None
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
nil_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_allocator_proc,
|
||||
procedure = nil_allocator_proc,
|
||||
data = nil,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
when ODIN_OS == "freestanding" {
|
||||
default_allocator_proc :: nil_allocator_proc
|
||||
default_allocator :: nil_allocator
|
||||
}
|
||||
@@ -1,32 +1,5 @@
|
||||
//+build wasi
|
||||
package runtime
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return nil, .Out_Of_Memory
|
||||
case .Free:
|
||||
return nil, .None
|
||||
case .Free_All:
|
||||
return nil, .Mode_Not_Implemented
|
||||
case .Resize:
|
||||
if size == 0 {
|
||||
return nil, .None
|
||||
}
|
||||
return nil, .Out_Of_Memory
|
||||
case .Query_Features:
|
||||
return nil, .Mode_Not_Implemented
|
||||
case .Query_Info:
|
||||
return nil, .Mode_Not_Implemented
|
||||
}
|
||||
return nil, .None
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_allocator_proc,
|
||||
data = nil,
|
||||
}
|
||||
}
|
||||
default_allocator_proc :: nil_allocator_proc
|
||||
default_allocator :: nil_allocator
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user