{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Multihash
( Multihash
, fromDigest
, encodedBytes
, multihash
, decode
, decodeDigest
, getMultihash
, CompactMultihash
, compact
, expand
, HashAlgorithm
, Multihashable
, fromCryptonite
, toCode
, fromCode
, digestSize
)
where
import Data.Multihash.Internal
import Control.DeepSeq (NFData)
import qualified Crypto.Hash as C
import Data.Bifunctor (bimap)
import qualified Data.Binary.Get as Binary
import Data.Binary.VarInt (buildVarInt)
import Data.ByteArray (ByteArrayAccess, convert)
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Builder as Builder
import qualified Data.ByteString.Lazy as LBS
import Data.ByteString.Short (ShortByteString, fromShort, toShort)
import Data.Coerce (coerce)
import Data.Hashable (Hashable)
newtype Multihash = Multihash ByteString
deriving (Eq, Ord, Hashable, NFData)
newtype CompactMultihash = Compact ShortByteString
deriving (Eq, Ord, Hashable, NFData)
fromDigest :: forall a. Multihashable a => C.Digest a -> Multihash
fromDigest dig =
Multihash
. LBS.toStrict . Builder.toLazyByteString
$ code <> len <> Builder.byteString bytes
where
code = buildVarInt . toCode $ fromCryptonite dig
bytes = convert dig
len = buildVarInt $ BS.length bytes
encodedBytes :: Multihash -> ByteString
encodedBytes = coerce
{-# INLINE encodedBytes #-}
multihash :: (ByteArrayAccess ba, Multihashable a) => a -> ba -> Multihash
multihash rithm = fromDigest . C.hashWith rithm
decode :: ByteString -> Either String Multihash
decode =
bimap _3 _3
. Binary.runGetOrFail getMultihash
. LBS.fromStrict
decodeDigest
:: forall a. Multihashable a
=> ByteString
-> Either String (C.Digest a)
decodeDigest =
bimap _3 _3
. Binary.runGetOrFail getMultihashedDigest
. LBS.fromStrict
getMultihash :: Binary.Get Multihash
getMultihash = do
algo <- Binary.lookAhead getHashAlgorithm
case algo of
Blake2s_160 -> fromDigest <$> getMultihashedDigest @C.Blake2s_160
Blake2s_224 -> fromDigest <$> getMultihashedDigest @C.Blake2s_224
Blake2s_256 -> fromDigest <$> getMultihashedDigest @C.Blake2s_256
Blake2b_160 -> fromDigest <$> getMultihashedDigest @C.Blake2b_160
Blake2b_224 -> fromDigest <$> getMultihashedDigest @C.Blake2b_224
Blake2b_256 -> fromDigest <$> getMultihashedDigest @C.Blake2b_256
Blake2b_384 -> fromDigest <$> getMultihashedDigest @C.Blake2b_384
Blake2b_512 -> fromDigest <$> getMultihashedDigest @C.Blake2b_512
MD4 -> fromDigest <$> getMultihashedDigest @C.MD4
MD5 -> fromDigest <$> getMultihashedDigest @C.MD5
SHA1 -> fromDigest <$> getMultihashedDigest @C.SHA1
SHA256 -> fromDigest <$> getMultihashedDigest @C.SHA256
SHA512 -> fromDigest <$> getMultihashedDigest @C.SHA512
Keccak_224 -> fromDigest <$> getMultihashedDigest @C.Keccak_224
Keccak_256 -> fromDigest <$> getMultihashedDigest @C.Keccak_256
Keccak_384 -> fromDigest <$> getMultihashedDigest @C.Keccak_384
Keccak_512 -> fromDigest <$> getMultihashedDigest @C.Keccak_512
SHA3_224 -> fromDigest <$> getMultihashedDigest @C.SHA3_224
SHA3_256 -> fromDigest <$> getMultihashedDigest @C.SHA3_256
SHA3_384 -> fromDigest <$> getMultihashedDigest @C.SHA3_384
SHA3_512 -> fromDigest <$> getMultihashedDigest @C.SHA3_512
compact :: Multihash -> CompactMultihash
compact = coerce . toShort . coerce
{-# INLINE compact #-}
expand :: CompactMultihash -> Multihash
expand = coerce . fromShort . coerce
{-# INLINE expand #-}