Reputation: 999
I am trying to use the Crypto.Hash module (cryptonite package) and produce "hash of hash" results.
The issue is that the signature of the module's hash function is hashlazy :: HashAlgorithm a => ByteString -> Digest a
, so before I can produce a hashlazy (hashlazy x)
, I'll need to translate a Digest into a ByteString.
Although there is a digestFromByteString function provided, there is no such function for the reverse (maybe it doesn't make sense for all Digests?).
So the only way I found is to do this :
-- produce a hexadecimal string representation of the digest
myHash :: ByteString -> String
myHash b = show (hashlazy bs :: Digest SHA256)
-- convert a hexadecimal string into a ByteString
fromHexString :: String -> ByteString
fromHexString = undefined
and then I can now do hashhash x = myHash $ fromHexString $ myHash x
...
But that looks very cumbersome.
My question: Am I using the library functions properly? Did I miss a conversion function somewhere? Or should I do things differently?
=== EDIT 1 ===
I wanted to keep my question simple and avoided other details which I thought weren't important, but I do need to convert the hash result back into a ByteString, as I need to do manipulate it before hashing again. Eg,
-- myByteString is a constant
-- firstByteString is original input
finalResult = hash $ append myByteString (hash firstByteString)
=== EDIT 2 ===
I accepted ben's answer, and learned from it, but for future reference, one way to convert Crypto.Hash's Digest a
into a ByteString
is via the memory package's Data.ByteArray module and the pack function :
module Main where
import Data.ByteString
import Data.ByteString as BS (pack)
import Data.ByteArray (ByteArrayAccess)
import qualified Data.ByteArray as BA (unpack)
import Crypto.Hash (Digest, hash)
import Crypto.Hash.Algorithms (SHA256(..))
somehash = hash ("foo" :: ByteString) :: Digest SHA256
toByteString :: ByteArrayAccess a => a -> ByteString
toByteString = BS.pack . BA.unpack
somebytestring = toByteString somehash
Upvotes: 1
Views: 838
Reputation: 2024
The hash functions there in general require a ByteArrayAccess
instance for their input. Only hashlazy
in particular requires a lazy ByteString
specifically.
hash (hashlazy x)
should work because Digest
has an instance of ByteArrayAccess
. To hash multiple Digest
s together, you can use the underlying hashInit
/hashUpdates
/hashFinalize
functions, passing your all Digest
s to hashUpdates
.
Quick example:
import System.IO
import Crypto.Hash
import Crypto.Hash.Algorithms
import Data.ByteString.UTF8 as B
main = do
putStr "Enter something to hash: " >> hFlush stdout
line1 <- fmap B.fromString getLine
let hash1 = hash line1 :: Digest SHA512
putStrLn $ "First hash is " ++ show hash1
putStr "Enter something else to hash: " >> hFlush stdout
line2 <- fmap B.fromString getLine
let
ctx1 :: Context SHA512
ctx1 = hashInit
ctx2 = hashUpdate ctx1 hash1
ctx3 = hashUpdate ctx2 line2
hash2 = hashFinalize ctx3
putStrLn $ "Second hash is " ++ show hash2
Gives:
Enter something to hash: foo
First hash is f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7
Enter something else to hash: bar
Second hash is b612043d657248615f4c184407f4e5bfc0e6334f86056dfb641cf35ce1e33d86e5349d8e82b3b5018adb07e6b6d653d288e9ed883af624e7f34f12117a620c00
Verify:
$ (echo -n foo | sha512sum | head -c 128 | xxd -p -r ; echo -n bar) | sha512sum
b612043d657248615f4c184407f4e5bfc0e6334f86056dfb641cf35ce1e33d86e5349d8e82b3b5018adb07e6b6d653d288e9ed883af624e7f34f12117a620c00 -
Edit 2: Note that if you actually do need to manipulate the digest you get as an array of bytes, you can use the operations on the BytesArrayAccess
class, or convert to regular old strict ByteString
s from there.
Upvotes: 3