module Crypto.Multihash
( MultihashDigest
, Base (..)
, Codable (..)
, HashAlgorithm (..)
, SHA1(..)
, SHA256(..)
, SHA512(..)
, SHA3_512(..)
, SHA3_384(..)
, SHA3_256(..)
, SHA3_224(..)
, Blake2b_512(..)
, Blake2s_256(..)
, encode
, multihash
, multihashlazy
) where
import Crypto.Hash (HashAlgorithm(..), Digest(..), hash, hashlazy)
import Crypto.Hash.Algorithms
import Crypto.Hash.IO
import Data.ByteArray (ByteArrayAccess, Bytes)
import qualified Data.ByteArray as BA
import qualified Data.ByteArray.Encoding as BE
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Base58 as B58
import Data.Word (Word8)
import Text.Printf (printf)
data Base = Base16
| Base32
| Base58
| Base64
deriving (Eq)
data MultihashDigest a = MultihashDigest
{ getAlgorithm :: a
, getLength :: Int
, getDigest :: Digest a
}
class Codable a where
toCode :: a -> Int
instance Codable SHA1 where
toCode SHA1 = 0x11
instance Codable SHA256 where
toCode SHA256 = 0x12
instance Codable SHA512 where
toCode SHA512 = 0x13
instance Codable SHA3_512 where
toCode SHA3_512 = 0x14
instance Codable SHA3_384 where
toCode SHA3_384 = 0x15
instance Codable SHA3_256 where
toCode SHA3_256 = 0x16
instance Codable SHA3_224 where
toCode SHA3_224 = 0x17
instance Codable Blake2b_512 where
toCode Blake2b_512 = 0x40
instance Codable Blake2s_256 where
toCode Blake2s_256 = 0x41
instance Show (MultihashDigest a) where
show (MultihashDigest _ _ d) = show d
multihashlazy :: (HashAlgorithm a, Codable a) => a -> BL.ByteString -> MultihashDigest a
multihashlazy alg bs = let digest = (hashlazy bs)
in MultihashDigest alg (BA.length digest) digest
multihash :: (HashAlgorithm a, Codable a, ByteArrayAccess bs) => a -> bs -> MultihashDigest a
multihash alg bs = let digest = (hash bs)
in MultihashDigest alg (BA.length digest) digest
encode :: (HashAlgorithm a, Codable a, Show a) => Base -> MultihashDigest a -> String
encode base (MultihashDigest alg len md) = if len == len'
then map (toEnum . fromIntegral) fullDigestUnpacked
else error $ printf "Corrupted %s MultihashDigest. Lenght is %d but should be %d." (show alg) len len'
where
len' :: Int
len' = BA.length md
fullDigestUnpacked :: [Word8]
fullDigestUnpacked = BA.unpack $ encoder fullDigest
where
encoder :: ByteArrayAccess a => a -> Bytes
encoder bs = case base of
Base16 -> BE.convertToBase BE.Base16 bs
Base32 -> error "Base32 encoder not implemented"
Base58 -> BA.convert $ B58.encodeBase58 B58.bitcoinAlphabet $ (BA.convert bs :: BS.ByteString)
Base64 -> BE.convertToBase BE.Base64 bs
fullDigest :: Bytes
fullDigest = BA.pack [dHead, dSize] `BA.append` dTail
where
dHead :: Word8
dHead = fromIntegral $ toCode alg
dSize :: Word8
dSize = fromIntegral $ len'
dTail :: Bytes
dTail = BA.convert md