Compare commits

...

204 Commits

Author SHA1 Message Date
gingerBill 9f0a30e36e Merge pull request #1337 from DanielGavin/parser-fix
Add Matrix_Type as literal type on "core:odin"
2021-11-28 10:38:37 +00:00
Daniel Gavin 517c8ff1dd Include Matrix_Type to the is_literal_type switch statement. 2021-11-28 02:14:25 +01:00
gingerBill 2b07afaf70 Add lb_build_addr on or_return and or_else for sanity's sake 2021-11-27 16:03:03 +00:00
gingerBill 6616882708 Correct reading from a console on Windows
e.g. `os.read(os.stdin, buf[:])`
2021-11-27 14:59:35 +00:00
gingerBill c9c197ba08 Add os.read_at_least and os_read_full utility procedures. 2021-11-27 14:57:49 +00:00
gingerBill 7876660d8c Add new utf16 procedures: decode, decode_to_utf8 2021-11-27 14:57:20 +00:00
gingerBill db9326f31d Merge pull request #1332 from odin-lang/nasm-support
NASM Support
2021-11-26 23:06:33 +00:00
gingerBill 27106dd9ae Allow .asm, .s, and .S as valid assembly file extensions 2021-11-26 22:25:07 +00:00
gingerBill 33dc12a61a Add supported check for .asm files 2021-11-26 14:46:03 +00:00
gingerBill ffd7ca57f1 Move nasm.exe to windows/nasm.exe, etc 2021-11-26 14:40:39 +00:00
gingerBill 44897b5eac Merge pull request #1334 from jockus/allow-enum-any-int
Allow enums to pass #any_int checks
2021-11-25 11:31:04 +00:00
Joakim Hentula 8255481204 Allow enums to pass #any_int checks 2021-11-25 11:20:40 +00:00
gingerBill 1e453cf1d7 Merge pull request #1296 from kevinsjoberg/do-not-filter-tests-when-empty
Do not filter test procedures when filter is empty
2021-11-25 09:13:31 +00:00
gingerBill c34a331696 Add -extra-assembler-flags 2021-11-24 22:20:18 +00:00
gingerBill 07ec93bfeb Add procs_windows_amd64.asm for use with -no-crt 2021-11-24 18:32:27 +00:00
gingerBill 994ee5a559 Allow for multiple .asm files 2021-11-24 17:57:31 +00:00
gingerBill 50057b0696 Add basic support for foreign import "foo.asm" on Windows with nasm.exe 2021-11-24 16:56:42 +00:00
gingerBill 00597127dd Add missing field skip_missing 2021-11-24 16:39:29 +00:00
gingerBill 70d4bc8573 Add nasm binaries 2021-11-24 16:36:34 +00:00
gingerBill bc775afccb Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-24 16:31:47 +00:00
gingerBill 504ea7deeb Fix update_untyped_expr_type for ternary if expressions with an untyped type 2021-11-24 16:31:37 +00:00
gingerBill 5e2280a787 Fix set_file_path_string and thread_safe_set_ast_file_from_id 2021-11-24 16:20:01 +00:00
gingerBill 84e03421d3 Merge pull request #1312 from DYSEQTA/master
Improve compiler help output with regard to command specific help.
2021-11-24 15:49:49 +00:00
DYSEQTA 0a87ffe0e6 Merge branch 'odin-lang:master' into master 2021-11-24 12:07:14 +11:00
DYSEQTA e5f961b48f Removed '--help' from help string as per request. 2021-11-24 11:10:40 +11:00
gingerBill 5db505c42f Merge pull request #1277 from Yawning/feature/modern-crypto
core/crypto: Add some "modern" primitives
2021-11-23 17:54:03 +00:00
gingerBill 275241f9b4 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-23 11:43:38 +00:00
gingerBill 9246e89c4a Fix #1328 2021-11-23 11:43:32 +00:00
gingerBill b56964e465 Merge pull request #1315 from SrMordred/patch-2
GetMouseDelta
2021-11-23 11:30:54 +00:00
gingerBill 2e89585c8c Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-23 10:59:50 +00:00
gingerBill e230b7110c Merge pull request #1327 from graphitemaster/fix-path-join-leak
fix memory leak in path.join
2021-11-22 15:34:30 +00:00
Dale Weiler a55f0cfb63 fix memory leak in path.join 2021-11-22 10:25:54 -05:00
gingerBill de435c9318 Remove unneeded semicolons from vendor:OpenGL 2021-11-21 14:52:40 +00:00
gingerBill f40f12d480 Minor cleanup to math constants 2021-11-21 14:06:32 +00:00
gingerBill 8a2c829e07 Patch odin doc binary format 2021-11-21 14:06:15 +00:00
gingerBill 42b9ce636f Remove #force_inline from all wrappers 2021-11-21 13:59:28 +00:00
gingerBill ca6951d05e Add MessageDecompose; Update the static library 2021-11-20 20:20:12 +00:00
gingerBill 446f1f6183 Correct foreign imports for portmidi on Windows 2021-11-20 19:27:34 +00:00
gingerBill d424c84bf9 Merge pull request #1322 from Gaunsessa/master
Add darwin support for glfw and re-add ln for js.
2021-11-20 12:22:38 +00:00
Gus 56d2bbc5b9 Added back ln for js 2021-11-20 20:03:54 +11:00
Gus 2c7bf87998 Added darwin support 2021-11-20 20:02:21 +11:00
gingerBill daebaa8b50 Fix #1319 2021-11-19 15:43:13 +00:00
gingerBill 9320a31f4d Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-19 12:26:19 +00:00
gingerBill 3e04b45106 Allow cast from float to complex 2021-11-19 12:26:10 +00:00
gingerBill acd5878d66 Merge pull request #1316 from Skytrias/master
add `builtin.` in slice.swap_between
2021-11-18 23:48:43 +00:00
Michael Kutowski 4439d59105 add builtin. 2021-11-19 00:24:56 +01:00
gingerBill 12c1291805 Add optional seed parameters to all hashes 2021-11-18 16:14:33 +00:00
Patric Dexheimer 61bc963e92 GetMouseDelta 2021-11-17 19:03:01 -03:00
gingerBill ae59f214ee @(tag=<string>) - dummy attribute for tooling 2021-11-17 21:32:33 +00:00
Yawning Angel 6bafa21bee crypto: Add rand_bytes
This adds `rand_bytes(dst: []byte)` which fills the destination buffer
with entropy from the cryptographic random number generator.  This takes
the "simple is best" approach and just directly returns the OS CSPRNG
output instead of doing anything fancy (a la OpenBSD's arc4random).
2021-11-17 14:00:00 +00:00
Yawning Angel 61c581baeb core/sys/unix: Add syscalls_linux.odin
Linux is in the unfortunate situation where the system call number is
architecture specific.  This consolidates the system call number
definitions in a single location, adds some wrappers, and hopefully
fixes the existing non-portable invocations of the syscall intrinsic.
2021-11-17 14:00:00 +00:00
Yawning Angel 6c4c9aef61 core/crypto: Add chacha20poly1305
This package implements the chacha20poly1305 AEAD construct as specified
in RFC 8439.
2021-11-17 13:59:53 +00:00
Yawning Angel 7bed317636 core/crypto: Add chacha20
This package implements the ChaCha20 stream cipher as specified in
RFC 8439, and the somewhat non-standard XChaCha20 variant that supports
a 192-bit nonce.

While an IETF draft for XChaCha20 standardization exists,
implementations that pre-date the draft use a 64-bit counter, instead of
the IETF-style 32-bit one.  This implementation opts for the latter as
compatibility with libsodium is more important than compatibility with
an expired IETF draft.
2021-11-17 13:59:53 +00:00
Yawning Angel 4647081f49 core/crypto/poly1305: Triple performance on amd64 with -o:speed 2021-11-17 13:59:53 +00:00
Yawning Angel 64db286582 core/crypto: Add poly1305
This package implements the Poly1305 MAC algorithm as specified in RFC
8439, using routines taked from fiat-crypto and poly1305-donna.
2021-11-17 13:59:53 +00:00
Yawning Angel 1a7a6a9116 core/crypto: Add x25519
This package implements the X25519 key agreement scheme as specified in
RFC 7748, using routines taken from fiat-crypto and Monocypher.
2021-11-17 13:59:53 +00:00
Yawning Angel d1e76ee4f2 core/crypto: Add constant-time memory comparison routines
Using a constant-time comparison is required when comparing things like
MACs, password digests, and etc to avoid exposing sensitive data via
trivial timing attacks.

These routines could also live under core:mem, but they are somewhat
specialized, and are likely only useful for cryptographic applications.
2021-11-17 13:59:53 +00:00
gingerBill 9be0d18e5d Correct x in ptr logic 2021-11-17 11:02:11 +00:00
gingerBill e877525073 Keep -vet happy for -no-crt and wasm targets 2021-11-17 10:40:55 +00:00
gingerBill f09638318f Add support for darwin to core:c/libc 2021-11-16 21:19:08 +00:00
gingerBill bb7703fcec Improve ptr_map_hash_key 2021-11-16 16:08:20 +00:00
gingerBill 1b28226a67 Add math.lgamma based off FreeBSD's /usr/src/lib/msun/src/e_lgamma_r.c 2021-11-16 15:32:32 +00:00
gingerBill 2b546a598c Add math.signbit; Add math.gamma based on http://netlib.sandia.gov/cephes/cprob/gamma.c 2021-11-16 15:23:19 +00:00
gingerBill b530ca9a5e Add math.nextafter 2021-11-16 15:12:01 +00:00
gingerBill d232796149 Fix typo 2021-11-16 15:09:47 +00:00
gingerBill e721f26a76 Implement ln based off FreeBSD's /usr/src/lib/msun/src/e_log.c 2021-11-16 15:05:04 +00:00
gingerBill 91408cb21f Implement atanh based on FreeBSD's /usr/src/lib/msun/src/e_atanh.c 2021-11-16 14:58:59 +00:00
gingerBill eb8b0d7a03 Add log1p, erf, erfc, ilogb logb (implemented based of FreeBSD's) 2021-11-16 14:54:57 +00:00
gingerBill 880af47ae7 Rename math_js.odin to math_basic_js.odin 2021-11-16 14:26:04 +00:00
gingerBill 91949b0992 Implement math.sqrt with intrinsics.sqrt 2021-11-16 14:11:20 +00:00
gingerBill 6a101e69a2 Implement ldexp and frexp in native Odin 2021-11-16 14:04:49 +00:00
cybermancer 1823b0cead Improve compiler help output with regard to command specific help. 2021-11-16 15:15:21 +11:00
gingerBill 1ec0b79345 Allow both -help and --help if passed as init_filename 2021-11-15 22:10:31 +00:00
gingerBill e814a3693f Improve usage of file_id 2021-11-15 17:26:01 +00:00
gingerBill f55fc4cd08 Add complex32 and quaternion64 for the 16-bit float types to fmt 2021-11-15 17:25:29 +00:00
gingerBill f47311f2f6 Remove scope field from Ast 2021-11-14 15:22:40 +00:00
gingerBill 3f038428a7 Begin minimizing Ast size 2021-11-14 15:12:37 +00:00
gingerBill b9701340b8 Add linalg.matrix4_look_at_from_fru 2021-11-13 19:15:37 +00:00
gingerBill 82110bf487 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-13 19:07:27 +00:00
gingerBill a75dc9d86d Fix minor issue with unmarshal for booleans 2021-11-13 19:07:16 +00:00
gingerBill bfa23f1352 Merge pull request #1308 from Yawning/fix/amd64-syscalls
src: Fix the syscall intrinsic code generation for Linux and Windows
2021-11-13 18:54:12 +00:00
Yawning Angel c430a82721 src: Fix the syscall intrinsic code generation for Linux and Windows
The old assembly generated for the syscall intrinsic did not specify
clobber constraints.  This adds RCX and R11 (that are clobbered by
the instruction itself), and memory (that is clobbered by some
system calls) to the assembly constraints.

Note: This is still incorrect on FreeBSD, which clobbers more registers
and uses the carry flag instead of -errno in rax to indicate an error.
2021-11-13 09:53:20 +00:00
Jeroen van Rijn cc316a473e Merge pull request #1299 from Kelimion/vendor-glfw-test
[vendor:glfw] Add test.
2021-11-10 19:24:55 +01:00
Jeroen van Rijn c213274607 [vendor:glfw] Add test. 2021-11-10 19:15:10 +01:00
Jeroen van Rijn c4a2580dfd Merge pull request #1290 from wjlroe/fix-glfw-on-windows
Fix path to static GLFW lib on Windows
2021-11-10 18:41:30 +01:00
Jeroen van Rijn 8a547b5922 Merge pull request #1298 from CarwynNelson/ws32-add-socket
Add socket() function to windows ws32 bindings
2021-11-10 17:23:59 +01:00
Carwyn Nelson c67c0789eb Add socket() function to windows ws32 bindings
It looks like this was missing from the winsock bindings. Odin contains
WSASocketW which I assume would also work for obtaining a socket, but
socket() is distinct and is what I was using, so I assume others will
want it too.
2021-11-10 15:55:50 +00:00
Jeroen van Rijn cefe312ba1 Merge pull request #1297 from CarwynNelson/patch-1
Fix the windows binding for getaddrinfo
2021-11-10 16:22:56 +01:00
Carwyn Nelson d8b1523161 Fix the windows binding for getaddrinfo
getaddrinfo should take a double pointer to ADDRINFOA instead of a single pointer. If you call the binding in its current state you will not get back a valid ADDRINFOA struct.

I have also changed the `node` and `service` params to be cstring to avoid having to do `transmute(u8) value`.
2021-11-10 15:15:40 +00:00
Kevin Sjöberg 61b02adc50 Do not filter test procedures when filter is empty
If `build_context.test_names` is empty, we do not need to perform any
filtering.
2021-11-10 15:49:23 +01:00
Jeroen van Rijn 989ddbd688 Merge pull request #1295 from zhibog/master
Add tests to Linux and Mac and add vendor tests
2021-11-10 15:34:29 +01:00
zhibog 96b670af49 Fix package name again 2021-11-10 15:31:29 +01:00
zhibog 359e02bad7 Fix botan lib name for apt 2021-11-10 15:26:26 +01:00
zhibog 8aadcacc0b Add tests to Linux and Mac and add vendor tests 2021-11-10 15:22:12 +01:00
Jeroen van Rijn 615efc7c86 Merge pull request #1294 from Kelimion/fix_dir_walk
Fix os.walk for UNC paths.
2021-11-10 15:09:22 +01:00
Jeroen van Rijn dd88104a81 Fix os.walk for UNC paths. 2021-11-10 14:59:54 +01:00
gingerBill 5cb23725ae Merge pull request #1289 from Kelimion/timings-export
Add functionality to export build timings.
2021-11-10 12:06:36 +00:00
Jeroen van Rijn 8c5c45a04c [timings-export] Style fixes. 2021-11-10 12:23:46 +01:00
gingerBill 4a552e6326 Merge pull request #1286 from DanielGavin/parser-fix
Add Any_Int as allowed flag in field signatures.
2021-11-10 10:59:53 +00:00
gingerBill 1f0758708f Merge pull request #1293 from kevinsjoberg/fix-test-filtering
Postpone checking test procedures
2021-11-10 10:52:01 +00:00
Kevin Sjöberg b8dec4268d Postpone checking test procedures
The dependency set need to be generated before we check the testing
procedures. Otherwise `checker->info.testing_procedures` will be empty
and thus no filtering is taking place.
2021-11-10 10:26:17 +01:00
gingerBill fc920a630f Merge pull request #1288 from odin-lang/target-js_wasm32
Target `js_wasm32` with `vendor:wasm/WebGL`
2021-11-09 23:15:42 +00:00
Jeroen van Rijn ffeac8895d Merge pull request #1291 from zhibog/master
Add Botan crypto lib as a vendor library
2021-11-09 23:59:14 +01:00
zhibog cef9632607 Add Botan crypto lib as a vendor library 2021-11-09 23:49:17 +01:00
gingerBill 76054dddb7 Revert build.bat 2021-11-09 22:11:18 +00:00
Jeroen van Rijn 9dc8753a14 [timings-export] Improve help messages
Also make `clang` happy as concerns the build settings switch/case.
2021-11-09 22:52:26 +01:00
William Roe a805d9a721 Fix path to static GLFW lib on Windows 2021-11-09 20:10:34 +00:00
Jeroen van Rijn 6c306f7633 Fix Linux warnings. 2021-11-09 20:31:22 +01:00
Jeroen van Rijn 05a86d5296 [timings-export] Implement JSON + CSV timngs export. 2021-11-09 19:57:55 +01:00
Jeroen van Rijn 9422fd311f [timings-export] Add -export-timings:format + -export-timings-file:filename. 2021-11-09 19:51:27 +01:00
gingerBill 80360f3f51 Add vendor packages for the js_wasm32 target 2021-11-09 18:26:42 +00:00
gingerBill 321d93bff1 Merge branch 'master' into target-js_wasm32 2021-11-09 18:06:19 +00:00
gingerBill 600d19c51b General catch-all for llvm debug types 2021-11-09 18:04:31 +00:00
gingerBill ed933bca19 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-09 16:36:01 +00:00
gingerBill a9ea590d24 Add dummy time_freestanding.odin 2021-11-09 16:35:50 +00:00
Jeroen van Rijn 275d39b59b Merge pull request #1287 from zhibog/master
Removed context switching system from the crypto library to simplify …
2021-11-09 16:57:59 +01:00
zhibog c24454ae70 Removed context switching system from the crypto library to simplify the code 2021-11-09 16:50:13 +01:00
DanielGavin fbc38c78eb Merge branch 'odin-lang:master' into parser-fix 2021-11-09 14:30:34 +01:00
Daniel Gavin b0db90de96 Add Any_Int as allowed flag in field signatures. 2021-11-09 14:29:53 +01:00
Jeroen van Rijn eb96f9677e Merge pull request #1285 from Kelimion/vet
[core:os/os2] Keep -vet happy.
2021-11-09 14:12:04 +01:00
Jeroen van Rijn 0a3b75c5f5 [core:os/os2] Keep -vet happy. 2021-11-09 14:06:14 +01:00
gingerBill 50562440bf Correct wasm-ld path for non-Windows platforms 2021-11-09 08:09:56 +00:00
gingerBill ce90c3c9ee Merge pull request #1284 from odin-lang/vendor-raylib-4.0
raylib 4.0
2021-11-09 08:05:48 +00:00
gingerBill d4bdcd55e1 Add Modified README.md 2021-11-08 16:25:51 +00:00
gingerBill 3f90faf0c9 Update vendor:raylib version 4.0 2021-11-08 15:57:55 +00:00
gingerBill 3d35c5ceb1 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-08 12:16:04 +00:00
gingerBill a674e842d0 Improve matrix indices to offset logic 2021-11-08 12:15:57 +00:00
gingerBill 23f0fbc376 Improve matrix->matrix casting implementation 2021-11-08 11:40:41 +00:00
gingerBill c63f4d68c8 Add math_js.odin specific calls (that just wrap the f64 procedures) 2021-11-07 20:06:05 +00:00
gingerBill 518460af66 Begin work in semi-standardized js_wasm32 target 2021-11-07 19:56:01 +00:00
gingerBill 39f652de47 Merge pull request #1280 from zhibog/master
Fix order of operations to make it correct and work with -o:speed flag
2021-11-07 18:08:33 +00:00
zhibog 483afe462b Fix order of operations to make it correct and work with -o:speed flaf 2021-11-07 18:53:30 +01:00
gingerBill 1296fabe2c Fix typos 2021-11-07 16:20:04 +00:00
gingerBill dc2edd3e79 Improve support for freestanding_wasm32 2021-11-07 16:19:27 +00:00
gingerBill e9c903f1ea Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-07 14:16:13 +00:00
gingerBill 83be954efd Minor spelling change 2021-11-07 14:16:05 +00:00
Jeroen van Rijn f84bdee1ba Merge pull request #1279 from DanielGavin/fix-json
Add json encoding test + fix enum not being set on success.
2021-11-07 14:47:35 +01:00
Daniel Gavin 5b074ceee5 Add json encoding test + fix enum not being set on success. 2021-11-07 14:35:52 +01:00
gingerBill 40eed29527 Remove LLVMAddDeadStoreEliminationPass pass 2021-11-06 18:11:29 +00:00
gingerBill 3d3785a7f1 Remove many LLVM optimization passes which were causes UB due to them assuming C-like behaviour incompatible with Odin 2021-11-06 17:23:33 +00:00
gingerBill 5df15b5724 Completely ignore LLVM_ADD_CONSTANT_VALUE_PASS LLVM >= 12 2021-11-06 16:29:53 +00:00
gingerBill ee259e4229 Merge pull request #1273 from odin-lang/compiler-map-improvements
Compiler Map Improvements
2021-11-05 18:12:40 +00:00
gingerBill 36985f8da0 Simplification to ptr_map_hash_key 2021-11-05 18:04:18 +00:00
gingerBill eb0faf9602 Unify hash logic for PtrSet 2021-11-05 17:58:11 +00:00
gingerBill 899cc71990 Improve ptr_map_hash_key 2021-11-05 17:55:09 +00:00
gingerBill 7be18b4a80 Be more correct with MapIndex usage 2021-11-05 17:36:00 +00:00
gingerBill 0c9bb9d920 Clean up logic 2021-11-05 17:32:17 +00:00
gingerBill 26e3daf5ad Unify MapFindResult types 2021-11-05 17:24:19 +00:00
gingerBill 0af69f8cda Remove map.cpp code 2021-11-05 17:16:37 +00:00
gingerBill 86e26c9a44 Remove dead code 2021-11-05 17:13:26 +00:00
gingerBill 541beb615b Move more things to PtrMap 2021-11-05 17:13:07 +00:00
gingerBill 6646348e1a Increase usage of PtrMap 2021-11-05 17:03:02 +00:00
gingerBill c38d6dc959 Remove HashKey usage for PtrMap calls 2021-11-05 16:46:09 +00:00
gingerBill 924faa58b4 Correct map_remove(PtrMap) 2021-11-05 16:45:27 +00:00
gingerBill 6be104e521 Make llvm backend code use PtrMap; remove dead code 2021-11-05 16:43:53 +00:00
gingerBill e95204908a Add PtrMap, begin working change Map to PtrMap where possible 2021-11-05 16:34:37 +00:00
gingerBill e963fc4d6a Change map index types to u32 from isize 2021-11-05 12:51:28 +00:00
gingerBill 1a75a71403 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-05 12:44:08 +00:00
gingerBill 439fc86740 Improve performance of the compiler hash table types and unify behaviour 2021-11-05 12:42:19 +00:00
gingerBill 0010e882a7 Make PtrSet match Map 2021-11-05 12:11:50 +00:00
gingerBill a022f18015 Reorganize code 2021-11-05 12:11:33 +00:00
Jeroen van Rijn cee9561259 Merge pull request #1271 from hdooley/master
don't try to use __cpuid() on arm64
2021-11-05 10:45:28 +01:00
Henry Dooley 3d0cd6f0dc don't try to use __cpuid() on arm64 2021-11-04 18:54:15 -07:00
gingerBill adb5928767 Change to RUNTIME_LINKAGE definition 2021-11-04 20:21:51 +00:00
gingerBill 23c74bc67b Update all_main.odin to include core:math/linalg/hlsl 2021-11-04 19:30:46 +00:00
gingerBill a22120fe94 Reorganize code 2021-11-04 17:38:58 +00:00
gingerBill ae25eaf10c Correct foreign import library usage 2021-11-04 17:25:37 +00:00
gingerBill adcfca966e Use Rtl*Memory procedures with -no-crt on Windows 2021-11-04 17:24:28 +00:00
gingerBill d8e34bd9b7 Add core:math/linalg/hlsl 2021-11-04 17:08:59 +00:00
gingerBill 68046d0c08 Allow casting between matrix types of different element types 2021-11-04 16:50:59 +00:00
gingerBill bc2bf1caeb Add #load_hash(<filepath>, <string-hash-kind>) 2021-11-04 16:29:41 +00:00
gingerBill d551144841 Add inverse for dmatN types 2021-11-04 16:09:19 +00:00
gingerBill 84540d7aa2 Add smoothstep 2021-11-04 15:57:27 +00:00
gingerBill 57eedfc4f4 Fix lb_emit_array_epi for matrix types 2021-11-04 15:01:31 +00:00
gingerBill 2718ade2bc Add core:math/linalg/glsl to all_main.odin 2021-11-04 14:56:16 +00:00
gingerBill 95f36d4fa5 Minor reorganization 2021-11-04 14:54:55 +00:00
gingerBill 3accf4048e Add f64 variants of all types and procedures 2021-11-04 14:52:03 +00:00
gingerBill eb05879148 Add more comments 2021-11-04 14:25:34 +00:00
gingerBill a882118c56 Add comments 2021-11-04 14:20:47 +00:00
gingerBill 57d15ac6e7 Remove unneeded suffixes 2021-11-04 14:11:34 +00:00
gingerBill e3cfdf6982 Remove build tag 2021-11-04 14:11:04 +00:00
gingerBill 017fe10762 core:math/linalg/glsl - GLSL-like mathematics types and operations 2021-11-04 14:09:12 +00:00
gingerBill 7bb7a741c6 Make math procedure contextless; Add asinh, acosh, atanh 2021-11-04 14:07:05 +00:00
gingerBill 14351c5bf2 Simplify logic for procs.odin 2021-11-04 13:56:38 +00:00
gingerBill 7ef3c87dbb Change RUNTIME_LINKAGE requirements 2021-11-04 13:52:53 +00:00
gingerBill b2a2aa15c2 Add ODIN_BUILD_MODE 2021-11-04 12:49:39 +00:00
gingerBill 1ec2f8d537 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-04 12:40:55 +00:00
gingerBill 6ded538546 @(linkage=<string>) for procedures and variables; @(require) for procedures; package runtime linkage improvements; Subsequence improvements to lb_run_remove_unused_function_pass 2021-11-04 12:40:50 +00:00
Jeroen van Rijn 0d1bc05419 Update issue templates 2021-11-04 12:37:24 +01:00
Jeroen van Rijn db2d7a4fdb Update issue templates 2021-11-04 12:36:48 +01:00
gingerBill 3fa7dabaa8 Correctly support -default-to-nil-allocator for all platforms 2021-11-04 11:03:21 +00:00
gingerBill 1980f32bd6 Correct demo.odin 2021-11-04 00:50:48 +00:00
gingerBill 9ab71ca0da Add ODIN_NO_CRT global constant 2021-11-04 00:50:28 +00:00
gingerBill 3d06dddb72 Allow casting from floats to quaternions 2021-11-03 12:45:19 +00:00
gingerBill 9896205a06 Make runtime builtin matrix procedures contextless 2021-11-03 12:44:34 +00:00
gingerBill 8a626ef564 Minor comments about matrix printing 2021-11-03 11:34:47 +00:00
gingerBill 8429943569 Represent matrices in fmt as expected 2021-11-03 11:27:21 +00:00
gingerBill edd12d505d Correct fmt for matrices 2021-11-03 11:20:04 +00:00
gingerBill 69f978f22b Correct lb_emit_matrix_flatten 2021-11-03 11:07:35 +00:00
gingerBill 229c98309e Correct assertion usage 2021-11-03 11:02:47 +00:00
Jeroen van Rijn c2665462e5 Merge pull request #1270 from Kelimion/fix_1268
Fix #1268.
2021-11-03 11:41:32 +01:00
Jeroen van Rijn 73648bb2d8 Fix #1268.
Error message for enumerated arrays going out of bounds was not yet updated for the Enum change.
2021-11-03 11:36:24 +01:00
Jeroen van Rijn ba0daaa706 Merge pull request #1269 from Kelimion/enum_array_bug
Fix error message.
2021-11-03 11:06:04 +01:00
Jeroen van Rijn dcc5697a48 Fix error message. 2021-11-03 11:01:18 +01:00
220 changed files with 22079 additions and 10443 deletions
+2 -2
View File
@@ -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
+26 -4
View File
@@ -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: |
+29
View File
@@ -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.
+1 -1
View File
@@ -79,4 +79,4 @@ if %release_mode% EQU 0 odin run examples/demo
del *.obj > NUL 2> NUL
:end_of_build
:end_of_build
+2
View File
@@ -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
View File
@@ -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"
}
+16
View File
@@ -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
+2
View File
@@ -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"
}
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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())
}
}
+2
View File
@@ -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"
}
+5
View File
@@ -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!
}
+5 -3
View File
@@ -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,
}
}
+2
View File
@@ -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
View File
@@ -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"
}
+8 -1
View File
@@ -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
View File
@@ -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) | &#10004;&#65039; | |
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | &#10004;&#65039; | &#10004;&#65039; |
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | &#10004;&#65039; | |
| [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | &#10004;&#65039; | &#10004;&#65039; |
| [Grøstl](http://www.groestl.info/Groestl.zip) | &#10004;&#65039; | |
| [HAVAL](https://web.archive.org/web/20150111210116/http://labs.calyptix.com/haval.php) | &#10004;&#65039; | |
| [JH](https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html) | &#10004;&#65039; | |
| [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | &#10004;&#65039; | &#10004;&#65039; |
| [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | &#10004;&#65039; | |
| [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | &#10004;&#65039; | &#10004;&#65039; |
| [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | &#10004;&#65039; | &#10004;&#65039; |
| [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | &#10004;&#65039; | &#10004;&#65039;\* |
| [SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | &#10004;&#65039; | &#10004;&#65039; |
| [SHA-2](https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf) | &#10004;&#65039; | &#10004;&#65039; |
| [SHA-3](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | &#10004;&#65039; | &#10004;&#65039; |
| [SHAKE](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | &#10004;&#65039; | &#10004;&#65039; |
| [Skein](https://www.schneier.com/academic/skein/) | | &#10004;&#65039;\*\* |
| [SM3](https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02) | &#10004;&#65039; | &#10004;&#65039; |
| [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | &#10004;&#65039; | &#10004;&#65039; |
| [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | &#10004;&#65039; | &#10004;&#65039; |
| [Tiger2](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | &#10004;&#65039; | |
| [Whirlpool](https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html) | &#10004;&#65039; | &#10004;&#65039; |
\* Only `RIPEMD-160`
\*\* Only `SKEIN-512`
| Algorithm | |
|:-------------------------------------------------------------------------------------------------------------|:-----------------|
| [BLAKE](https://web.archive.org/web/20190915215948/https://131002.net/blake) | &#10004;&#65039; |
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | &#10004;&#65039; |
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | &#10004;&#65039; |
| [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | &#10004;&#65039; |
| [Grøstl](http://www.groestl.info/Groestl.zip) | &#10004;&#65039; |
| [HAVAL](https://web.archive.org/web/20150111210116/http://labs.calyptix.com/haval.php) | &#10004;&#65039; |
| [JH](https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html) | &#10004;&#65039; |
| [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | &#10004;&#65039; |
| [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | &#10004;&#65039; |
| [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | &#10004;&#65039; |
| [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | &#10004;&#65039; |
| [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | &#10004;&#65039; |
| [SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | &#10004;&#65039; |
| [SHA-2](https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf) | &#10004;&#65039; |
| [SHA-3](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | &#10004;&#65039; |
| [SHAKE](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | &#10004;&#65039; |
| [SM3](https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02) | &#10004;&#65039; |
| [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | &#10004;&#65039; |
| [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | &#10004;&#65039; |
| [Tiger2](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | &#10004;&#65039; |
| [Whirlpool](https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html) | &#10004;&#65039; |
#### 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`.
+22 -14
View File
@@ -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 {
-79
View File
@@ -1,79 +0,0 @@
package _ctx
/*
Copyright 2021 zhibog
Made available under the BSD-3 license.
List of contributors:
zhibog: Initial creation and testing of the bindings.
Implementation of the context, used internally by the crypto library.
*/
import "core:io"
import "core:os"
Hash_Size :: enum {
_16,
_20,
_24,
_28,
_32,
_40,
_48,
_64,
_128,
}
Hash_Context :: struct {
botan_hash_algo: cstring,
external_ctx: any,
internal_ctx: any,
hash_size: Hash_Size,
hash_size_val: int,
is_using_odin: bool,
using vtbl: ^Hash_Context_Vtable,
}
Hash_Context_Vtable :: struct {
hash_bytes_16 : proc (ctx: ^Hash_Context, input: []byte) -> [16]byte,
hash_bytes_20 : proc (ctx: ^Hash_Context, input: []byte) -> [20]byte,
hash_bytes_24 : proc (ctx: ^Hash_Context, input: []byte) -> [24]byte,
hash_bytes_28 : proc (ctx: ^Hash_Context, input: []byte) -> [28]byte,
hash_bytes_32 : proc (ctx: ^Hash_Context, input: []byte) -> [32]byte,
hash_bytes_40 : proc (ctx: ^Hash_Context, input: []byte) -> [40]byte,
hash_bytes_48 : proc (ctx: ^Hash_Context, input: []byte) -> [48]byte,
hash_bytes_64 : proc (ctx: ^Hash_Context, input: []byte) -> [64]byte,
hash_bytes_128 : proc (ctx: ^Hash_Context, input: []byte) -> [128]byte,
hash_file_16 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool),
hash_file_20 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool),
hash_file_24 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool),
hash_file_28 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool),
hash_file_32 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool),
hash_file_40 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([40]byte, bool),
hash_file_48 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool),
hash_file_64 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool),
hash_file_128 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([128]byte, bool),
hash_stream_16 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([16]byte, bool),
hash_stream_20 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([20]byte, bool),
hash_stream_24 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([24]byte, bool),
hash_stream_28 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([28]byte, bool),
hash_stream_32 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([32]byte, bool),
hash_stream_40 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([40]byte, bool),
hash_stream_48 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([48]byte, bool),
hash_stream_64 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([64]byte, bool),
hash_stream_128 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([128]byte, bool),
hash_bytes_slice : proc (ctx: ^Hash_Context, input: []byte, out_size: int, allocator := context.allocator) -> []byte,
hash_file_slice : proc (ctx: ^Hash_Context, hd: os.Handle, out_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool),
hash_stream_slice : proc (ctx: ^Hash_Context, s: io.Stream, out_size: int, allocator := context.allocator) -> ([]byte, bool),
init : proc (ctx: ^Hash_Context),
update : proc (ctx: ^Hash_Context, data: []byte),
final : proc (ctx: ^Hash_Context, hash: []byte),
}
_init_vtable :: #force_inline proc() -> ^Hash_Context {
ctx := new(Hash_Context)
vtbl := new(Hash_Context_Vtable)
ctx.vtbl = vtbl
return ctx
}
+35
View File
@@ -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
+24
View File
@@ -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
}
+5 -6
View File
@@ -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 {
+10 -11
View File
@@ -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
View File
@@ -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)
}
+42 -127
View File
@@ -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)
}
+42 -127
View File
@@ -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)
}
-498
View File
@@ -1,498 +0,0 @@
package botan
/*
Copyright 2021 zhibog
Made available under the BSD-3 license.
List of contributors:
zhibog: Initial creation and testing of the bindings.
Implementation of the context for the Botan side.
*/
import "core:os"
import "core:io"
import "core:fmt"
import "core:strings"
import "../_ctx"
hash_bytes_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
hash: [16]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._16, 16), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
hash: [20]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._20, 20), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [24]byte {
hash: [24]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._24, 24), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
hash: [28]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._28, 28), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
hash: [32]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._32, 32), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
hash: [48]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._48, 48), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
hash: [64]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._64, 64), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [128]byte {
hash: [128]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._128, 128), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
hash := make([]byte, bit_size, allocator)
c: hash_t
hash_init(&c, _check_ctx(ctx, nil, bit_size), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash[:]
}
hash_file_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
if !load_at_once {
return hash_stream_16(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_16(ctx, buf[:]), ok
}
}
return [16]byte{}, false
}
hash_file_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
if !load_at_once {
return hash_stream_20(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_20(ctx, buf[:]), ok
}
}
return [20]byte{}, false
}
hash_file_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
if !load_at_once {
return hash_stream_24(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_24(ctx, buf[:]), ok
}
}
return [24]byte{}, false
}
hash_file_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
if !load_at_once {
return hash_stream_28(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_28(ctx, buf[:]), ok
}
}
return [28]byte{}, false
}
hash_file_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_32(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
hash_file_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
if !load_at_once {
return hash_stream_48(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_48(ctx, buf[:]), ok
}
}
return [48]byte{}, false
}
hash_file_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_64(ctx, buf[:]), ok
}
}
return [64]byte{}, false
}
hash_file_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([128]byte, bool) {
if !load_at_once {
return hash_stream_128(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_128(ctx, buf[:]), ok
}
}
return [128]byte{}, false
}
hash_file_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
if !load_at_once {
return hash_stream_slice(ctx, os.stream_from_handle(hd), bit_size, allocator)
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_slice(ctx, buf[:], bit_size, allocator), ok
}
}
return nil, false
}
hash_stream_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([16]byte, bool) {
hash: [16]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._16, 16), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([20]byte, bool) {
hash: [20]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._20, 20), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([24]byte, bool) {
hash: [24]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._24, 24), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([28]byte, bool) {
hash: [28]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._28, 28), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._32, 32), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([48]byte, bool) {
hash: [48]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._48, 48), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([64]byte, bool) {
hash: [64]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._64, 64), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([128]byte, bool) {
hash: [128]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._128, 128), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
hash := make([]byte, bit_size, allocator)
c: hash_t
hash_init(&c, _check_ctx(ctx, nil, bit_size), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash[:], true
}
init :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
c: hash_t
hash_init(&c, ctx.botan_hash_algo, 0)
ctx.external_ctx = c
}
update :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.external_ctx.(hash_t); ok {
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
}
}
final :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.external_ctx.(hash_t); ok {
hash_final(c, &hash[0])
hash_destroy(c)
}
}
assign_hash_vtable :: proc(ctx: ^_ctx.Hash_Context, hash_algo: cstring) {
ctx.init = init
ctx.update = update
ctx.final = final
ctx.botan_hash_algo = hash_algo
switch hash_algo {
case HASH_MD4, HASH_MD5:
ctx.hash_bytes_16 = hash_bytes_16
ctx.hash_file_16 = hash_file_16
ctx.hash_stream_16 = hash_stream_16
case HASH_SHA1, HASH_RIPEMD_160:
ctx.hash_bytes_20 = hash_bytes_20
ctx.hash_file_20 = hash_file_20
ctx.hash_stream_20 = hash_stream_20
case HASH_SHA2, HASH_SHA3:
ctx.hash_bytes_28 = hash_bytes_28
ctx.hash_file_28 = hash_file_28
ctx.hash_stream_28 = hash_stream_28
ctx.hash_bytes_32 = hash_bytes_32
ctx.hash_file_32 = hash_file_32
ctx.hash_stream_32 = hash_stream_32
ctx.hash_bytes_48 = hash_bytes_48
ctx.hash_file_48 = hash_file_48
ctx.hash_stream_48 = hash_stream_48
ctx.hash_bytes_64 = hash_bytes_64
ctx.hash_file_64 = hash_file_64
ctx.hash_stream_64 = hash_stream_64
case HASH_GOST, HASH_WHIRLPOOL, HASH_SM3:
ctx.hash_bytes_32 = hash_bytes_32
ctx.hash_file_32 = hash_file_32
ctx.hash_stream_32 = hash_stream_32
case HASH_STREEBOG:
ctx.hash_bytes_32 = hash_bytes_32
ctx.hash_file_32 = hash_file_32
ctx.hash_stream_32 = hash_stream_32
ctx.hash_bytes_64 = hash_bytes_64
ctx.hash_file_64 = hash_file_64
ctx.hash_stream_64 = hash_stream_64
case HASH_BLAKE2B:
ctx.hash_bytes_64 = hash_bytes_64
ctx.hash_file_64 = hash_file_64
ctx.hash_stream_64 = hash_stream_64
case HASH_TIGER:
ctx.hash_bytes_16 = hash_bytes_16
ctx.hash_file_16 = hash_file_16
ctx.hash_stream_16 = hash_stream_16
ctx.hash_bytes_20 = hash_bytes_20
ctx.hash_file_20 = hash_file_20
ctx.hash_stream_20 = hash_stream_20
ctx.hash_bytes_24 = hash_bytes_24
ctx.hash_file_24 = hash_file_24
ctx.hash_stream_24 = hash_stream_24
case HASH_SKEIN_512:
ctx.hash_bytes_slice = hash_bytes_slice
ctx.hash_file_slice = hash_file_slice
ctx.hash_stream_slice = hash_stream_slice
}
}
_check_ctx :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash_size: _ctx.Hash_Size, hash_size_val: int) -> cstring {
ctx.hash_size = hash_size
ctx.hash_size_val = hash_size_val
switch ctx.botan_hash_algo {
case HASH_SHA2:
#partial switch hash_size {
case ._28: return HASH_SHA_224
case ._32: return HASH_SHA_256
case ._48: return HASH_SHA_384
case ._64: return HASH_SHA_512
}
case HASH_SHA3:
#partial switch hash_size {
case ._28: return HASH_SHA3_224
case ._32: return HASH_SHA3_256
case ._48: return HASH_SHA3_384
case ._64: return HASH_SHA3_512
}
case HASH_KECCAK:
#partial switch hash_size {
case ._28: return HASH_KECCAK_224
case ._32: return HASH_KECCAK_256
case ._48: return HASH_KECCAK_384
case ._64: return HASH_KECCAK_512
}
case HASH_STREEBOG:
#partial switch hash_size {
case ._32: return HASH_STREEBOG_256
case ._64: return HASH_STREEBOG_512
}
case HASH_TIGER:
#partial switch hash_size {
case ._16: return HASH_TIGER_128
case ._20: return HASH_TIGER_160
case ._24: return HASH_TIGER_192
}
case HASH_SKEIN_512:
return strings.unsafe_string_to_cstring(fmt.tprintf("Skein-512(%d)", hash_size_val * 8))
case: return ctx.botan_hash_algo
}
return nil
}
+581
View File
@@ -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
}
+52
View File
@@ -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
View File
@@ -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
View File
@@ -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]
}
}
File diff suppressed because it is too large Load Diff
+214 -380
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
}
+163
View File
@@ -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
}
}
+7
View File
@@ -0,0 +1,7 @@
package crypto
when ODIN_OS != "linux" {
_rand_bytes :: proc (dst: []byte) {
unimplemented("crypto: rand_bytes not supported on this OS")
}
}
+37
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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[:])
}
-487
View File
@@ -1,487 +0,0 @@
package skein
/*
Copyright 2021 zhibog
Made available under the BSD-3 license.
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the SKEIN hashing algorithm, as defined in <https://www.schneier.com/academic/skein/>
This package offers the internal state sizes of 256, 512 and 1024 bits and arbitrary output size.
*/
import "core:os"
import "core:io"
import "../botan"
import "../_ctx"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
ctx.is_using_odin = false
} else {
_assign_hash_vtable(ctx)
ctx.is_using_odin = true
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
// @note(zh): Default to SKEIN-512
ctx.hash_bytes_slice = hash_bytes_skein512_odin
ctx.hash_file_slice = hash_file_skein512_odin
ctx.hash_stream_slice = hash_stream_skein512_odin
ctx.init = _init_skein512_odin
ctx.update = _update_skein512_odin
ctx.final = _final_skein512_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
_hash_impl.is_using_odin = false
// @note(zh): Botan only supports SKEIN-512.
botan.assign_hash_vtable(_hash_impl, botan.HASH_SKEIN_512)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
@(warning="SKEIN is not yet implemented in Odin. Botan bindings will be used")
use_odin :: #force_inline proc() {
// _hash_impl.is_using_odin = true
// _assign_hash_vtable(_hash_impl)
use_botan()
}
@(private)
_create_skein256_ctx :: #force_inline proc(size: int) {
_hash_impl.hash_size_val = size
if _hash_impl.is_using_odin {
ctx: Skein256_Context
ctx.h.bit_length = u64(size)
_hash_impl.internal_ctx = ctx
_hash_impl.hash_bytes_slice = hash_bytes_skein256_odin
_hash_impl.hash_file_slice = hash_file_skein256_odin
_hash_impl.hash_stream_slice = hash_stream_skein256_odin
_hash_impl.init = _init_skein256_odin
_hash_impl.update = _update_skein256_odin
_hash_impl.final = _final_skein256_odin
}
}
@(private)
_create_skein512_ctx :: #force_inline proc(size: int) {
_hash_impl.hash_size_val = size
if _hash_impl.is_using_odin {
ctx: Skein512_Context
ctx.h.bit_length = u64(size)
_hash_impl.internal_ctx = ctx
_hash_impl.hash_bytes_slice = hash_bytes_skein512_odin
_hash_impl.hash_file_slice = hash_file_skein512_odin
_hash_impl.hash_stream_slice = hash_stream_skein512_odin
_hash_impl.init = _init_skein512_odin
_hash_impl.update = _update_skein512_odin
_hash_impl.final = _final_skein512_odin
}
}
@(private)
_create_skein1024_ctx :: #force_inline proc(size: int) {
_hash_impl.hash_size_val = size
if _hash_impl.is_using_odin {
ctx: Skein1024_Context
ctx.h.bit_length = u64(size)
_hash_impl.internal_ctx = ctx
_hash_impl.hash_bytes_slice = hash_bytes_skein1024_odin
_hash_impl.hash_file_slice = hash_file_skein1024_odin
_hash_impl.hash_stream_slice = hash_stream_skein1024_odin
_hash_impl.init = _init_skein1024_odin
_hash_impl.update = _update_skein1024_odin
_hash_impl.final = _final_skein1024_odin
}
}
/*
High level API
*/
// hash_skein256_string will hash the given input and return the
// computed hash
hash_skein256_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
return hash_skein256_bytes(transmute([]byte)(data), bit_size, allocator)
}
// hash_skein256_bytes will hash the given input and return the
// computed hash
hash_skein256_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
_create_skein256_ctx(bit_size)
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
}
// hash_skein256_stream will read the stream in chunks and compute a
// hash from its contents
hash_skein256_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
_create_skein256_ctx(bit_size)
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
}
// hash_skein256_file will read the file provided by the given handle
// and compute a hash
hash_skein256_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
_create_skein256_ctx(bit_size)
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
}
hash_skein256 :: proc {
hash_skein256_stream,
hash_skein256_file,
hash_skein256_bytes,
hash_skein256_string,
}
// hash_skein512_string will hash the given input and return the
// computed hash
hash_skein512_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
return hash_skein512_bytes(transmute([]byte)(data), bit_size, allocator)
}
// hash_skein512_bytes will hash the given input and return the
// computed hash
hash_skein512_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
_create_skein512_ctx(bit_size)
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
}
// hash_skein512_stream will read the stream in chunks and compute a
// hash from its contents
hash_skein512_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
_create_skein512_ctx(bit_size)
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
}
// hash_skein512_file will read the file provided by the given handle
// and compute a hash
hash_skein512_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
_create_skein512_ctx(bit_size)
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
}
hash_skein512 :: proc {
hash_skein512_stream,
hash_skein512_file,
hash_skein512_bytes,
hash_skein512_string,
}
// hash_skein1024_string will hash the given input and return the
// computed hash
hash_skein1024_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
return hash_skein1024_bytes(transmute([]byte)(data), bit_size, allocator)
}
// hash_skein1024_bytes will hash the given input and return the
// computed hash
hash_skein1024_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
_create_skein1024_ctx(bit_size)
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
}
// hash_skein1024_stream will read the stream in chunks and compute a
// hash from its contents
hash_skein1024_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
_create_skein1024_ctx(bit_size)
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
}
// hash_skein1024_file will read the file provided by the given handle
// and compute a hash
hash_skein1024_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
_create_skein1024_ctx(bit_size)
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
}
hash_skein1024 :: proc {
hash_skein1024_stream,
hash_skein1024_file,
hash_skein1024_bytes,
hash_skein1024_string,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
hash := make([]byte, bit_size, allocator)
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
return hash
} else {
delete(hash)
return nil
}
}
hash_stream_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
hash := make([]byte, bit_size, allocator)
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
delete(hash)
return nil, false
}
}
hash_file_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
if !load_at_once {
return hash_stream_skein256_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_skein256_odin(ctx, buf[:], bit_size, allocator), ok
}
}
return nil, false
}
hash_bytes_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
hash := make([]byte, bit_size, allocator)
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
return hash
} else {
delete(hash)
return nil
}
}
hash_stream_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
hash := make([]byte, bit_size, allocator)
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
delete(hash)
return nil, false
}
}
hash_file_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
if !load_at_once {
return hash_stream_skein512_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_skein512_odin(ctx, buf[:], bit_size, allocator), ok
}
}
return nil, false
}
hash_bytes_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
hash := make([]byte, bit_size, allocator)
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
return hash
} else {
delete(hash)
return nil
}
}
hash_stream_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
hash := make([]byte, bit_size, allocator)
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
delete(hash)
return nil, false
}
}
hash_file_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
if !load_at_once {
return hash_stream_skein512_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_skein512_odin(ctx, buf[:], bit_size, allocator), ok
}
}
return nil, false
}
@(private)
_init_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_skein256_ctx(ctx.hash_size_val)
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
init_odin(&c)
}
}
@(private)
_update_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
update_odin(&c, data)
}
}
@(private)
_final_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
final_odin(&c, hash)
}
}
@(private)
_init_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_skein512_ctx(ctx.hash_size_val)
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
init_odin(&c)
}
}
@(private)
_update_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
update_odin(&c, data)
}
}
@(private)
_final_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
final_odin(&c, hash)
}
}
@(private)
_init_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_skein1024_ctx(ctx.hash_size_val)
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
init_odin(&c)
}
}
@(private)
_update_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
update_odin(&c, data)
}
}
@(private)
_final_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
final_odin(&c, hash)
}
}
/*
SKEIN implementation
*/
STATE_WORDS_256 :: 4
STATE_WORDS_512 :: 8
STATE_WORDS_1024 :: 16
STATE_BYTES_256 :: 32
STATE_BYTES_512 :: 64
STATE_BYTES_1024 :: 128
Skein_Header :: struct {
bit_length: u64,
bcnt: u64,
t: [2]u64,
}
Skein256_Context :: struct {
h: Skein_Header,
x: [STATE_WORDS_256]u64,
b: [STATE_BYTES_256]byte,
}
Skein512_Context :: struct {
h: Skein_Header,
x: [STATE_WORDS_512]u64,
b: [STATE_BYTES_512]byte,
}
Skein1024_Context :: struct {
h: Skein_Header,
x: [STATE_WORDS_1024]u64,
b: [STATE_BYTES_1024]byte,
}
init_odin :: proc(ctx: ^$T) {
}
update_odin :: proc(ctx: ^$T, data: []byte) {
}
final_odin :: proc(ctx: ^$T, hash: []byte) {
}
+78 -184
View File
@@ -6,7 +6,6 @@ package sm3
List of contributors:
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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
+117 -203
View File
@@ -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])
}
}
+126
View File
@@ -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[:])
}
+2 -2
View File
@@ -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 {
+4 -3
View File
@@ -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
}
+1
View File
@@ -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
View File
@@ -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)
+44
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
//+build !freestanding
//+build !freestanding !js
package fmt
import "core:runtime"
+20 -22
View File
@@ -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
}
+1 -1
View File
@@ -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
}
+49
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+169
View File
@@ -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,
}
+54
View File
@@ -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,
}
+410
View File
@@ -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
}
+226
View File
@@ -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,
}
+361
View File
@@ -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,
}
+198
View File
@@ -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)
}
+34 -42
View File
@@ -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)
}
+1
View File
@@ -579,6 +579,7 @@ Field_Flags_Signature :: Field_Flags{
.No_Alias,
.C_Vararg,
.Auto_Cast,
.Any_Int,
.Default_Parameters,
}
+1 -1
View File
@@ -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,
+1
View File
@@ -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
}
+64 -9
View File
@@ -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
View File
@@ -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
View File
@@ -1,7 +1,6 @@
package os2
import "core:mem"
import "core:io"
import "core:strconv"
import "core:unicode/utf8"
-1
View File
@@ -1,7 +1,6 @@
//+private
package os2
import "core:runtime"
import "core:mem"
import win32 "core:sys/windows"
+4
View File
@@ -0,0 +1,4 @@
//+freestanding
package os
#panic("package os does not support a freestanding target")
+4
View File
@@ -0,0 +1,4 @@
//+js
package os
#panic("package os does not support a js target")
+2 -3
View File
@@ -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 {
+10 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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()
}
+19 -19
View File
@@ -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 {
+3 -13
View File
@@ -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"
+5
View File
@@ -0,0 +1,5 @@
//+build js
package runtime
default_allocator_proc :: nil_allocator_proc
default_allocator :: nil_allocator
+27 -6
View File
@@ -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
}
+2 -29
View File
@@ -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