{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE ScopedTypeVariables #-} -- | -- Copyright : 2018 Monadic GmbH -- License : BSD3 -- Maintainer : kim@monadic.xyz, team@monadic.xyz -- Stability : provisional -- Portability : non-portable (GHC extensions) -- -- encoding of 'Crypto.Hash.Digest's. -- -- Example: -- -- >>> :set -XOverloadedStrings -- >>> import qualified Crypto.Hash as C -- >>> import Data.ByteArray.Encoding (Base(..), convertToBase) -- >>> import Data.ByteString (ByteString) -- >>> import qualified Data.ByteString.Char8 as C8 -- >>> import System.IO (stdout) -- >>> :{ -- let -- input :: ByteString -- input = "multihash" -- atBase :: Base -> Multihash -> ByteString -- atBase base = convertToBase base . encodedBytes -- in -- C8.hPutStr stdout $ C8.unlines -- [ atBase Base16 $ multihash C.SHA1 input -- , atBase Base32 $ multihash C.SHA1 input -- , atBase Base16 $ multihash C.SHA256 input -- , atBase Base32 $ multihash C.SHA256 input -- ] -- :} -- 111488c2f11fb2ce392acb5b2986e640211c4690073e -- CEKIRQXRD6ZM4OJKZNNSTBXGIAQRYRUQA47A==== -- 12209cbc07c3f991725836a3aa2a581ca2029198aa420b9d99bc0e131d9f3e2cbe47 -- CIQJZPAHYP4ZC4SYG2R2UKSYDSRAFEMYVJBAXHMZXQHBGHM7HYWL4RY= -- module Data.Multihash ( Multihash , fromDigest , encodedBytes , multihash , decode , decodeDigest , getMultihash -- * Compact representation , CompactMultihash , compact , expand -- * Re-exports , 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) -- | A multihash-encoded strict 'ByteString'. newtype Multihash = Multihash ByteString deriving (Eq, Ord, Hashable, NFData) -- | A 'Multihash' backed by a 'ShortByteString'. -- -- This is useful when holding many 'Multihash'es in memory, due to lower memory -- overhead and less heap fragmentation. See the documentation for -- 'ShortByteString' for details. newtype CompactMultihash = Compact ShortByteString deriving (Eq, Ord, Hashable, NFData) -- | Encode a 'C.Digest' as a 'Multihash'. 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 -- | Extract the raw, multihash-encoded bytes of a 'Multihash'. encodedBytes :: Multihash -> ByteString encodedBytes = coerce {-# INLINE encodedBytes #-} -- | Hash a value to a 'Multihash' multihash :: (ByteArrayAccess ba, Multihashable a) => a -> ba -> Multihash multihash rithm = fromDigest . C.hashWith rithm -- | Decode a 'Multihash' from a 'ByteString'. decode :: ByteString -> Either String Multihash decode = bimap _3 _3 . Binary.runGetOrFail getMultihash . LBS.fromStrict -- | Decode a 'C.Digest' from a multihash-encoded 'ByteString'. 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 -- | Convert a 'Multihash' to a compact representation. compact :: Multihash -> CompactMultihash compact = coerce . toShort . coerce {-# INLINE compact #-} -- | Convert a 'CompactMultihash' to the regular representation. expand :: CompactMultihash -> Multihash expand = coerce . fromShort . coerce {-# INLINE expand #-}