Created
July 30, 2018 21:33
-
-
Save lrettig/c96e5482503c5cd5cc4b64573cd6ca8f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; | |
;; Keccak-256 (pre-standard SHA3) implementation in WASM | |
;; | |
;; Main entry point is $KECCAK which has 4 parameters: | |
;; - context offset (i32) -> 616 bytes of context | |
;; - input offset (i32) | |
;; - input length (i32) | |
;; - output offset (i32) -> 32 byte hash | |
;; | |
;; The context is laid out as follows: | |
;; 0: 1600 bits - 200 bytes - hashing state | |
;; 200: 64 bits - 8 bytes - residue position | |
;; 208: 1536 bits - 192 bytes - residue buffer | |
;; 400: 1536 bits - 192 bytes - round constants | |
;; 592: 192 bits - 24 bytes - rotation constants | |
;; | |
;; -- | |
;; | |
;; Specification at: http://keccak.noekeon.org/specs_summary.html | |
;; | |
;; This implementation is based on https://github.com/rhash/RHash/blob/master/librhash/sha3.c | |
;; | |
;; Most of the methods are fully unrolled. Would be much nicer with macros, hopefully this | |
;; gets implemented: https://github.com/WebAssembly/sexpr-wasm-prototype/issues/92 | |
;; | |
(module | |
(memory 1) | |
(export "memory" (memory 0)) | |
(export "main" (func $keccak)) | |
(func $keccak_theta | |
(param $context_offset i32) | |
(local $C0 i64) | |
(local $C1 i64) | |
(local $C2 i64) | |
(local $C3 i64) | |
(local $C4 i64) | |
(local $D0 i64) | |
(local $D1 i64) | |
(local $D2 i64) | |
(local $D3 i64) | |
(local $D4 i64) | |
;; C[x] = A[x] ^ A[x + 5] ^ A[x + 10] ^ A[x + 15] ^ A[x + 20]; | |
(set_local $C0 | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 0))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 40))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 80))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 120))) | |
(i64.load (i32.add (get_local $context_offset) (i32.const 160))) | |
) | |
) | |
) | |
) | |
) | |
(set_local $C1 | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 8))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 48))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 88))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 128))) | |
(i64.load (i32.add (get_local $context_offset) (i32.const 168))) | |
) | |
) | |
) | |
) | |
) | |
(set_local $C2 | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 16))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 56))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 96))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 136))) | |
(i64.load (i32.add (get_local $context_offset) (i32.const 176))) | |
) | |
) | |
) | |
) | |
) | |
(set_local $C3 | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 24))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 64))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 104))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 144))) | |
(i64.load (i32.add (get_local $context_offset) (i32.const 184))) | |
) | |
) | |
) | |
) | |
) | |
(set_local $C4 | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 32))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 72))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 112))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 152))) | |
(i64.load (i32.add (get_local $context_offset) (i32.const 192))) | |
) | |
) | |
) | |
) | |
) | |
;; D[0] = ROTL64(C[1], 1) ^ C[4]; | |
(set_local $D0 | |
(i64.xor | |
(get_local $C4) | |
(i64.rotl | |
(get_local $C1) | |
(i64.const 1) | |
) | |
) | |
) | |
;; D[1] = ROTL64(C[2], 1) ^ C[0]; | |
(set_local $D1 | |
(i64.xor | |
(get_local $C0) | |
(i64.rotl | |
(get_local $C2) | |
(i64.const 1) | |
) | |
) | |
) | |
;; D[2] = ROTL64(C[3], 1) ^ C[1]; | |
(set_local $D2 | |
(i64.xor | |
(get_local $C1) | |
(i64.rotl | |
(get_local $C3) | |
(i64.const 1) | |
) | |
) | |
) | |
;; D[3] = ROTL64(C[4], 1) ^ C[2]; | |
(set_local $D3 | |
(i64.xor | |
(get_local $C2) | |
(i64.rotl | |
(get_local $C4) | |
(i64.const 1) | |
) | |
) | |
) | |
;; D[4] = ROTL64(C[0], 1) ^ C[3]; | |
(set_local $D4 | |
(i64.xor | |
(get_local $C3) | |
(i64.rotl | |
(get_local $C0) | |
(i64.const 1) | |
) | |
) | |
) | |
;; A[x] ^= D[x]; | |
;; A[x + 5] ^= D[x]; | |
;; A[x + 10] ^= D[x]; | |
;; A[x + 15] ^= D[x]; | |
;; A[x + 20] ^= D[x]; | |
;; x = 0 | |
(i64.store (i32.add (get_local $context_offset) (i32.const 0)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 0))) | |
(get_local $D0) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 40)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 40))) | |
(get_local $D0) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 80)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 80))) | |
(get_local $D0) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 120)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 120))) | |
(get_local $D0) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 160)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 160))) | |
(get_local $D0) | |
) | |
) | |
;; x = 1 | |
(i64.store (i32.add (get_local $context_offset) (i32.const 8)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 8))) | |
(get_local $D1) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 48)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 48))) | |
(get_local $D1) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 88)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 88))) | |
(get_local $D1) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 128)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 128))) | |
(get_local $D1) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 168)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 168))) | |
(get_local $D1) | |
) | |
) | |
;; x = 2 | |
(i64.store (i32.add (get_local $context_offset) (i32.const 16)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 16))) | |
(get_local $D2) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 56)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 56))) | |
(get_local $D2) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 96)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 96))) | |
(get_local $D2) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 136)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 136))) | |
(get_local $D2) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 176)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 176))) | |
(get_local $D2) | |
) | |
) | |
;; x = 3 | |
(i64.store (i32.add (get_local $context_offset) (i32.const 24)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 24))) | |
(get_local $D3) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 64)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 64))) | |
(get_local $D3) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 104)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 104))) | |
(get_local $D3) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 144)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 144))) | |
(get_local $D3) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 184)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 184))) | |
(get_local $D3) | |
) | |
) | |
;; x = 4 | |
(i64.store (i32.add (get_local $context_offset) (i32.const 32)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 32))) | |
(get_local $D4) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 72)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 72))) | |
(get_local $D4) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 112)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 112))) | |
(get_local $D4) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 152)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 152))) | |
(get_local $D4) | |
) | |
) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 192)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 192))) | |
(get_local $D4) | |
) | |
) | |
) | |
(func $keccak_rho | |
(param $context_offset i32) | |
(param $rotation_consts i32) | |
;;(local $tmp i32) | |
;; state[ 1] = ROTL64(state[ 1], 1); | |
;;(set_local $tmp (i32.add (get_local $context_offset) (i32.const 1))) | |
;;(i64.store (get_local $tmp) (i64.rotl (i64.load (get_local $context_offset)) (i64.const 1))) | |
;;(set_local $tmp (i32.add (get_local $context_offset) (i32.const 2))) | |
;;(i64.store (get_local $tmp) (i64.rotl (i64.load (get_local $context_offset)) (i64.const 62))) | |
(local $tmp i32) | |
(local $i i32) | |
;; for (i = 0; i <= 24; i++) | |
(set_local $i (i32.const 0)) | |
(loop $done $loop | |
(if (i32.ge_u (get_local $i) (i32.const 24)) | |
(br $done) | |
) | |
(set_local $tmp (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (i32.const 1) (get_local $i))))) | |
(i64.store (get_local $tmp) (i64.rotl (i64.load (get_local $tmp)) (i64.load8_u (i32.add (get_local $rotation_consts) (get_local $i))))) | |
(set_local $i (i32.add (get_local $i) (i32.const 1))) | |
(br $loop) | |
) | |
) | |
(func $keccak_pi | |
(param $context_offset i32) | |
(local $A1 i64) | |
(set_local $A1 (i64.load (i32.add (get_local $context_offset) (i32.const 8)))) | |
;; Swap non-overlapping fields, i.e. $A1 = $A6, etc. | |
;; NOTE: $A0 is untouched | |
(i64.store (i32.add (get_local $context_offset) (i32.const 8)) (i64.load (i32.add (get_local $context_offset) (i32.const 48)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 48)) (i64.load (i32.add (get_local $context_offset) (i32.const 72)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 72)) (i64.load (i32.add (get_local $context_offset) (i32.const 176)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 176)) (i64.load (i32.add (get_local $context_offset) (i32.const 112)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 112)) (i64.load (i32.add (get_local $context_offset) (i32.const 160)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 160)) (i64.load (i32.add (get_local $context_offset) (i32.const 16)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 16)) (i64.load (i32.add (get_local $context_offset) (i32.const 96)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 96)) (i64.load (i32.add (get_local $context_offset) (i32.const 104)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 104)) (i64.load (i32.add (get_local $context_offset) (i32.const 152)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 152)) (i64.load (i32.add (get_local $context_offset) (i32.const 184)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 184)) (i64.load (i32.add (get_local $context_offset) (i32.const 120)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 120)) (i64.load (i32.add (get_local $context_offset) (i32.const 32)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 32)) (i64.load (i32.add (get_local $context_offset) (i32.const 192)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 192)) (i64.load (i32.add (get_local $context_offset) (i32.const 168)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 168)) (i64.load (i32.add (get_local $context_offset) (i32.const 64)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 64)) (i64.load (i32.add (get_local $context_offset) (i32.const 128)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 128)) (i64.load (i32.add (get_local $context_offset) (i32.const 40)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 40)) (i64.load (i32.add (get_local $context_offset) (i32.const 24)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 24)) (i64.load (i32.add (get_local $context_offset) (i32.const 144)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 144)) (i64.load (i32.add (get_local $context_offset) (i32.const 136)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 136)) (i64.load (i32.add (get_local $context_offset) (i32.const 88)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 88)) (i64.load (i32.add (get_local $context_offset) (i32.const 56)))) | |
(i64.store (i32.add (get_local $context_offset) (i32.const 56)) (i64.load (i32.add (get_local $context_offset) (i32.const 80)))) | |
;; Place the previously saved overlapping field | |
(i64.store (i32.add (get_local $context_offset) (i32.const 80)) (get_local $A1)) | |
) | |
(func $keccak_chi | |
(param $context_offset i32) | |
(local $A0 i64) | |
(local $A1 i64) | |
(local $i i32) | |
;; for (round = 0; round < 25; i += 5) | |
(set_local $i (i32.const 0)) | |
(loop $done $loop | |
(if (i32.ge_u (get_local $i) (i32.const 25)) | |
(br $done) | |
) | |
(set_local $A0 (i64.load (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (get_local $i))))) | |
(set_local $A1 (i64.load (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 1)))))) | |
;; A[0 + i] ^= ~A1 & A[2 + i]; | |
(i64.store (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (get_local $i))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (get_local $i)))) | |
(i64.and | |
(i64.xor (get_local $A1) (i64.const 0xFFFFFFFFFFFFFFFF)) ;; bitwise not | |
(i64.load (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 2))))) | |
) | |
) | |
) | |
;; A[1 + i] ^= ~A[2 + i] & A[3 + i]; | |
(i64.store (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 1)))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 1))))) | |
(i64.and | |
(i64.xor (i64.load (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 2))))) (i64.const 0xFFFFFFFFFFFFFFFF)) ;; bitwise not | |
(i64.load (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 3))))) | |
) | |
) | |
) | |
;; A[2 + i] ^= ~A[3 + i] & A[4 + i]; | |
(i64.store (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 2)))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 2))))) | |
(i64.and | |
(i64.xor (i64.load (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 3))))) (i64.const 0xFFFFFFFFFFFFFFFF)) ;; bitwise not | |
(i64.load (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 4))))) | |
) | |
) | |
) | |
;; A[3 + i] ^= ~A[4 + i] & A0; | |
(i64.store (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 3)))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 3))))) | |
(i64.and | |
(i64.xor (i64.load (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 4))))) (i64.const 0xFFFFFFFFFFFFFFFF)) ;; bitwise not | |
(get_local $A0) | |
) | |
) | |
) | |
;; A[4 + i] ^= ~A0 & A1; | |
(i64.store (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 4)))) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.mul (i32.const 8) (i32.add (get_local $i) (i32.const 4))))) | |
(i64.and | |
(i64.xor (get_local $A0) (i64.const 0xFFFFFFFFFFFFFFFF)) ;; bitwise not | |
(get_local $A1) | |
) | |
) | |
) | |
(set_local $i (i32.add (get_local $i) (i32.const 5))) | |
(br $loop) | |
) | |
) | |
(func $keccak_permute | |
(param $context_offset i32) | |
(local $rotation_consts i32) | |
(local $round_consts i32) | |
(local $round i32) | |
(set_local $round_consts (i32.add (get_local $context_offset) (i32.const 400))) | |
(set_local $rotation_consts (i32.add (get_local $context_offset) (i32.const 592))) | |
;; for (round = 0; round < 24; round++) | |
(set_local $round (i32.const 0)) | |
(loop $done $loop | |
(if (i32.ge_u (get_local $round) (i32.const 24)) | |
(br $done) | |
) | |
;; theta transform | |
(call $keccak_theta (get_local $context_offset)) | |
;; rho transform | |
(call $keccak_rho (get_local $context_offset) (get_local $rotation_consts)) | |
;; pi transform | |
(call $keccak_pi (get_local $context_offset)) | |
;; chi transform | |
(call $keccak_chi (get_local $context_offset)) | |
;; iota transform | |
;; context_offset[0] ^= KECCAK_ROUND_CONSTANTS[round]; | |
(i64.store (get_local $context_offset) | |
(i64.xor | |
(i64.load (get_local $context_offset)) | |
(i64.load (i32.add (get_local $round_consts) (i32.mul (i32.const 8) (get_local $round)))) | |
) | |
) | |
(set_local $round (i32.add (get_local $round) (i32.const 1))) | |
(br $loop) | |
) | |
) | |
(func $keccak_block | |
(param $input_offset i32) | |
(param $input_length i32) ;; ignored, we expect keccak256 | |
(param $context_offset i32) | |
;; read blocks in little-endian order and XOR against context_offset | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 0)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 0))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 0))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 8)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 8))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 8))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 16)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 16))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 16))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 24)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 24))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 24))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 32)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 32))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 32))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 40)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 40))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 40))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 48)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 48))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 48))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 56)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 56))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 56))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 64)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 64))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 64))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 72)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 72))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 72))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 80)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 80))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 80))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 88)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 88))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 88))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 96)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 96))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 96))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 104)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 104))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 104))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 112)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 112))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 112))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 120)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 120))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 120))) | |
) | |
) | |
(i64.store | |
(i32.add (get_local $context_offset) (i32.const 128)) | |
(i64.xor | |
(i64.load (i32.add (get_local $context_offset) (i32.const 128))) | |
(i64.load (i32.add (get_local $input_offset) (i32.const 128))) | |
) | |
) | |
(call $keccak_permute (get_local $context_offset)) | |
) | |
;; | |
;; Initialise the context | |
;; | |
(func $keccak_init | |
(param $context_offset i32) | |
(local $round_consts i32) | |
(local $rotation_consts i32) | |
(call $keccak_reset (get_local $context_offset)) | |
;; insert the round constants (used by $KECCAK_IOTA) | |
(set_local $round_consts (i32.add (get_local $context_offset) (i32.const 400))) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 0)) (i64.const 0x0000000000000001)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 8)) (i64.const 0x0000000000008082)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 16)) (i64.const 0x800000000000808A)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 24)) (i64.const 0x8000000080008000)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 32)) (i64.const 0x000000000000808B)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 40)) (i64.const 0x0000000080000001)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 48)) (i64.const 0x8000000080008081)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 56)) (i64.const 0x8000000000008009)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 64)) (i64.const 0x000000000000008A)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 72)) (i64.const 0x0000000000000088)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 80)) (i64.const 0x0000000080008009)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 88)) (i64.const 0x000000008000000A)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 96)) (i64.const 0x000000008000808B)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 104)) (i64.const 0x800000000000008B)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 112)) (i64.const 0x8000000000008089)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 120)) (i64.const 0x8000000000008003)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 128)) (i64.const 0x8000000000008002)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 136)) (i64.const 0x8000000000000080)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 144)) (i64.const 0x000000000000800A)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 152)) (i64.const 0x800000008000000A)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 160)) (i64.const 0x8000000080008081)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 168)) (i64.const 0x8000000000008080)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 176)) (i64.const 0x0000000080000001)) | |
(i64.store (i32.add (get_local $round_consts) (i32.const 184)) (i64.const 0x8000000080008008)) | |
;; insert the rotation constants (used by $keccak_rho) | |
(set_local $rotation_consts (i32.add (get_local $context_offset) (i32.const 592))) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 0)) (i32.const 1)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 1)) (i32.const 62)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 2)) (i32.const 28)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 3)) (i32.const 27)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 4)) (i32.const 36)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 5)) (i32.const 44)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 6)) (i32.const 6)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 7)) (i32.const 55)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 8)) (i32.const 20)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 9)) (i32.const 3)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 10)) (i32.const 10)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 11)) (i32.const 43)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 12)) (i32.const 25)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 13)) (i32.const 39)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 14)) (i32.const 41)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 15)) (i32.const 45)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 16)) (i32.const 15)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 17)) (i32.const 21)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 18)) (i32.const 8)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 19)) (i32.const 18)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 20)) (i32.const 2)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 21)) (i32.const 61)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 22)) (i32.const 56)) | |
(i32.store8 (i32.add (get_local $rotation_consts) (i32.const 23)) (i32.const 14)) | |
) | |
;; | |
;; Reset the context | |
;; | |
(func $keccak_reset | |
(param $context_offset i32) | |
;; clear out the context memory | |
(call $memset (get_local $context_offset) (i32.const 0) (i32.const 400)) | |
) | |
;; | |
;; Push input to the context | |
;; | |
(func $keccak_update | |
(param $context_offset i32) | |
(param $input_offset i32) | |
(param $input_length i32) | |
(local $residue_offset i32) | |
(local $residue_buffer i32) | |
(local $residue_index i32) | |
(local $tmp i32) | |
;; this is where we store the pointer | |
(set_local $residue_offset (i32.add (get_local $context_offset) (i32.const 200))) | |
;; this is where the buffer is | |
(set_local $residue_buffer (i32.add (get_local $context_offset) (i32.const 208))) | |
(set_local $residue_index (i32.load (get_local $residue_offset))) | |
;; process residue from last block | |
(if (i32.ne (get_local $residue_index) (i32.const 0)) | |
(then | |
;; the space left in the residue buffer | |
(set_local $tmp (i32.sub (i32.const 136) (get_local $residue_index))) | |
;; limit to what we have as an input | |
(if (i32.lt_u (get_local $input_length) (get_local $tmp)) | |
(set_local $tmp (get_local $input_length)) | |
) | |
;; fill up the residue buffer | |
(call $memcpy | |
(i32.add (get_local $residue_buffer) (get_local $residue_index)) | |
(get_local $input_offset) | |
(get_local $tmp) | |
) | |
(set_local $residue_index (i32.add (get_local $residue_index) (get_local $tmp))) | |
;; block complete | |
(if (i32.eq (get_local $residue_index) (i32.const 136)) | |
(call $keccak_block (get_local $input_offset) (i32.const 136) (get_local $context_offset)) | |
(set_local $residue_index (i32.const 0)) | |
) | |
(i32.store (get_local $residue_offset) (get_local $residue_index)) | |
(set_local $input_length (i32.sub (get_local $input_length) (get_local $tmp))) | |
) | |
) | |
;; while (input_length > block_size) | |
(loop $done $loop | |
(if (i32.lt_u (get_local $input_length) (i32.const 136)) | |
(br $done) | |
) | |
(call $keccak_block (get_local $input_offset) (i32.const 136) (get_local $context_offset)) | |
(set_local $input_offset (i32.add (get_local $input_offset) (i32.const 136))) | |
(set_local $input_length (i32.sub (get_local $input_length) (i32.const 136))) | |
(br $loop) | |
) | |
;; copy to the residue buffer | |
(if (i32.gt_u (get_local $input_length) (i32.const 0)) | |
(then | |
(call $memcpy | |
(i32.add (get_local $residue_buffer) (get_local $residue_index)) | |
(get_local $input_offset) | |
(get_local $input_length) | |
) | |
(set_local $residue_index (i32.add (get_local $residue_index) (get_local $input_length))) | |
(i32.store (get_local $residue_offset) (get_local $residue_index)) | |
) | |
) | |
) | |
;; | |
;; Finalise and return the hash | |
;; | |
;; The 256 bit hash is returned at the output offset. | |
;; | |
(func $keccak_finish | |
(param $context_offset i32) | |
(param $output_offset i32) | |
(local $residue_offset i32) | |
(local $residue_buffer i32) | |
(local $residue_index i32) | |
(local $tmp i32) | |
;; this is where we store the pointer | |
(set_local $residue_offset (i32.add (get_local $context_offset) (i32.const 200))) | |
;; this is where the buffer is | |
(set_local $residue_buffer (i32.add (get_local $context_offset) (i32.const 208))) | |
(set_local $residue_index (i32.load (get_local $residue_offset))) | |
(set_local $tmp (get_local $residue_index)) | |
;; clear the rest of the residue buffer | |
(call $memset (i32.add (get_local $residue_buffer) (get_local $tmp)) (i32.const 0) (i32.sub (i32.const 136) (get_local $tmp))) | |
;; ((char*)ctx->message)[ctx->rest] |= 0x01; | |
(set_local $tmp (i32.add (get_local $residue_buffer) (get_local $residue_index))) | |
(i32.store8 (get_local $tmp) (i32.or (i32.load8_u (get_local $tmp)) (i32.const 0x01))) | |
;; ((char*)ctx->message)[block_size - 1] |= 0x80; | |
(set_local $tmp (i32.add (get_local $residue_buffer) (i32.const 135))) | |
(i32.store8 (get_local $tmp) (i32.or (i32.load8_u (get_local $tmp)) (i32.const 0x80))) | |
(call $keccak_block (get_local $residue_buffer) (i32.const 136) (get_local $context_offset)) | |
;; the first 32 bytes pointed at by $output_offset is the final hash | |
(i64.store (get_local $output_offset) (i64.load (get_local $context_offset))) | |
(i64.store (i32.add (get_local $output_offset) (i32.const 8)) (i64.load (i32.add (get_local $context_offset) (i32.const 8)))) | |
(i64.store (i32.add (get_local $output_offset) (i32.const 16)) (i64.load (i32.add (get_local $context_offset) (i32.const 16)))) | |
(i64.store (i32.add (get_local $output_offset) (i32.const 24)) (i64.load (i32.add (get_local $context_offset) (i32.const 24)))) | |
) | |
;; | |
;; Calculate the hash. Helper method incorporating the above three. | |
;; | |
(func $keccak | |
(param $context_offset i32) | |
(param $input_offset i32) | |
(param $input_length i32) | |
(param $output_offset i32) | |
(call $keccak_init (get_local $context_offset)) | |
(call $keccak_update (get_local $context_offset) (get_local $input_offset) (get_local $input_length)) | |
(call $keccak_finish (get_local $context_offset) (get_local $output_offset)) | |
) | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment