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 73d2f687b4788d902a29b8b245081396507336fd
parent 9d74a5e1d3995809b3d3f14144554f114592d99c
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 10 Oct 2026 15:23:04 +0400

cbits: detect ARM SHA512 support at runtime

'sha512_arm_available' previously reported the result of a
compile-time feature test, and the cabal file forced that test to
pass on every aarch64 build via '-march=armv8.2-a+sha3'.  A
binary built that way executes SHA512 instructions unconditionally,
faulting with SIGILL on aarch64 cores that lack the extension.

The global '-march' flag is dropped.  The block kernel alone is now
compiled with target("+sha3"), and availability is queried from the
OS at runtime (getauxval on Linux, sysctl on Darwin), falling back
to the pure Haskell implementation where it can't be determined.

Diffstat:
Mcbits/sha512_arm.c | 41+++++++++++++++++++++++++++++++++++++----
Mppad-sha512.cabal | 2--
2 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/cbits/sha512_arm.c b/cbits/sha512_arm.c @@ -1,10 +1,19 @@ #include <stdint.h> #include <string.h> -#if defined(__aarch64__) && defined(__ARM_FEATURE_SHA512) +#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, @@ -55,7 +64,11 @@ static const uint64_t K[80] = { * 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]); @@ -446,14 +459,34 @@ void sha512_block_arm(uint64_t *state, const uint64_t *block) { vst1q_u64(&state[6], gh); } -/* Return 1 if ARM SHA512 is available, 0 otherwise */ +#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) { - return 1; +#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 when ARM SHA512 is not available */ +/* Stub implementations for non-aarch64 builds */ void sha512_block_arm(uint64_t *state, const uint64_t *block) { (void)state; (void)block; diff --git a/ppad-sha512.cabal b/ppad-sha512.cabal @@ -47,8 +47,6 @@ library , bytestring >= 0.9 && < 0.13 c-sources: cbits/sha512_arm.c - if arch(aarch64) - cc-options: -march=armv8.2-a+sha3 if flag(sanitize) cc-options: -fsanitize=address,undefined -fno-omit-frame-pointer ghc-options: -optl=-fsanitize=address,undefined