sha512

Pure Haskell SHA-512, HMAC-SHA512 (docs.ppad.tech/sha512).
git clone git://git.ppad.tech/sha512.git
Log | Files | Refs | README | LICENSE

commit adc0406051a470836d2e0bc2edb4b1fc2ca06c43
parent 8148385e2efc89e73e3a242a0b8373b190ada831
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 31 Oct 2026 13:47:59 +0400

lib: rename the Arm module and kernel to Native

Crypto.Hash.SHA512.Arm becomes Crypto.Hash.SHA512.Native, and
cbits/sha512_arm.c becomes cbits/sha512_native.c, exporting
sha512_block_native and sha512_native_available.  Nothing in the
Haskell module is specific to ARM beyond the FFI symbol it binds, so
the same driver can sit over a block function for another
architecture.

Diffstat:
Dcbits/sha512_arm.c | 500-------------------------------------------------------------------------------
Acbits/sha512_native.c | 500+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/Crypto/Hash/SHA512.hs | 10+++++-----
Dlib/Crypto/Hash/SHA512/Arm.hs | 268-------------------------------------------------------------------------------
Alib/Crypto/Hash/SHA512/Native.hs | 268+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mppad-sha512.cabal | 4++--
6 files changed, 775 insertions(+), 775 deletions(-)

diff --git a/cbits/sha512_arm.c b/cbits/sha512_arm.c @@ -1,500 +0,0 @@ -#include <stdint.h> -#include <string.h> - -#if defined(__aarch64__) - -#include <arm_neon.h> - -#if defined(__APPLE__) -#include <sys/sysctl.h> -#elif defined(__linux__) -#include <sys/auxv.h> -#ifndef HWCAP_SHA512 -#define HWCAP_SHA512 (1UL << 21) -#endif -#endif - -static const uint64_t K[80] = { - 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, - 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, - 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, - 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, - 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, - 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, - 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, - 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, - 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, - 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, - 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, - 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, - 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, - 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, - 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, - 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, - 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, - 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, - 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, - 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, - 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, - 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, - 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, - 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, - 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, - 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, - 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, - 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, - 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, - 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, - 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, - 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, - 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, - 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, - 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, - 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, - 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, - 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, - 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, - 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL -}; - -/* - * Process one 128-byte block using ARM SHA512 crypto instructions. - * - * state: pointer to 8 uint64_t words (a,b,c,d,e,f,g,h) - * block: pointer to 16 uint64_t words (already native endian) - * - * The state is updated in place. - * - * Compiled for the SHA512 extension only; callers must first check - * 'sha512_arm_available'. - */ -__attribute__((target("+sha3"))) -void sha512_block_arm(uint64_t *state, const uint64_t *block) { - /* Load current hash state */ - uint64x2_t ab = vld1q_u64(&state[0]); - uint64x2_t cd = vld1q_u64(&state[2]); - uint64x2_t ef = vld1q_u64(&state[4]); - uint64x2_t gh = vld1q_u64(&state[6]); - - /* Save original for final addition */ - uint64x2_t ab_orig = ab; - uint64x2_t cd_orig = cd; - uint64x2_t ef_orig = ef; - uint64x2_t gh_orig = gh; - - /* Load message (already native endian) */ - uint64x2_t m0 = vld1q_u64(&block[0]); - uint64x2_t m1 = vld1q_u64(&block[2]); - uint64x2_t m2 = vld1q_u64(&block[4]); - uint64x2_t m3 = vld1q_u64(&block[6]); - uint64x2_t m4 = vld1q_u64(&block[8]); - uint64x2_t m5 = vld1q_u64(&block[10]); - uint64x2_t m6 = vld1q_u64(&block[12]); - uint64x2_t m7 = vld1q_u64(&block[14]); - - uint64x2_t tmp; - - /* Rounds 0-1 */ - tmp = vaddq_u64(m0, vld1q_u64(&K[0])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(gh, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); - gh = vsha512h2q_u64(tmp, cd, ab); - cd = vaddq_u64(cd, tmp); - m0 = vsha512su1q_u64(vsha512su0q_u64(m0, m1), m7, vextq_u64(m4, m5, 1)); - - /* Rounds 2-3 */ - tmp = vaddq_u64(m1, vld1q_u64(&K[2])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ef, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); - ef = vsha512h2q_u64(tmp, ab, gh); - ab = vaddq_u64(ab, tmp); - m1 = vsha512su1q_u64(vsha512su0q_u64(m1, m2), m0, vextq_u64(m5, m6, 1)); - - /* Rounds 4-5 */ - tmp = vaddq_u64(m2, vld1q_u64(&K[4])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(cd, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); - cd = vsha512h2q_u64(tmp, gh, ef); - gh = vaddq_u64(gh, tmp); - m2 = vsha512su1q_u64(vsha512su0q_u64(m2, m3), m1, vextq_u64(m6, m7, 1)); - - /* Rounds 6-7 */ - tmp = vaddq_u64(m3, vld1q_u64(&K[6])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ab, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); - ab = vsha512h2q_u64(tmp, ef, cd); - ef = vaddq_u64(ef, tmp); - m3 = vsha512su1q_u64(vsha512su0q_u64(m3, m4), m2, vextq_u64(m7, m0, 1)); - - /* Rounds 8-9 */ - tmp = vaddq_u64(m4, vld1q_u64(&K[8])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(gh, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); - gh = vsha512h2q_u64(tmp, cd, ab); - cd = vaddq_u64(cd, tmp); - m4 = vsha512su1q_u64(vsha512su0q_u64(m4, m5), m3, vextq_u64(m0, m1, 1)); - - /* Rounds 10-11 */ - tmp = vaddq_u64(m5, vld1q_u64(&K[10])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ef, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); - ef = vsha512h2q_u64(tmp, ab, gh); - ab = vaddq_u64(ab, tmp); - m5 = vsha512su1q_u64(vsha512su0q_u64(m5, m6), m4, vextq_u64(m1, m2, 1)); - - /* Rounds 12-13 */ - tmp = vaddq_u64(m6, vld1q_u64(&K[12])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(cd, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); - cd = vsha512h2q_u64(tmp, gh, ef); - gh = vaddq_u64(gh, tmp); - m6 = vsha512su1q_u64(vsha512su0q_u64(m6, m7), m5, vextq_u64(m2, m3, 1)); - - /* Rounds 14-15 */ - tmp = vaddq_u64(m7, vld1q_u64(&K[14])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ab, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); - ab = vsha512h2q_u64(tmp, ef, cd); - ef = vaddq_u64(ef, tmp); - m7 = vsha512su1q_u64(vsha512su0q_u64(m7, m0), m6, vextq_u64(m3, m4, 1)); - - /* Rounds 16-17 */ - tmp = vaddq_u64(m0, vld1q_u64(&K[16])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(gh, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); - gh = vsha512h2q_u64(tmp, cd, ab); - cd = vaddq_u64(cd, tmp); - m0 = vsha512su1q_u64(vsha512su0q_u64(m0, m1), m7, vextq_u64(m4, m5, 1)); - - /* Rounds 18-19 */ - tmp = vaddq_u64(m1, vld1q_u64(&K[18])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ef, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); - ef = vsha512h2q_u64(tmp, ab, gh); - ab = vaddq_u64(ab, tmp); - m1 = vsha512su1q_u64(vsha512su0q_u64(m1, m2), m0, vextq_u64(m5, m6, 1)); - - /* Rounds 20-21 */ - tmp = vaddq_u64(m2, vld1q_u64(&K[20])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(cd, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); - cd = vsha512h2q_u64(tmp, gh, ef); - gh = vaddq_u64(gh, tmp); - m2 = vsha512su1q_u64(vsha512su0q_u64(m2, m3), m1, vextq_u64(m6, m7, 1)); - - /* Rounds 22-23 */ - tmp = vaddq_u64(m3, vld1q_u64(&K[22])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ab, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); - ab = vsha512h2q_u64(tmp, ef, cd); - ef = vaddq_u64(ef, tmp); - m3 = vsha512su1q_u64(vsha512su0q_u64(m3, m4), m2, vextq_u64(m7, m0, 1)); - - /* Rounds 24-25 */ - tmp = vaddq_u64(m4, vld1q_u64(&K[24])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(gh, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); - gh = vsha512h2q_u64(tmp, cd, ab); - cd = vaddq_u64(cd, tmp); - m4 = vsha512su1q_u64(vsha512su0q_u64(m4, m5), m3, vextq_u64(m0, m1, 1)); - - /* Rounds 26-27 */ - tmp = vaddq_u64(m5, vld1q_u64(&K[26])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ef, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); - ef = vsha512h2q_u64(tmp, ab, gh); - ab = vaddq_u64(ab, tmp); - m5 = vsha512su1q_u64(vsha512su0q_u64(m5, m6), m4, vextq_u64(m1, m2, 1)); - - /* Rounds 28-29 */ - tmp = vaddq_u64(m6, vld1q_u64(&K[28])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(cd, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); - cd = vsha512h2q_u64(tmp, gh, ef); - gh = vaddq_u64(gh, tmp); - m6 = vsha512su1q_u64(vsha512su0q_u64(m6, m7), m5, vextq_u64(m2, m3, 1)); - - /* Rounds 30-31 */ - tmp = vaddq_u64(m7, vld1q_u64(&K[30])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ab, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); - ab = vsha512h2q_u64(tmp, ef, cd); - ef = vaddq_u64(ef, tmp); - m7 = vsha512su1q_u64(vsha512su0q_u64(m7, m0), m6, vextq_u64(m3, m4, 1)); - - /* Rounds 32-33 */ - tmp = vaddq_u64(m0, vld1q_u64(&K[32])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(gh, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); - gh = vsha512h2q_u64(tmp, cd, ab); - cd = vaddq_u64(cd, tmp); - m0 = vsha512su1q_u64(vsha512su0q_u64(m0, m1), m7, vextq_u64(m4, m5, 1)); - - /* Rounds 34-35 */ - tmp = vaddq_u64(m1, vld1q_u64(&K[34])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ef, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); - ef = vsha512h2q_u64(tmp, ab, gh); - ab = vaddq_u64(ab, tmp); - m1 = vsha512su1q_u64(vsha512su0q_u64(m1, m2), m0, vextq_u64(m5, m6, 1)); - - /* Rounds 36-37 */ - tmp = vaddq_u64(m2, vld1q_u64(&K[36])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(cd, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); - cd = vsha512h2q_u64(tmp, gh, ef); - gh = vaddq_u64(gh, tmp); - m2 = vsha512su1q_u64(vsha512su0q_u64(m2, m3), m1, vextq_u64(m6, m7, 1)); - - /* Rounds 38-39 */ - tmp = vaddq_u64(m3, vld1q_u64(&K[38])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ab, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); - ab = vsha512h2q_u64(tmp, ef, cd); - ef = vaddq_u64(ef, tmp); - m3 = vsha512su1q_u64(vsha512su0q_u64(m3, m4), m2, vextq_u64(m7, m0, 1)); - - /* Rounds 40-41 */ - tmp = vaddq_u64(m4, vld1q_u64(&K[40])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(gh, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); - gh = vsha512h2q_u64(tmp, cd, ab); - cd = vaddq_u64(cd, tmp); - m4 = vsha512su1q_u64(vsha512su0q_u64(m4, m5), m3, vextq_u64(m0, m1, 1)); - - /* Rounds 42-43 */ - tmp = vaddq_u64(m5, vld1q_u64(&K[42])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ef, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); - ef = vsha512h2q_u64(tmp, ab, gh); - ab = vaddq_u64(ab, tmp); - m5 = vsha512su1q_u64(vsha512su0q_u64(m5, m6), m4, vextq_u64(m1, m2, 1)); - - /* Rounds 44-45 */ - tmp = vaddq_u64(m6, vld1q_u64(&K[44])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(cd, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); - cd = vsha512h2q_u64(tmp, gh, ef); - gh = vaddq_u64(gh, tmp); - m6 = vsha512su1q_u64(vsha512su0q_u64(m6, m7), m5, vextq_u64(m2, m3, 1)); - - /* Rounds 46-47 */ - tmp = vaddq_u64(m7, vld1q_u64(&K[46])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ab, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); - ab = vsha512h2q_u64(tmp, ef, cd); - ef = vaddq_u64(ef, tmp); - m7 = vsha512su1q_u64(vsha512su0q_u64(m7, m0), m6, vextq_u64(m3, m4, 1)); - - /* Rounds 48-49 */ - tmp = vaddq_u64(m0, vld1q_u64(&K[48])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(gh, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); - gh = vsha512h2q_u64(tmp, cd, ab); - cd = vaddq_u64(cd, tmp); - m0 = vsha512su1q_u64(vsha512su0q_u64(m0, m1), m7, vextq_u64(m4, m5, 1)); - - /* Rounds 50-51 */ - tmp = vaddq_u64(m1, vld1q_u64(&K[50])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ef, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); - ef = vsha512h2q_u64(tmp, ab, gh); - ab = vaddq_u64(ab, tmp); - m1 = vsha512su1q_u64(vsha512su0q_u64(m1, m2), m0, vextq_u64(m5, m6, 1)); - - /* Rounds 52-53 */ - tmp = vaddq_u64(m2, vld1q_u64(&K[52])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(cd, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); - cd = vsha512h2q_u64(tmp, gh, ef); - gh = vaddq_u64(gh, tmp); - m2 = vsha512su1q_u64(vsha512su0q_u64(m2, m3), m1, vextq_u64(m6, m7, 1)); - - /* Rounds 54-55 */ - tmp = vaddq_u64(m3, vld1q_u64(&K[54])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ab, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); - ab = vsha512h2q_u64(tmp, ef, cd); - ef = vaddq_u64(ef, tmp); - m3 = vsha512su1q_u64(vsha512su0q_u64(m3, m4), m2, vextq_u64(m7, m0, 1)); - - /* Rounds 56-57 */ - tmp = vaddq_u64(m4, vld1q_u64(&K[56])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(gh, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); - gh = vsha512h2q_u64(tmp, cd, ab); - cd = vaddq_u64(cd, tmp); - m4 = vsha512su1q_u64(vsha512su0q_u64(m4, m5), m3, vextq_u64(m0, m1, 1)); - - /* Rounds 58-59 */ - tmp = vaddq_u64(m5, vld1q_u64(&K[58])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ef, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); - ef = vsha512h2q_u64(tmp, ab, gh); - ab = vaddq_u64(ab, tmp); - m5 = vsha512su1q_u64(vsha512su0q_u64(m5, m6), m4, vextq_u64(m1, m2, 1)); - - /* Rounds 60-61 */ - tmp = vaddq_u64(m6, vld1q_u64(&K[60])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(cd, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); - cd = vsha512h2q_u64(tmp, gh, ef); - gh = vaddq_u64(gh, tmp); - m6 = vsha512su1q_u64(vsha512su0q_u64(m6, m7), m5, vextq_u64(m2, m3, 1)); - - /* Rounds 62-63 */ - tmp = vaddq_u64(m7, vld1q_u64(&K[62])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ab, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); - ab = vsha512h2q_u64(tmp, ef, cd); - ef = vaddq_u64(ef, tmp); - m7 = vsha512su1q_u64(vsha512su0q_u64(m7, m0), m6, vextq_u64(m3, m4, 1)); - - /* Rounds 64-65 */ - tmp = vaddq_u64(m0, vld1q_u64(&K[64])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(gh, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); - gh = vsha512h2q_u64(tmp, cd, ab); - cd = vaddq_u64(cd, tmp); - - /* Rounds 66-67 */ - tmp = vaddq_u64(m1, vld1q_u64(&K[66])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ef, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); - ef = vsha512h2q_u64(tmp, ab, gh); - ab = vaddq_u64(ab, tmp); - - /* Rounds 68-69 */ - tmp = vaddq_u64(m2, vld1q_u64(&K[68])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(cd, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); - cd = vsha512h2q_u64(tmp, gh, ef); - gh = vaddq_u64(gh, tmp); - - /* Rounds 70-71 */ - tmp = vaddq_u64(m3, vld1q_u64(&K[70])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ab, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); - ab = vsha512h2q_u64(tmp, ef, cd); - ef = vaddq_u64(ef, tmp); - - /* Rounds 72-73 */ - tmp = vaddq_u64(m4, vld1q_u64(&K[72])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(gh, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); - gh = vsha512h2q_u64(tmp, cd, ab); - cd = vaddq_u64(cd, tmp); - - /* Rounds 74-75 */ - tmp = vaddq_u64(m5, vld1q_u64(&K[74])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ef, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); - ef = vsha512h2q_u64(tmp, ab, gh); - ab = vaddq_u64(ab, tmp); - - /* Rounds 76-77 */ - tmp = vaddq_u64(m6, vld1q_u64(&K[76])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(cd, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); - cd = vsha512h2q_u64(tmp, gh, ef); - gh = vaddq_u64(gh, tmp); - - /* Rounds 78-79 */ - tmp = vaddq_u64(m7, vld1q_u64(&K[78])); - tmp = vextq_u64(tmp, tmp, 1); - tmp = vaddq_u64(ab, tmp); - tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); - ab = vsha512h2q_u64(tmp, ef, cd); - ef = vaddq_u64(ef, tmp); - - /* Add original state back */ - ab = vaddq_u64(ab, ab_orig); - cd = vaddq_u64(cd, cd_orig); - ef = vaddq_u64(ef, ef_orig); - gh = vaddq_u64(gh, gh_orig); - - /* Store result */ - vst1q_u64(&state[0], ab); - vst1q_u64(&state[2], cd); - vst1q_u64(&state[4], ef); - vst1q_u64(&state[6], gh); -} - -#if defined(__APPLE__) -static int sysctl_flag(const char *name) { - int val = 0; - size_t len = sizeof(val); - if (sysctlbyname(name, &val, &len, NULL, 0) != 0) return 0; - return val != 0; -} -#endif - -/* - * Return 1 if the CPU implements the ARM SHA512 extension, 0 - * otherwise. We only know how to ask Darwin and Linux, and report 0 - * (selecting the pure Haskell fallback) on any other OS. - */ -int sha512_arm_available(void) { -#if defined(__APPLE__) - return sysctl_flag("hw.optional.arm.FEAT_SHA512") - || sysctl_flag("hw.optional.armv8_2_sha512"); -#elif defined(__linux__) - return (getauxval(AT_HWCAP) & HWCAP_SHA512) != 0; -#else - return 0; -#endif -} - -#else - -/* Stub implementations for non-aarch64 builds */ -void sha512_block_arm(uint64_t *state, const uint64_t *block) { - (void)state; - (void)block; - /* Should never be called - use pure Haskell fallback */ -} - -int sha512_arm_available(void) { - return 0; -} - -#endif diff --git a/cbits/sha512_native.c b/cbits/sha512_native.c @@ -0,0 +1,500 @@ +#include <stdint.h> +#include <string.h> + +#if defined(__aarch64__) + +#include <arm_neon.h> + +#if defined(__APPLE__) +#include <sys/sysctl.h> +#elif defined(__linux__) +#include <sys/auxv.h> +#ifndef HWCAP_SHA512 +#define HWCAP_SHA512 (1UL << 21) +#endif +#endif + +static const uint64_t K[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +/* + * Process one 128-byte block using ARM SHA512 crypto instructions. + * + * state: pointer to 8 uint64_t words (a,b,c,d,e,f,g,h) + * block: pointer to 16 uint64_t words (already native endian) + * + * The state is updated in place. + * + * Compiled for the SHA512 extension only; callers must first check + * 'sha512_native_available'. + */ +__attribute__((target("+sha3"))) +void sha512_block_native(uint64_t *state, const uint64_t *block) { + /* Load current hash state */ + uint64x2_t ab = vld1q_u64(&state[0]); + uint64x2_t cd = vld1q_u64(&state[2]); + uint64x2_t ef = vld1q_u64(&state[4]); + uint64x2_t gh = vld1q_u64(&state[6]); + + /* Save original for final addition */ + uint64x2_t ab_orig = ab; + uint64x2_t cd_orig = cd; + uint64x2_t ef_orig = ef; + uint64x2_t gh_orig = gh; + + /* Load message (already native endian) */ + uint64x2_t m0 = vld1q_u64(&block[0]); + uint64x2_t m1 = vld1q_u64(&block[2]); + uint64x2_t m2 = vld1q_u64(&block[4]); + uint64x2_t m3 = vld1q_u64(&block[6]); + uint64x2_t m4 = vld1q_u64(&block[8]); + uint64x2_t m5 = vld1q_u64(&block[10]); + uint64x2_t m6 = vld1q_u64(&block[12]); + uint64x2_t m7 = vld1q_u64(&block[14]); + + uint64x2_t tmp; + + /* Rounds 0-1 */ + tmp = vaddq_u64(m0, vld1q_u64(&K[0])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(gh, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); + gh = vsha512h2q_u64(tmp, cd, ab); + cd = vaddq_u64(cd, tmp); + m0 = vsha512su1q_u64(vsha512su0q_u64(m0, m1), m7, vextq_u64(m4, m5, 1)); + + /* Rounds 2-3 */ + tmp = vaddq_u64(m1, vld1q_u64(&K[2])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ef, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); + ef = vsha512h2q_u64(tmp, ab, gh); + ab = vaddq_u64(ab, tmp); + m1 = vsha512su1q_u64(vsha512su0q_u64(m1, m2), m0, vextq_u64(m5, m6, 1)); + + /* Rounds 4-5 */ + tmp = vaddq_u64(m2, vld1q_u64(&K[4])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(cd, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); + cd = vsha512h2q_u64(tmp, gh, ef); + gh = vaddq_u64(gh, tmp); + m2 = vsha512su1q_u64(vsha512su0q_u64(m2, m3), m1, vextq_u64(m6, m7, 1)); + + /* Rounds 6-7 */ + tmp = vaddq_u64(m3, vld1q_u64(&K[6])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ab, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); + ab = vsha512h2q_u64(tmp, ef, cd); + ef = vaddq_u64(ef, tmp); + m3 = vsha512su1q_u64(vsha512su0q_u64(m3, m4), m2, vextq_u64(m7, m0, 1)); + + /* Rounds 8-9 */ + tmp = vaddq_u64(m4, vld1q_u64(&K[8])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(gh, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); + gh = vsha512h2q_u64(tmp, cd, ab); + cd = vaddq_u64(cd, tmp); + m4 = vsha512su1q_u64(vsha512su0q_u64(m4, m5), m3, vextq_u64(m0, m1, 1)); + + /* Rounds 10-11 */ + tmp = vaddq_u64(m5, vld1q_u64(&K[10])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ef, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); + ef = vsha512h2q_u64(tmp, ab, gh); + ab = vaddq_u64(ab, tmp); + m5 = vsha512su1q_u64(vsha512su0q_u64(m5, m6), m4, vextq_u64(m1, m2, 1)); + + /* Rounds 12-13 */ + tmp = vaddq_u64(m6, vld1q_u64(&K[12])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(cd, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); + cd = vsha512h2q_u64(tmp, gh, ef); + gh = vaddq_u64(gh, tmp); + m6 = vsha512su1q_u64(vsha512su0q_u64(m6, m7), m5, vextq_u64(m2, m3, 1)); + + /* Rounds 14-15 */ + tmp = vaddq_u64(m7, vld1q_u64(&K[14])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ab, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); + ab = vsha512h2q_u64(tmp, ef, cd); + ef = vaddq_u64(ef, tmp); + m7 = vsha512su1q_u64(vsha512su0q_u64(m7, m0), m6, vextq_u64(m3, m4, 1)); + + /* Rounds 16-17 */ + tmp = vaddq_u64(m0, vld1q_u64(&K[16])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(gh, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); + gh = vsha512h2q_u64(tmp, cd, ab); + cd = vaddq_u64(cd, tmp); + m0 = vsha512su1q_u64(vsha512su0q_u64(m0, m1), m7, vextq_u64(m4, m5, 1)); + + /* Rounds 18-19 */ + tmp = vaddq_u64(m1, vld1q_u64(&K[18])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ef, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); + ef = vsha512h2q_u64(tmp, ab, gh); + ab = vaddq_u64(ab, tmp); + m1 = vsha512su1q_u64(vsha512su0q_u64(m1, m2), m0, vextq_u64(m5, m6, 1)); + + /* Rounds 20-21 */ + tmp = vaddq_u64(m2, vld1q_u64(&K[20])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(cd, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); + cd = vsha512h2q_u64(tmp, gh, ef); + gh = vaddq_u64(gh, tmp); + m2 = vsha512su1q_u64(vsha512su0q_u64(m2, m3), m1, vextq_u64(m6, m7, 1)); + + /* Rounds 22-23 */ + tmp = vaddq_u64(m3, vld1q_u64(&K[22])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ab, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); + ab = vsha512h2q_u64(tmp, ef, cd); + ef = vaddq_u64(ef, tmp); + m3 = vsha512su1q_u64(vsha512su0q_u64(m3, m4), m2, vextq_u64(m7, m0, 1)); + + /* Rounds 24-25 */ + tmp = vaddq_u64(m4, vld1q_u64(&K[24])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(gh, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); + gh = vsha512h2q_u64(tmp, cd, ab); + cd = vaddq_u64(cd, tmp); + m4 = vsha512su1q_u64(vsha512su0q_u64(m4, m5), m3, vextq_u64(m0, m1, 1)); + + /* Rounds 26-27 */ + tmp = vaddq_u64(m5, vld1q_u64(&K[26])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ef, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); + ef = vsha512h2q_u64(tmp, ab, gh); + ab = vaddq_u64(ab, tmp); + m5 = vsha512su1q_u64(vsha512su0q_u64(m5, m6), m4, vextq_u64(m1, m2, 1)); + + /* Rounds 28-29 */ + tmp = vaddq_u64(m6, vld1q_u64(&K[28])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(cd, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); + cd = vsha512h2q_u64(tmp, gh, ef); + gh = vaddq_u64(gh, tmp); + m6 = vsha512su1q_u64(vsha512su0q_u64(m6, m7), m5, vextq_u64(m2, m3, 1)); + + /* Rounds 30-31 */ + tmp = vaddq_u64(m7, vld1q_u64(&K[30])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ab, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); + ab = vsha512h2q_u64(tmp, ef, cd); + ef = vaddq_u64(ef, tmp); + m7 = vsha512su1q_u64(vsha512su0q_u64(m7, m0), m6, vextq_u64(m3, m4, 1)); + + /* Rounds 32-33 */ + tmp = vaddq_u64(m0, vld1q_u64(&K[32])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(gh, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); + gh = vsha512h2q_u64(tmp, cd, ab); + cd = vaddq_u64(cd, tmp); + m0 = vsha512su1q_u64(vsha512su0q_u64(m0, m1), m7, vextq_u64(m4, m5, 1)); + + /* Rounds 34-35 */ + tmp = vaddq_u64(m1, vld1q_u64(&K[34])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ef, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); + ef = vsha512h2q_u64(tmp, ab, gh); + ab = vaddq_u64(ab, tmp); + m1 = vsha512su1q_u64(vsha512su0q_u64(m1, m2), m0, vextq_u64(m5, m6, 1)); + + /* Rounds 36-37 */ + tmp = vaddq_u64(m2, vld1q_u64(&K[36])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(cd, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); + cd = vsha512h2q_u64(tmp, gh, ef); + gh = vaddq_u64(gh, tmp); + m2 = vsha512su1q_u64(vsha512su0q_u64(m2, m3), m1, vextq_u64(m6, m7, 1)); + + /* Rounds 38-39 */ + tmp = vaddq_u64(m3, vld1q_u64(&K[38])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ab, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); + ab = vsha512h2q_u64(tmp, ef, cd); + ef = vaddq_u64(ef, tmp); + m3 = vsha512su1q_u64(vsha512su0q_u64(m3, m4), m2, vextq_u64(m7, m0, 1)); + + /* Rounds 40-41 */ + tmp = vaddq_u64(m4, vld1q_u64(&K[40])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(gh, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); + gh = vsha512h2q_u64(tmp, cd, ab); + cd = vaddq_u64(cd, tmp); + m4 = vsha512su1q_u64(vsha512su0q_u64(m4, m5), m3, vextq_u64(m0, m1, 1)); + + /* Rounds 42-43 */ + tmp = vaddq_u64(m5, vld1q_u64(&K[42])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ef, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); + ef = vsha512h2q_u64(tmp, ab, gh); + ab = vaddq_u64(ab, tmp); + m5 = vsha512su1q_u64(vsha512su0q_u64(m5, m6), m4, vextq_u64(m1, m2, 1)); + + /* Rounds 44-45 */ + tmp = vaddq_u64(m6, vld1q_u64(&K[44])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(cd, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); + cd = vsha512h2q_u64(tmp, gh, ef); + gh = vaddq_u64(gh, tmp); + m6 = vsha512su1q_u64(vsha512su0q_u64(m6, m7), m5, vextq_u64(m2, m3, 1)); + + /* Rounds 46-47 */ + tmp = vaddq_u64(m7, vld1q_u64(&K[46])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ab, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); + ab = vsha512h2q_u64(tmp, ef, cd); + ef = vaddq_u64(ef, tmp); + m7 = vsha512su1q_u64(vsha512su0q_u64(m7, m0), m6, vextq_u64(m3, m4, 1)); + + /* Rounds 48-49 */ + tmp = vaddq_u64(m0, vld1q_u64(&K[48])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(gh, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); + gh = vsha512h2q_u64(tmp, cd, ab); + cd = vaddq_u64(cd, tmp); + m0 = vsha512su1q_u64(vsha512su0q_u64(m0, m1), m7, vextq_u64(m4, m5, 1)); + + /* Rounds 50-51 */ + tmp = vaddq_u64(m1, vld1q_u64(&K[50])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ef, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); + ef = vsha512h2q_u64(tmp, ab, gh); + ab = vaddq_u64(ab, tmp); + m1 = vsha512su1q_u64(vsha512su0q_u64(m1, m2), m0, vextq_u64(m5, m6, 1)); + + /* Rounds 52-53 */ + tmp = vaddq_u64(m2, vld1q_u64(&K[52])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(cd, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); + cd = vsha512h2q_u64(tmp, gh, ef); + gh = vaddq_u64(gh, tmp); + m2 = vsha512su1q_u64(vsha512su0q_u64(m2, m3), m1, vextq_u64(m6, m7, 1)); + + /* Rounds 54-55 */ + tmp = vaddq_u64(m3, vld1q_u64(&K[54])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ab, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); + ab = vsha512h2q_u64(tmp, ef, cd); + ef = vaddq_u64(ef, tmp); + m3 = vsha512su1q_u64(vsha512su0q_u64(m3, m4), m2, vextq_u64(m7, m0, 1)); + + /* Rounds 56-57 */ + tmp = vaddq_u64(m4, vld1q_u64(&K[56])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(gh, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); + gh = vsha512h2q_u64(tmp, cd, ab); + cd = vaddq_u64(cd, tmp); + m4 = vsha512su1q_u64(vsha512su0q_u64(m4, m5), m3, vextq_u64(m0, m1, 1)); + + /* Rounds 58-59 */ + tmp = vaddq_u64(m5, vld1q_u64(&K[58])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ef, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); + ef = vsha512h2q_u64(tmp, ab, gh); + ab = vaddq_u64(ab, tmp); + m5 = vsha512su1q_u64(vsha512su0q_u64(m5, m6), m4, vextq_u64(m1, m2, 1)); + + /* Rounds 60-61 */ + tmp = vaddq_u64(m6, vld1q_u64(&K[60])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(cd, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); + cd = vsha512h2q_u64(tmp, gh, ef); + gh = vaddq_u64(gh, tmp); + m6 = vsha512su1q_u64(vsha512su0q_u64(m6, m7), m5, vextq_u64(m2, m3, 1)); + + /* Rounds 62-63 */ + tmp = vaddq_u64(m7, vld1q_u64(&K[62])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ab, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); + ab = vsha512h2q_u64(tmp, ef, cd); + ef = vaddq_u64(ef, tmp); + m7 = vsha512su1q_u64(vsha512su0q_u64(m7, m0), m6, vextq_u64(m3, m4, 1)); + + /* Rounds 64-65 */ + tmp = vaddq_u64(m0, vld1q_u64(&K[64])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(gh, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); + gh = vsha512h2q_u64(tmp, cd, ab); + cd = vaddq_u64(cd, tmp); + + /* Rounds 66-67 */ + tmp = vaddq_u64(m1, vld1q_u64(&K[66])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ef, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); + ef = vsha512h2q_u64(tmp, ab, gh); + ab = vaddq_u64(ab, tmp); + + /* Rounds 68-69 */ + tmp = vaddq_u64(m2, vld1q_u64(&K[68])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(cd, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); + cd = vsha512h2q_u64(tmp, gh, ef); + gh = vaddq_u64(gh, tmp); + + /* Rounds 70-71 */ + tmp = vaddq_u64(m3, vld1q_u64(&K[70])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ab, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); + ab = vsha512h2q_u64(tmp, ef, cd); + ef = vaddq_u64(ef, tmp); + + /* Rounds 72-73 */ + tmp = vaddq_u64(m4, vld1q_u64(&K[72])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(gh, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); + gh = vsha512h2q_u64(tmp, cd, ab); + cd = vaddq_u64(cd, tmp); + + /* Rounds 74-75 */ + tmp = vaddq_u64(m5, vld1q_u64(&K[74])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ef, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); + ef = vsha512h2q_u64(tmp, ab, gh); + ab = vaddq_u64(ab, tmp); + + /* Rounds 76-77 */ + tmp = vaddq_u64(m6, vld1q_u64(&K[76])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(cd, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); + cd = vsha512h2q_u64(tmp, gh, ef); + gh = vaddq_u64(gh, tmp); + + /* Rounds 78-79 */ + tmp = vaddq_u64(m7, vld1q_u64(&K[78])); + tmp = vextq_u64(tmp, tmp, 1); + tmp = vaddq_u64(ab, tmp); + tmp = vsha512hq_u64(tmp, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); + ab = vsha512h2q_u64(tmp, ef, cd); + ef = vaddq_u64(ef, tmp); + + /* Add original state back */ + ab = vaddq_u64(ab, ab_orig); + cd = vaddq_u64(cd, cd_orig); + ef = vaddq_u64(ef, ef_orig); + gh = vaddq_u64(gh, gh_orig); + + /* Store result */ + vst1q_u64(&state[0], ab); + vst1q_u64(&state[2], cd); + vst1q_u64(&state[4], ef); + vst1q_u64(&state[6], gh); +} + +#if defined(__APPLE__) +static int sysctl_flag(const char *name) { + int val = 0; + size_t len = sizeof(val); + if (sysctlbyname(name, &val, &len, NULL, 0) != 0) return 0; + return val != 0; +} +#endif + +/* + * Return 1 if the CPU implements the ARM SHA512 extension, 0 + * otherwise. We only know how to ask Darwin and Linux, and report 0 + * (selecting the pure Haskell fallback) on any other OS. + */ +int sha512_native_available(void) { +#if defined(__APPLE__) + return sysctl_flag("hw.optional.arm.FEAT_SHA512") + || sysctl_flag("hw.optional.armv8_2_sha512"); +#elif defined(__linux__) + return (getauxval(AT_HWCAP) & HWCAP_SHA512) != 0; +#else + return 0; +#endif +} + +#else + +/* Stub implementations for non-aarch64 builds */ +void sha512_block_native(uint64_t *state, const uint64_t *block) { + (void)state; + (void)block; + /* Should never be called - use pure Haskell fallback */ +} + +int sha512_native_available(void) { + return 0; +} + +#endif diff --git a/lib/Crypto/Hash/SHA512.hs b/lib/Crypto/Hash/SHA512.hs @@ -33,7 +33,7 @@ module Crypto.Hash.SHA512 ( import qualified Data.ByteString as BS import Data.Word (Word8, Word64) import Foreign.Ptr (Ptr) -import qualified Crypto.Hash.SHA512.Arm as Arm +import qualified Crypto.Hash.SHA512.Native as Native import Crypto.Hash.SHA512.Internal (MAC(..), Registers) import qualified Crypto.Hash.SHA512.Lazy as Lazy import qualified Crypto.Hash.SHA512.Pure as Pure @@ -49,7 +49,7 @@ import qualified Crypto.Hash.SHA512.Pure as Pure -- "<strict 512-bit message digest>" hash :: BS.ByteString -> BS.ByteString hash m - | Arm.sha512_arm_available = Arm.hash m + | Native.sha512_native_available = Native.hash m | otherwise = Pure.hash m {-# INLINABLE hash #-} @@ -67,7 +67,7 @@ hash m -- "<strict 512-bit MAC>" hmac :: BS.ByteString -> BS.ByteString -> MAC hmac k m - | Arm.sha512_arm_available = MAC (Arm.hmac k m) + | Native.sha512_native_available = MAC (Native.hmac k m) | otherwise = MAC (Pure.hmac k m) {-# INLINABLE hmac #-} @@ -84,7 +84,7 @@ _hmac_rr -> Registers -- ^ message -> IO () _hmac_rr rp bp k m - | Arm.sha512_arm_available = Arm._hmac_rr rp bp k m + | Native.sha512_native_available = Native._hmac_rr rp bp k m | otherwise = Pure._hmac_rr rp bp k m {-# INLINABLE _hmac_rr #-} @@ -102,6 +102,6 @@ _hmac_rsb -> BS.ByteString -- ^ data -> IO () _hmac_rsb rp bp k v sep dat - | Arm.sha512_arm_available = Arm._hmac_rsb rp bp k v sep dat + | Native.sha512_native_available = Native._hmac_rsb rp bp k v sep dat | otherwise = Pure._hmac_rsb rp bp k v sep dat {-# INLINABLE _hmac_rsb #-} diff --git a/lib/Crypto/Hash/SHA512/Arm.hs b/lib/Crypto/Hash/SHA512/Arm.hs @@ -1,268 +0,0 @@ -{-# OPTIONS_HADDOCK hide #-} -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE MagicHash #-} -{-# LANGUAGE PatternSynonyms #-} -{-# LANGUAGE UnboxedTuples #-} - --- | --- Module: Crypto.Hash.SHA512.Arm --- Copyright: (c) 2024 Jared Tobin --- License: MIT --- Maintainer: Jared Tobin <jared@ppad.tech> --- --- ARM crypto extension support for SHA-512. - -module Crypto.Hash.SHA512.Arm ( - sha512_arm_available - , hash - , hmac - , _hmac_rr - , _hmac_rsb - ) where - -import qualified Data.ByteString as BS -import qualified Data.ByteString.Internal as BI -import qualified Data.ByteString.Unsafe as BU -import Data.Word (Word8, Word64) -import Foreign.C.Types (CInt(..)) -import Foreign.Marshal.Alloc (allocaBytes) -import Foreign.Ptr (Ptr) -import qualified GHC.Exts as Exts -import qualified GHC.IO (IO(..)) -import qualified GHC.Ptr -import Crypto.Hash.SHA512.Internal hiding (update) -import System.IO.Unsafe (unsafeDupablePerformIO) - --- ffi ------------------------------------------------------------------------ - -foreign import ccall unsafe "sha512_block_arm" - c_sha512_block :: Ptr Word64 -> Ptr Word64 -> IO () - -foreign import ccall unsafe "sha512_arm_available" - c_sha512_arm_available :: IO CInt - --- utilities ------------------------------------------------------------------ - -fi :: (Integral a, Num b) => a -> b -fi = fromIntegral -{-# INLINE fi #-} - -peek_registers - :: Ptr Word64 - -> Registers -peek_registers (GHC.Ptr.Ptr addr) = R - (Exts.indexWord64OffAddr# addr 0#) - (Exts.indexWord64OffAddr# addr 1#) - (Exts.indexWord64OffAddr# addr 2#) - (Exts.indexWord64OffAddr# addr 3#) - (Exts.indexWord64OffAddr# addr 4#) - (Exts.indexWord64OffAddr# addr 5#) - (Exts.indexWord64OffAddr# addr 6#) - (Exts.indexWord64OffAddr# addr 7#) -{-# INLINE peek_registers #-} - -poke_block :: Ptr Word64 -> Block -> IO () -poke_block - (GHC.Ptr.Ptr addr) - (B w00 w01 w02 w03 w04 w05 w06 w07 w08 w09 w10 w11 w12 w13 w14 w15) - = GHC.IO.IO $ \s00 -> - case Exts.writeWord64OffAddr# addr 00# w00 s00 of { s01 -> - case Exts.writeWord64OffAddr# addr 01# w01 s01 of { s02 -> - case Exts.writeWord64OffAddr# addr 02# w02 s02 of { s03 -> - case Exts.writeWord64OffAddr# addr 03# w03 s03 of { s04 -> - case Exts.writeWord64OffAddr# addr 04# w04 s04 of { s05 -> - case Exts.writeWord64OffAddr# addr 05# w05 s05 of { s06 -> - case Exts.writeWord64OffAddr# addr 06# w06 s06 of { s07 -> - case Exts.writeWord64OffAddr# addr 07# w07 s07 of { s08 -> - case Exts.writeWord64OffAddr# addr 08# w08 s08 of { s09 -> - case Exts.writeWord64OffAddr# addr 09# w09 s09 of { s10 -> - case Exts.writeWord64OffAddr# addr 10# w10 s10 of { s11 -> - case Exts.writeWord64OffAddr# addr 11# w11 s11 of { s12 -> - case Exts.writeWord64OffAddr# addr 12# w12 s12 of { s13 -> - case Exts.writeWord64OffAddr# addr 13# w13 s13 of { s14 -> - case Exts.writeWord64OffAddr# addr 14# w14 s14 of { s15 -> - case Exts.writeWord64OffAddr# addr 15# w15 s15 of { s16 -> - (# s16, () #) }}}}}}}}}}}}}}}} -{-# INLINE poke_block #-} - --- update --------------------------------------------------------------------- - -update :: Ptr Word64 -> Ptr Word64 -> Block -> IO () -update rp bp block = do - poke_block bp block - c_sha512_block rp bp -{-# INLINE update #-} - --- api ----------------------------------------------------------------------- - --- | Are ARM +sha512 extensions available? -sha512_arm_available :: Bool -sha512_arm_available = unsafeDupablePerformIO c_sha512_arm_available /= 0 -{-# NOINLINE sha512_arm_available #-} - -hash - :: BS.ByteString - -> BS.ByteString -hash m = unsafeDupablePerformIO $ - allocaBytes 64 $ \rp -> - allocaBytes 128 $ \bp -> do - poke_registers rp (iv ()) - _hash rp bp 0 m - let !rs = peek_registers rp - pure (cat rs) - -_hash - :: Ptr Word64 -- ^ register state - -> Ptr Word64 -- ^ block state - -> Word64 -- ^ extra prefix length, for padding calculation - -> BS.ByteString -- ^ input - -> IO () -_hash rp bp el m@(BI.PS _ _ l) = do - hash_blocks rp bp m - let !fin@(BI.PS _ _ ll) = BU.unsafeDrop (l - l `rem` 128) m - !total = el + fi l - if ll < 112 - then do - let !ult = parse_pad1 fin total - update rp bp ult - else do - let !(# pen, ult #) = parse_pad2 fin total - update rp bp pen - update rp bp ult -{-# INLINABLE _hash #-} - -hash_blocks - :: Ptr Word64 -- ^ register state - -> Ptr Word64 -- ^ block state - -> BS.ByteString -- ^ input - -> IO () -hash_blocks rp bp m@(BI.PS _ _ l) = loop 0 where - loop !j - | j + 128 > l = pure () - | otherwise = do - let !block = parse m j - update rp bp block - loop (j + 128) -{-# INLINE hash_blocks #-} - --- hmac ----------------------------------------------------------------------- - -hmac :: BS.ByteString -> BS.ByteString -> BS.ByteString -hmac k m = unsafeDupablePerformIO $ - allocaBytes 64 $ \rp -> - allocaBytes 128 $ \bp -> do - _hmac rp bp (prep_key k) m - pure (cat (peek_registers rp)) - -prep_key :: BS.ByteString -> Block -prep_key k@(BI.PS _ _ l) - | l > 128 = parse_key (hash k) - | otherwise = parse_key k -{-# INLINABLE prep_key #-} - --- assume padded key as block. -_hmac - :: Ptr Word64 -- ^ register state - -> Ptr Word64 -- ^ block state - -> Block -- ^ padded key - -> BS.ByteString -- ^ message - -> IO () -_hmac rp bp k m = do - poke_registers rp (iv ()) - update rp bp (xor k (Exts.wordToWord64# 0x3636363636363636##)) - _hash rp bp 128 m - let !block = pad_registers_with_length (peek_registers rp) - poke_registers rp (iv ()) - update rp bp (xor k (Exts.wordToWord64# 0x5C5C5C5C5C5C5C5C##)) - update rp bp block -{-# NOINLINE _hmac #-} - -_hmac_rr - :: Ptr Word64 -- ^ register state - -> Ptr Word64 -- ^ block state - -> Registers -- ^ key - -> Registers -- ^ message - -> IO () -_hmac_rr rp bp k m = do - let !key = pad_registers k - !block = pad_registers_with_length m - _hmac_bb rp bp key block -{-# INLINABLE _hmac_rr #-} - -_hmac_bb - :: Ptr Word64 -- ^ register state - -> Ptr Word64 -- ^ block state - -> Block -- ^ padded key - -> Block -- ^ padded message - -> IO () -_hmac_bb rp bp k m = do - poke_registers rp (iv ()) - update rp bp (xor k (Exts.wordToWord64# 0x3636363636363636##)) - update rp bp m - let !inner = pad_registers_with_length (peek_registers rp) - poke_registers rp (iv ()) - update rp bp (xor k (Exts.wordToWord64# 0x5C5C5C5C5C5C5C5C##)) - update rp bp inner -{-# INLINABLE _hmac_bb #-} - --- | HMAC(key, v || sep || data) using ARM crypto extensions. --- Writes result to destination pointer. -_hmac_rsb - :: Ptr Word64 -- ^ destination (8 Word64s) - -> Ptr Word64 -- ^ scratch block buffer (16 Word64s) - -> Registers -- ^ key - -> Registers -- ^ v - -> Word8 -- ^ separator byte - -> BS.ByteString -- ^ data - -> IO () -_hmac_rsb rp bp k v sep dat = do - poke_registers rp (iv ()) - let !key = pad_registers k - update rp bp (xor key (Exts.wordToWord64# 0x3636363636363636##)) - _hash_vsb rp bp 128 v sep dat - let !inner = pad_registers_with_length (peek_registers rp) - poke_registers rp (iv ()) - update rp bp (xor key (Exts.wordToWord64# 0x5C5C5C5C5C5C5C5C##)) - update rp bp inner -{-# INLINABLE _hmac_rsb #-} - --- | Hash (v || sep || dat) with ARM crypto extensions. --- Assumes register state already initialized at rp. -_hash_vsb - :: Ptr Word64 -- ^ register state - -> Ptr Word64 -- ^ block buffer - -> Word64 -- ^ extra prefix length - -> Registers -- ^ v - -> Word8 -- ^ sep - -> BS.ByteString -- ^ dat - -> IO () -_hash_vsb rp bp el v sep dat@(BI.PS _ _ l) - | l >= 63 = do - -- first block is complete: v || sep || dat[0:63] - let !b0 = parse_vsb v sep dat - update rp bp b0 - -- hash remaining complete blocks from dat[63:] - let !rest = BU.unsafeDrop 63 dat - !restLen = l - 63 - hash_blocks rp bp rest - -- handle final padding - let !finLen = restLen `rem` 128 - !fin = BU.unsafeDrop (restLen - finLen) rest - !total = el + 65 + fi l - if finLen < 112 - then update rp bp (parse_pad1 fin total) - else do - let !(# pen, ult #) = parse_pad2 fin total - update rp bp pen - update rp bp ult - | otherwise = do - -- message < 128 bytes total, straight to padding - let !total = el + 65 + fi l - if 65 + l < 112 - then update rp bp (parse_pad1_vsb v sep dat total) - else do - let !(# pen, ult #) = parse_pad2_vsb v sep dat total - update rp bp pen - update rp bp ult -{-# INLINABLE _hash_vsb #-} - diff --git a/lib/Crypto/Hash/SHA512/Native.hs b/lib/Crypto/Hash/SHA512/Native.hs @@ -0,0 +1,268 @@ +{-# OPTIONS_HADDOCK hide #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE UnboxedTuples #-} + +-- | +-- Module: Crypto.Hash.SHA512.Native +-- Copyright: (c) 2024 Jared Tobin +-- License: MIT +-- Maintainer: Jared Tobin <jared@ppad.tech> +-- +-- ARM crypto extension support for SHA-512. + +module Crypto.Hash.SHA512.Native ( + sha512_native_available + , hash + , hmac + , _hmac_rr + , _hmac_rsb + ) where + +import qualified Data.ByteString as BS +import qualified Data.ByteString.Internal as BI +import qualified Data.ByteString.Unsafe as BU +import Data.Word (Word8, Word64) +import Foreign.C.Types (CInt(..)) +import Foreign.Marshal.Alloc (allocaBytes) +import Foreign.Ptr (Ptr) +import qualified GHC.Exts as Exts +import qualified GHC.IO (IO(..)) +import qualified GHC.Ptr +import Crypto.Hash.SHA512.Internal hiding (update) +import System.IO.Unsafe (unsafeDupablePerformIO) + +-- ffi ------------------------------------------------------------------------ + +foreign import ccall unsafe "sha512_block_native" + c_sha512_block :: Ptr Word64 -> Ptr Word64 -> IO () + +foreign import ccall unsafe "sha512_native_available" + c_sha512_native_available :: IO CInt + +-- utilities ------------------------------------------------------------------ + +fi :: (Integral a, Num b) => a -> b +fi = fromIntegral +{-# INLINE fi #-} + +peek_registers + :: Ptr Word64 + -> Registers +peek_registers (GHC.Ptr.Ptr addr) = R + (Exts.indexWord64OffAddr# addr 0#) + (Exts.indexWord64OffAddr# addr 1#) + (Exts.indexWord64OffAddr# addr 2#) + (Exts.indexWord64OffAddr# addr 3#) + (Exts.indexWord64OffAddr# addr 4#) + (Exts.indexWord64OffAddr# addr 5#) + (Exts.indexWord64OffAddr# addr 6#) + (Exts.indexWord64OffAddr# addr 7#) +{-# INLINE peek_registers #-} + +poke_block :: Ptr Word64 -> Block -> IO () +poke_block + (GHC.Ptr.Ptr addr) + (B w00 w01 w02 w03 w04 w05 w06 w07 w08 w09 w10 w11 w12 w13 w14 w15) + = GHC.IO.IO $ \s00 -> + case Exts.writeWord64OffAddr# addr 00# w00 s00 of { s01 -> + case Exts.writeWord64OffAddr# addr 01# w01 s01 of { s02 -> + case Exts.writeWord64OffAddr# addr 02# w02 s02 of { s03 -> + case Exts.writeWord64OffAddr# addr 03# w03 s03 of { s04 -> + case Exts.writeWord64OffAddr# addr 04# w04 s04 of { s05 -> + case Exts.writeWord64OffAddr# addr 05# w05 s05 of { s06 -> + case Exts.writeWord64OffAddr# addr 06# w06 s06 of { s07 -> + case Exts.writeWord64OffAddr# addr 07# w07 s07 of { s08 -> + case Exts.writeWord64OffAddr# addr 08# w08 s08 of { s09 -> + case Exts.writeWord64OffAddr# addr 09# w09 s09 of { s10 -> + case Exts.writeWord64OffAddr# addr 10# w10 s10 of { s11 -> + case Exts.writeWord64OffAddr# addr 11# w11 s11 of { s12 -> + case Exts.writeWord64OffAddr# addr 12# w12 s12 of { s13 -> + case Exts.writeWord64OffAddr# addr 13# w13 s13 of { s14 -> + case Exts.writeWord64OffAddr# addr 14# w14 s14 of { s15 -> + case Exts.writeWord64OffAddr# addr 15# w15 s15 of { s16 -> + (# s16, () #) }}}}}}}}}}}}}}}} +{-# INLINE poke_block #-} + +-- update --------------------------------------------------------------------- + +update :: Ptr Word64 -> Ptr Word64 -> Block -> IO () +update rp bp block = do + poke_block bp block + c_sha512_block rp bp +{-# INLINE update #-} + +-- api ----------------------------------------------------------------------- + +-- | Are hardware SHA-512 instructions available? +sha512_native_available :: Bool +sha512_native_available = unsafeDupablePerformIO c_sha512_native_available /= 0 +{-# NOINLINE sha512_native_available #-} + +hash + :: BS.ByteString + -> BS.ByteString +hash m = unsafeDupablePerformIO $ + allocaBytes 64 $ \rp -> + allocaBytes 128 $ \bp -> do + poke_registers rp (iv ()) + _hash rp bp 0 m + let !rs = peek_registers rp + pure (cat rs) + +_hash + :: Ptr Word64 -- ^ register state + -> Ptr Word64 -- ^ block state + -> Word64 -- ^ extra prefix length, for padding calculation + -> BS.ByteString -- ^ input + -> IO () +_hash rp bp el m@(BI.PS _ _ l) = do + hash_blocks rp bp m + let !fin@(BI.PS _ _ ll) = BU.unsafeDrop (l - l `rem` 128) m + !total = el + fi l + if ll < 112 + then do + let !ult = parse_pad1 fin total + update rp bp ult + else do + let !(# pen, ult #) = parse_pad2 fin total + update rp bp pen + update rp bp ult +{-# INLINABLE _hash #-} + +hash_blocks + :: Ptr Word64 -- ^ register state + -> Ptr Word64 -- ^ block state + -> BS.ByteString -- ^ input + -> IO () +hash_blocks rp bp m@(BI.PS _ _ l) = loop 0 where + loop !j + | j + 128 > l = pure () + | otherwise = do + let !block = parse m j + update rp bp block + loop (j + 128) +{-# INLINE hash_blocks #-} + +-- hmac ----------------------------------------------------------------------- + +hmac :: BS.ByteString -> BS.ByteString -> BS.ByteString +hmac k m = unsafeDupablePerformIO $ + allocaBytes 64 $ \rp -> + allocaBytes 128 $ \bp -> do + _hmac rp bp (prep_key k) m + pure (cat (peek_registers rp)) + +prep_key :: BS.ByteString -> Block +prep_key k@(BI.PS _ _ l) + | l > 128 = parse_key (hash k) + | otherwise = parse_key k +{-# INLINABLE prep_key #-} + +-- assume padded key as block. +_hmac + :: Ptr Word64 -- ^ register state + -> Ptr Word64 -- ^ block state + -> Block -- ^ padded key + -> BS.ByteString -- ^ message + -> IO () +_hmac rp bp k m = do + poke_registers rp (iv ()) + update rp bp (xor k (Exts.wordToWord64# 0x3636363636363636##)) + _hash rp bp 128 m + let !block = pad_registers_with_length (peek_registers rp) + poke_registers rp (iv ()) + update rp bp (xor k (Exts.wordToWord64# 0x5C5C5C5C5C5C5C5C##)) + update rp bp block +{-# NOINLINE _hmac #-} + +_hmac_rr + :: Ptr Word64 -- ^ register state + -> Ptr Word64 -- ^ block state + -> Registers -- ^ key + -> Registers -- ^ message + -> IO () +_hmac_rr rp bp k m = do + let !key = pad_registers k + !block = pad_registers_with_length m + _hmac_bb rp bp key block +{-# INLINABLE _hmac_rr #-} + +_hmac_bb + :: Ptr Word64 -- ^ register state + -> Ptr Word64 -- ^ block state + -> Block -- ^ padded key + -> Block -- ^ padded message + -> IO () +_hmac_bb rp bp k m = do + poke_registers rp (iv ()) + update rp bp (xor k (Exts.wordToWord64# 0x3636363636363636##)) + update rp bp m + let !inner = pad_registers_with_length (peek_registers rp) + poke_registers rp (iv ()) + update rp bp (xor k (Exts.wordToWord64# 0x5C5C5C5C5C5C5C5C##)) + update rp bp inner +{-# INLINABLE _hmac_bb #-} + +-- | HMAC(key, v || sep || data) using hardware SHA-512 instructions. +-- Writes result to destination pointer. +_hmac_rsb + :: Ptr Word64 -- ^ destination (8 Word64s) + -> Ptr Word64 -- ^ scratch block buffer (16 Word64s) + -> Registers -- ^ key + -> Registers -- ^ v + -> Word8 -- ^ separator byte + -> BS.ByteString -- ^ data + -> IO () +_hmac_rsb rp bp k v sep dat = do + poke_registers rp (iv ()) + let !key = pad_registers k + update rp bp (xor key (Exts.wordToWord64# 0x3636363636363636##)) + _hash_vsb rp bp 128 v sep dat + let !inner = pad_registers_with_length (peek_registers rp) + poke_registers rp (iv ()) + update rp bp (xor key (Exts.wordToWord64# 0x5C5C5C5C5C5C5C5C##)) + update rp bp inner +{-# INLINABLE _hmac_rsb #-} + +-- | Hash (v || sep || dat) with hardware SHA-512 instructions. +-- Assumes register state already initialized at rp. +_hash_vsb + :: Ptr Word64 -- ^ register state + -> Ptr Word64 -- ^ block buffer + -> Word64 -- ^ extra prefix length + -> Registers -- ^ v + -> Word8 -- ^ sep + -> BS.ByteString -- ^ dat + -> IO () +_hash_vsb rp bp el v sep dat@(BI.PS _ _ l) + | l >= 63 = do + -- first block is complete: v || sep || dat[0:63] + let !b0 = parse_vsb v sep dat + update rp bp b0 + -- hash remaining complete blocks from dat[63:] + let !rest = BU.unsafeDrop 63 dat + !restLen = l - 63 + hash_blocks rp bp rest + -- handle final padding + let !finLen = restLen `rem` 128 + !fin = BU.unsafeDrop (restLen - finLen) rest + !total = el + 65 + fi l + if finLen < 112 + then update rp bp (parse_pad1 fin total) + else do + let !(# pen, ult #) = parse_pad2 fin total + update rp bp pen + update rp bp ult + | otherwise = do + -- message < 128 bytes total, straight to padding + let !total = el + 65 + fi l + if 65 + l < 112 + then update rp bp (parse_pad1_vsb v sep dat total) + else do + let !(# pen, ult #) = parse_pad2_vsb v sep dat total + update rp bp pen + update rp bp ult +{-# INLINABLE _hash_vsb #-} + diff --git a/ppad-sha512.cabal b/ppad-sha512.cabal @@ -41,13 +41,13 @@ library Crypto.Hash.SHA512.Lazy Crypto.Hash.SHA512.Pure other-modules: - Crypto.Hash.SHA512.Arm + Crypto.Hash.SHA512.Native Data.Barrier build-depends: base >= 4.9 && < 5 , bytestring >= 0.9 && < 0.13 c-sources: - cbits/sha512_arm.c + cbits/sha512_native.c if flag(sanitize) cc-options: -fsanitize=address,undefined -fno-omit-frame-pointer ghc-options: -optl=-fsanitize=address,undefined