commit 30430a3086a418dbe0209af5032ca78f9eaf5054
parent 4b8ceefa4f400620b0c3ac815be46b2b84329153
Author: Jared Tobin <jared@jtobin.io>
Date:   Tue, 10 Sep 2024 10:09:40 +0400
lib: add hash_lazy
Diffstat:
1 file changed, 37 insertions(+), 17 deletions(-)
diff --git a/lib/Crypto/Hash/SHA256.hs b/lib/Crypto/Hash/SHA256.hs
@@ -6,6 +6,7 @@
 
 module Crypto.Hash.SHA256 (
     hash
+  , hash_lazy
   ) where
 
 import qualified Data.Bits as B
@@ -28,9 +29,16 @@ blocks :: Int -> BS.ByteString -> [BS.ByteString]
 blocks s = loop where
   loop bs
     | BS.null bs = []
-    | otherwise = case BS.splitAt s bs of
+    | otherwise = case BS.splitAt (fi s) bs of
         (c, r) -> c : loop r
 
+blocks_lazy :: Int -> BL.ByteString -> [BS.ByteString]
+blocks_lazy s = loop where
+  loop bs
+    | BL.null bs = []
+    | otherwise = case BL.splitAt (fi s) bs of
+        (c, r) -> BL.toStrict c : loop r
+
 -- verbatim from Data.Binary
 word32be :: BS.ByteString -> Word32
 word32be s =
@@ -43,6 +51,21 @@ word32be s =
 -- message padding and parsing
 -- https://datatracker.ietf.org/doc/html/rfc6234#section-4.1
 
+pad :: BS.ByteString -> BS.ByteString
+pad m = BS.toStrict . BSB.toLazyByteString $
+    loop (BSB.byteString m <> BSB.word8 0x80) k
+  where
+    l = fi (BS.length m)
+
+    -- k such that (l + 1 + k) mod 64 = 56
+    k :: Word64
+    k = let r = 56 - fi l `mod` 64 - 1 :: Integer -- fi prevents underflow
+        in  fi (if r < 0 then r + 64 else r)
+
+    loop acc j
+      | j == 0 = acc <> BSB.word64BE (l * 8)
+      | otherwise = loop (acc <> BSB.word8 0x00) (pred j)
+
 pad_lazy :: BL.ByteString -> BL.ByteString
 pad_lazy (BL.toChunks -> m) = con 0 mempty m where
   con !l acc = \case
@@ -56,6 +79,7 @@ pad_lazy (BL.toChunks -> m) = con 0 mempty m where
           don = fin l k (acc <> BSB.word8 0x80)
       in  BSB.toLazyByteString don
 
+  -- k such that (l + 1 + k) mod 64 = 56
   sol :: Word64 -> Word64
   sol l = let r = 56 - fi l `mod` 64 - 1 :: Integer -- fi prevents underflow
           in  fi (if r < 0 then r + 64 else r)
@@ -66,21 +90,6 @@ pad_lazy (BL.toChunks -> m) = con 0 mempty m where
         let nacc = acc <> BSB.word8 0x00
         in  fin l (pred k) nacc
 
-pad :: BS.ByteString -> BS.ByteString
-pad m = BS.toStrict . BSB.toLazyByteString $
-    loop (BSB.byteString m <> BSB.word8 0x80) k
-  where
-    l = fi (BS.length m)
-
-    -- k such that (l + 1 + k) mod 64 = 56
-    k :: Word64
-    k = let r = 56 - fi l `mod` 64 - 1 :: Integer -- fi prevents underflow
-        in  fi (if r < 0 then r + 64 else r)
-
-    loop acc j
-      | j == 0 = acc <> BSB.word64BE (l * 8)
-      | otherwise = loop (acc <> BSB.word8 0x00) (pred j)
-
 -- functions and constants used
 -- https://datatracker.ietf.org/doc/html/rfc6234#section-5.1
 
@@ -295,7 +304,7 @@ block_hash r@Registers {..} s = loop 0 r where
 
 -- 6.2 step 4
 cat :: Registers -> BS.ByteString
-cat Registers {..} = BS.toStrict . BSB.toLazyByteString $ mconcat [
+cat Registers {..} = BL.toStrict . BSB.toLazyByteString $ mconcat [
     BSB.word32BE h0
   , BSB.word32BE h1
   , BSB.word32BE h2
@@ -317,3 +326,14 @@ hash =
   where
     alg acc = block_hash acc . prepare_schedule . parse
 
+-- | Compute a condensed representation of a lazy bytestring via
+--   SHA-256.
+hash_lazy :: BL.ByteString -> BS.ByteString
+hash_lazy =
+      cat
+    . L.foldl' alg iv
+    . blocks_lazy 64
+    . pad_lazy
+  where
+    alg acc = block_hash acc . prepare_schedule . parse
+