packages feed

ppad-sha256-0.2.5: lib/Crypto/Hash/SHA256/Arm.hs

{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE BangPatterns #-}

-- |
-- Module: Crypto.Hash.SHA256.Arm
-- Copyright: (c) 2024 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- ARM crypto extension support for SHA-256.

module Crypto.Hash.SHA256.Arm (
    sha256_arm_available
  , hash_arm
  , hash_arm_with
  ) where

import Control.Monad (unless, when)
import qualified Data.Bits as B
import qualified Data.ByteString as BS
import qualified Data.ByteString.Internal as BI
import qualified Data.ByteString.Unsafe as BU
import Data.Word (Word8, Word32, Word64)
import Foreign.Marshal.Alloc (allocaBytes)
import Foreign.Ptr (Ptr, plusPtr)
import Foreign.Storable (poke, peek)
import Crypto.Hash.SHA256.Internal (unsafe_padding)
import System.IO.Unsafe (unsafePerformIO)

-- ffi -----------------------------------------------------------------------

foreign import ccall unsafe "sha256_block_arm"
  c_sha256_block :: Ptr Word32 -> Ptr Word8 -> IO ()

foreign import ccall unsafe "sha256_arm_available"
  c_sha256_arm_available :: IO Int

-- utilities -----------------------------------------------------------------

fi :: (Integral a, Num b) => a -> b
fi = fromIntegral
{-# INLINE fi #-}

-- api -----------------------------------------------------------------------

sha256_arm_available :: Bool
sha256_arm_available = unsafePerformIO c_sha256_arm_available /= 0
{-# NOINLINE sha256_arm_available #-}

hash_arm :: BS.ByteString -> BS.ByteString
hash_arm = hash_arm_with mempty 0

-- | Hash with optional 64-byte prefix and extra length for padding.
hash_arm_with
  :: BS.ByteString  -- ^ optional 64-byte prefix (or empty)
  -> Word64         -- ^ extra length to add for padding
  -> BS.ByteString  -- ^ message
  -> BS.ByteString
hash_arm_with prefix el m@(BI.PS fp off l) = unsafePerformIO $
    allocaBytes 32 $ \state -> do
      poke_iv state
      -- process prefix block if provided
      unless (BS.null prefix) $ do
        let BI.PS pfp poff _ = prefix
        BI.unsafeWithForeignPtr pfp $ \src ->
          c_sha256_block state (src `plusPtr` poff)

      go state 0

      let !remaining@(BI.PS _ _ rlen) = BU.unsafeDrop (l - l `rem` 64) m
          BI.PS padfp padoff _ = unsafe_padding remaining (el + fi l)
      BI.unsafeWithForeignPtr padfp $ \src -> do
        c_sha256_block state (src `plusPtr` padoff)
        when (rlen >= 56) $
          c_sha256_block state (src `plusPtr` (padoff + 64))

      read_state state
  where
    go !state !j
      | j + 64 <= l = do
          BI.unsafeWithForeignPtr fp $ \src ->
            c_sha256_block state (src `plusPtr` (off + j))
          go state (j + 64)
      | otherwise = pure ()

-- arm helpers ---------------------------------------------------------------

poke_iv :: Ptr Word32 -> IO ()
poke_iv !state = do
  poke state                (0x6a09e667 :: Word32)
  poke (state `plusPtr` 4)  (0xbb67ae85 :: Word32)
  poke (state `plusPtr` 8)  (0x3c6ef372 :: Word32)
  poke (state `plusPtr` 12) (0xa54ff53a :: Word32)
  poke (state `plusPtr` 16) (0x510e527f :: Word32)
  poke (state `plusPtr` 20) (0x9b05688c :: Word32)
  poke (state `plusPtr` 24) (0x1f83d9ab :: Word32)
  poke (state `plusPtr` 28) (0x5be0cd19 :: Word32)

read_state :: Ptr Word32 -> IO BS.ByteString
read_state !state = BI.create 32 $ \out -> do
  h0 <- peek state                :: IO Word32
  h1 <- peek (state `plusPtr` 4)  :: IO Word32
  h2 <- peek (state `plusPtr` 8)  :: IO Word32
  h3 <- peek (state `plusPtr` 12) :: IO Word32
  h4 <- peek (state `plusPtr` 16) :: IO Word32
  h5 <- peek (state `plusPtr` 20) :: IO Word32
  h6 <- peek (state `plusPtr` 24) :: IO Word32
  h7 <- peek (state `plusPtr` 28) :: IO Word32
  poke_word32be out 0 h0
  poke_word32be out 4 h1
  poke_word32be out 8 h2
  poke_word32be out 12 h3
  poke_word32be out 16 h4
  poke_word32be out 20 h5
  poke_word32be out 24 h6
  poke_word32be out 28 h7

poke_word32be :: Ptr Word8 -> Int -> Word32 -> IO ()
poke_word32be !p !off !w = do
  poke (p `plusPtr` off)       (fi (w `B.unsafeShiftR` 24) :: Word8)
  poke (p `plusPtr` (off + 1)) (fi (w `B.unsafeShiftR` 16) :: Word8)
  poke (p `plusPtr` (off + 2)) (fi (w `B.unsafeShiftR` 8) :: Word8)
  poke (p `plusPtr` (off + 3)) (fi w :: Word8)