{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}

-- | Implementation of the Blake2b hashing algorithm, with various sizes.
module Cardano.Crypto.Hash.Blake2b
  ( Blake2b_224
  , Blake2b_256
  , blake2b_libsodium -- Used for Hash.Short
  )
where

import Control.Monad (unless)
import Cardano.Crypto.Libsodium.C (c_crypto_generichash_blake2b)

import Cardano.Crypto.Hash.Class (HashAlgorithm (..), SizeHash, hashAlgorithmName, digest)
import Foreign.Ptr (castPtr, nullPtr)
import Foreign.C.Error (errnoToIOError, getErrno)
import GHC.IO.Exception (ioException)

import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as BI

data Blake2b_224
data Blake2b_256

instance HashAlgorithm Blake2b_224 where
  type SizeHash Blake2b_224 = 28
  hashAlgorithmName :: proxy Blake2b_224 -> String
hashAlgorithmName proxy Blake2b_224
_ = String
"blake2b_224"
  digest :: proxy Blake2b_224 -> ByteString -> ByteString
digest proxy Blake2b_224
_ = Int -> ByteString -> ByteString
blake2b_libsodium Int
28

instance HashAlgorithm Blake2b_256 where
  type SizeHash Blake2b_256 = 32
  hashAlgorithmName :: proxy Blake2b_256 -> String
hashAlgorithmName proxy Blake2b_256
_ = String
"blake2b_256"
  digest :: proxy Blake2b_256 -> ByteString -> ByteString
digest proxy Blake2b_256
_ = Int -> ByteString -> ByteString
blake2b_libsodium Int
32

blake2b_libsodium :: Int -> B.ByteString -> B.ByteString
blake2b_libsodium :: Int -> ByteString -> ByteString
blake2b_libsodium Int
size ByteString
input =
  Int -> (Ptr Word8 -> IO ()) -> ByteString
BI.unsafeCreate Int
size ((Ptr Word8 -> IO ()) -> ByteString)
-> (Ptr Word8 -> IO ()) -> ByteString
forall a b. (a -> b) -> a -> b
$ \Ptr Word8
outptr ->
    ByteString -> (CStringLen -> IO ()) -> IO ()
forall a. ByteString -> (CStringLen -> IO a) -> IO a
B.useAsCStringLen ByteString
input ((CStringLen -> IO ()) -> IO ()) -> (CStringLen -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \(Ptr CChar
inptr, Int
inputlen) -> do
      Int
res <- Ptr Any
-> CSize -> Ptr CUChar -> CULLong -> Ptr Any -> CSize -> IO Int
forall out key.
Ptr out
-> CSize -> Ptr CUChar -> CULLong -> Ptr key -> CSize -> IO Int
c_crypto_generichash_blake2b (Ptr Word8 -> Ptr Any
forall a b. Ptr a -> Ptr b
castPtr Ptr Word8
outptr) (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
size) (Ptr CChar -> Ptr CUChar
forall a b. Ptr a -> Ptr b
castPtr Ptr CChar
inptr) (Int -> CULLong
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
inputlen) Ptr Any
forall a. Ptr a
nullPtr CSize
0 -- we used unkeyed hash
      Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Int
res Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
        Errno
errno <- IO Errno
getErrno
        IOException -> IO ()
forall a. IOException -> IO a
ioException (IOException -> IO ()) -> IOException -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> Errno -> Maybe Handle -> Maybe String -> IOException
errnoToIOError String
"digest @Blake2b: crypto_generichash_blake2b" Errno
errno Maybe Handle
forall a. Maybe a
Nothing Maybe String
forall a. Maybe a
Nothing