packages feed

crypto-sodium-0.0.5.0: lib/Crypto/Sodium/Hash/Internal.hs

-- SPDX-FileCopyrightText: 2021 Serokell
--
-- SPDX-License-Identifier: MPL-2.0

{-# LANGUAGE ExplicitNamespaces, TypeOperators, TypeFamilies #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}

-- | Internals of @crypto_generichash@.
module Crypto.Sodium.Hash.Internal
  ( HashBlake2b
  , blake2b
  ) where

import Prelude hiding (length)

import Data.ByteArray (ByteArray, ByteArrayAccess, length, withByteArray)
import Data.ByteArray.Sized (SizedByteArray, allocRet)
import Data.Proxy (Proxy (Proxy))
import Foreign.Ptr (nullPtr)
import GHC.TypeNats (KnownNat, natVal, type (<=))

import qualified Libsodium as Na

-- | Hash returned by 'blake2b'.
--
-- This type is parametrised by hash size in bytes and the actual data type
-- that contains bytes. This can be, for example, a @ByteString@.
--
-- Length must be between 16 and 64 bytes.
type HashBlake2b len a = SizedByteArray len a

-- | Hash a message using BLAKE2b.
blake2b
  ::  forall len hashBytes pt key.
      ( ByteArrayAccess pt
      , ByteArrayAccess key
      , ByteArray hashBytes
      , KnownNat len
      , Na.CRYPTO_GENERICHASH_BYTES_MIN <= len
      , len <= Na.CRYPTO_GENERICHASH_BYTES_MAX
      )
  => Maybe key -- ^ Hash key
  -> pt  -- ^ Message to hash
  -> IO (HashBlake2b len hashBytes)
blake2b key msg = do
  (_ret, hash) <-
    allocRet @len Proxy $ \hashPtr ->
    withByteArray msg $ \msgPtr ->
    withKey $ \keyPtr ->
      Na.crypto_generichash_blake2b hashPtr (fromIntegral $ natVal @len Proxy)
        msgPtr (fromIntegral $ length msg)
        keyPtr keyLen
  -- _ret can be only 0, so we don’t check it
  pure hash
  where
    (withKey, keyLen)
      | Just key' <- key = (withByteArray key', fromIntegral $ length key')
      | otherwise = (($ nullPtr), 0)