1

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
Janthelme
  • 989
  • 10
  • 23
  • `cryptonite` is a _cryptographic **primitive** library_ – for computing hashes from raw binary data, but it doesn't really seem to offer an interface for actually hashing Haskell values. I suppose the idea is that you should combine it with your favourite serialisation library for that purpose, e.g. [`cereal`](http://hackage.haskell.org/package/cereal). There's also a [`crypto-simple`](http://hackage.haskell.org/package/crypto-simple) library that seems to target applications such as yours. – leftaroundabout Sep 18 '16 at 10:41

1 Answers1

3

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 Digests together, you can use the underlying hashInit/hashUpdates/hashFinalize functions, passing your all Digests 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 ByteStrings from there.

ben
  • 1,994
  • 12
  • 20
  • Thanks, that's completely correct and I should have checked but my problem is that I need to do a bit more than straight hashing a digest. For instance, I need to concatenate a given ByteString before taking the hash. Eg something like `hash (append someBstring hash firstBstring)`. I'll update my question. – Janthelme Sep 18 '16 at 11:25
  • You can call `hashUpdate` multiple times, once on the digest from the previous hashing and once on the extra bytestring(s). – ben Sep 18 '16 at 11:27
  • Thanks. I don't think I understand the `hasUpdate` function properly. How should I use it to hash the contatenation of a given ByteString to the result of a previous hash? Don't I need to convert the result of the previous hash to ByteString? – Janthelme Sep 18 '16 at 11:35
  • 1
    Updated my answer with an example, does that help? – ben Sep 18 '16 at 12:00
  • Works like a charm thanks. I'm also impressed with the CLI wizardry. Is it correct to say that I can use `hasUpdate` as long as I want to append ByteStrings (I meant Digests), but in case I need to do different type of manipulations (eg truncate, etc...) then I would need to convert each hash results back to an intermediary ByteString equivalent? – Janthelme Sep 18 '16 at 13:20
  • You can use `hashUpdate` to effectively append all kinds of byte-like things. Otherwise check out the [`ByteArrayAccess`](https://hackage.haskell.org/package/memory/docs/Data-ByteArray.html#t:ByteArrayAccess) class that `Digest`s have an instance for, they support all kinds of bytestring-like operations, and, failing that, offer a generic `convert` function that should get you a strict `ByteString`. – ben Sep 18 '16 at 14:55