packages feed

mldsa 0.1.0.0 → 0.1.1.0

raw patch · 14 files changed

+227/−57 lines, 14 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -1,5 +1,20 @@ # Changelog for `mldsa` +## 0.1.1.0 - 2026-08-16++* Remove redundant hashing for mu and rho'' during signature generation++* Use cached public-key hash during signature verification++* Improve performance of modular reduction with the NCG++* Use `finally` instead of masking asynchronous exceptions while hashing the+  entire message++* Add value barriers to prevent LLVM from introducing unwanted branches++* Refresh the test vectors+ ## 0.1.0.0 - 2026-07-05  * First version. Released on an unsuspecting world.
mldsa.cabal view
@@ -1,11 +1,11 @@ cabal-version: 2.2 --- This file has been generated from package.yaml by hpack version 0.39.1.+-- This file has been generated from package.yaml by hpack version 0.39.6. -- -- see: https://github.com/sol/hpack  name:           mldsa-version:        0.1.0.0+version:        0.1.1.0 synopsis:       Module-Lattice-based Digital Signature Algorithm description:    Module-Lattice-based Digital Signature Algorithm (ML-DSA) implemented in                 Haskell.@@ -38,12 +38,14 @@       Crypto.PubKey.ML_DSA   other-modules:       Auxiliary+      Barrier       Base       Block       BlockN       Builder       ByteArrayST       Crypto+      Endian       Equality       Fusion       Internal@@ -122,6 +124,7 @@   main-is: Tests.hs   other-modules:       Auxiliary+      Barrier       Base       Block       BlockN@@ -129,6 +132,7 @@       ByteArrayST       Crypto       Crypto.PubKey.ML_DSA+      Endian       Equality       Fusion       Internal
src/Auxiliary.hs view
@@ -37,7 +37,6 @@  import Data.ByteArray (ByteArrayAccess) import qualified Data.ByteArray as B-import qualified Data.Memory.Endian as B  import Data.Primitive.Types (Prim(..)) @@ -62,6 +61,7 @@ import Foreign.Storable (pokeByteOff)  import Base+import Barrier import Block (blockIndex) import BlockN (BlockN, MutableBlockN) import Builder (Builder)@@ -76,6 +76,7 @@ import qualified Builder import qualified ByteArrayST as ST import qualified Crypto+import qualified Endian as B import qualified Vector import Math @@ -124,12 +125,36 @@  -- Reduction 𝑥 mod 𝑞 for 0 ≤ 𝑥 < 2𝑞 reduceSimple :: Word32 -> Word32-reduceSimple x = select32 mask x subtracted+#ifdef __GLASGOW_HASKELL_LLVM__+reduceSimple = reduceSimpleLLVM+#else+reduceSimple = fromIntegral . reduceSimpleNCG . fromIntegral+#endif+{-# INLINE reduceSimple #-}++#ifdef __GLASGOW_HASKELL_LLVM__+-- LLVM operates more efficiently on Word16 directly+reduceSimpleLLVM :: Word32 -> Word32+reduceSimpleLLVM x = (mask .&. x) .|. (complement mask .&. subtracted)   where     subtracted = x - q32     mask = subtracted `unsafeShiftIR` 31-{-# INLINE reduceSimple #-}+#else+-- NCG code performance is better with full machine words, as this removes+-- conversion instructions at several intermediate steps+reduceSimpleNCG :: Word -> Word+reduceSimpleNCG x = (mask .&. x) .|. (complement mask .&. subtracted)+  where+    subtracted = x - qW+    mask = subtracted `unsafeShiftIRW` (finiteBitSize qW - 1) +    unsafeShiftIRW :: Word -> Int -> Word+    unsafeShiftIRW w s = fromIntegral ((fromIntegral w :: Int) `unsafeShiftR` s)++    qW :: Word+    qW = fromInteger q+#endif+ #ifdef ML_DSA_TESTING -- Reduction 𝑥 mod 𝑞 for 0 ≤ 𝑥 < 1025𝑞 reduce :: Word64 -> Word32@@ -703,7 +728,7 @@                 set mb val >> go (idx + 1)      new :: ST s (MutableBlockN Sec N H s)-    new = newF >>= \mb -> BlockN.erase mb >> return mb+    new = newF >>= \mb -> mb <$ BlockN.erase mb      set :: MutableBlockN Sec N H s -> Word8 -> ST s ()     set mb val = BlockN.write mb (fromIntegral val) (H 1)@@ -724,7 +749,7 @@ -- Samples a polynomial 𝑐 ∈ 𝑅 with coefficients from {-1, 0, 1} and -- Hamming weight 𝜏 ≤ 64 sampleInBall :: Int -> SecureBytes Sec -> Rq Sec-sampleInBall tau rho = Rq $+sampleInBall tau rho = Rq $ BlockN.seq tau $     BlockN.runNew (Proxy :: Proxy Sec) $ \c -> runXof c 136   where     runXof :: MutableBlockN Sec N Zq s -> Int -> ST s ()@@ -826,8 +851,9 @@      -- rejection sampling from {-2, … , 2}     poke2 b j z-        | z < 15 = BlockN.write b j (Zq 2 .- Zq (mod5 z)) >> return (j + 1)+        | z < 15 = BlockN.write b j (Zq 2 .- Zq z') >> return (j + 1)         | otherwise = return j+      where z' = barrier32 (mod5 z)      -- rejection sampling from {-4, … , 4}     poke4 b j z@@ -886,16 +912,17 @@     _     -> high523776 r  high190464 :: Word32 -> Word32-high190464 r = r1 `xor` (((43 - r1) `unsafeShiftIR` 31) .&. r1)+high190464 r = wrapAround (barrier32 r1)   where+    wrapAround x = x .&. ((x - 44) `unsafeShiftIR` 31)     r' = (r + 127) `unsafeShiftR` 7-    r1 = (r' * 11275 + (1 `unsafeShiftL` 23)) `unsafeShiftR` 24;+    r1 = (r' * 11275 + (1 `unsafeShiftL` 23)) `unsafeShiftR` 24  high523776 :: Word32 -> Word32 high523776 r = r1 .&. 15   where     r' = (r + 127) `unsafeShiftR` 7-    r1 = (r' * 1025 + (1 `unsafeShiftL` 21)) `unsafeShiftR` 22;+    r1 = (r' * 1025 + (1 `unsafeShiftL` 21)) `unsafeShiftR` 22  -- Returns 𝑟1 from the output of decomposeZq 𝑟 highBits :: Word32 -> Rq Sec -> BlockN Sec N Word32
+ src/Barrier.hs view
@@ -0,0 +1,60 @@+-- |+-- Module      : Barrier+-- License     : BSD-3-Clause+-- Copyright   : (c) 2026 Olivier Chéron+--+-- Utilities to block LLVM optimizations that are unwanted.  Currently this+-- targets the X86 CMOV conversion pass, to make sure constant-time masking+-- operations are preserved and not transformed into branches.+--+{-# LANGUAGE CPP #-}+#if (defined(i386_HOST_ARCH) || defined(x86_64_HOST_ARCH)) \+    && defined(__GLASGOW_HASKELL_LLVM__)+#define MLDSA_USE_VALUE_BARRIER 1+#endif+#ifdef MLDSA_USE_VALUE_BARRIER+{-# LANGUAGE MagicHash #-}+#endif+module Barrier+    ( barrier32+    ) where++import Data.Word++#ifdef MLDSA_USE_VALUE_BARRIER++#if MIN_VERSION_base(4,16,0)+import GHC.Exts (Word32#)+#else+import GHC.Exts (Word#)+#endif+import GHC.Word (Word32(W32#))++-- Implements a value barrier:  at call site the content of the function is+-- opaque to the optimizer, thus the call prevents optimizations that need+-- knowledge of the value.+--+-- We want to avoid memory allocations and use CPU registers so the mechanism+-- relies on unlifted values in and out.++barrier32 :: Word32 -> Word32+barrier32 (W32# x#) = W32# (barrier32# x#)+{-# INLINE barrier32 #-}++#if MIN_VERSION_base(4,16,0)+barrier32# :: Word32# -> Word32#+#else+barrier32# :: Word# -> Word#+#endif+barrier32# x# = x#+{-# NOINLINE barrier32# #-}++#else++-- When using the NCG, or not on X86, the call can be completely eliminated.++barrier32 :: Word32 -> Word32+barrier32 = id+{-# INLINE barrier32 #-}++#endif
src/BlockN.hs view
@@ -241,7 +241,7 @@ iterMapIxContext :: (EqPrimSize a b, Classified marking, KnownNat n) => (Int -> a -> b) -> Context (BlockN marking n a) -> Context (BlockN marking n b) iterMapIxContext f = mapContext m   where m = MapF { mapUpdate = unsafeMapIx f-                 , mapInit = \x -> newF >>= \mb -> Prelude.seq x (iterSet (g x) mb) >> return mb+                 , mapInit = \x -> newF >>= \mb -> mb <$ Prelude.seq x (iterSet (g x) mb)                  }         g x (Offset i) = f i (index x (Offset i)) {-# INLINE [1] iterMapIxContext #-}
src/Builder.hs view
@@ -10,8 +10,8 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE RankNTypes #-} module Builder-    ( Builder, builderLength, bytes, copyBuilderToPtr, create, promote, public-    , run, runRelaxed, runToBlock, secret, storable, unsafeCreate+    ( Builder, builderLength, bytes, copyBuilderToPtr, create, memo, promote+    , public, run, runRelaxed, runToBlock, secret, storable, unsafeCreate     ) where  import Data.ByteArray (ByteArray, ByteArrayAccess)@@ -69,6 +69,9 @@  empty :: Builder marking empty = Builder 0 $ \_ -> return ()++memo :: Classified marking => Builder marking -> Builder marking+memo = bytes . run  public :: ByteArrayAccess ba => ba -> Builder marking public b = unsafeCreate (B.length b) (B.copyByteArrayToPtr b)
src/Crypto.hs view
@@ -23,7 +23,7 @@ import Crypto.Hash.Algorithms import Crypto.Hash.IO -import Control.Exception (assert, mask_)+import Control.Exception (assert, finally) import Control.Monad.ST  import Data.ByteArray (ByteArrayAccess, Bytes, MemView(..), ScrubbedBytes)@@ -108,17 +108,21 @@     constEqW a b         | Block.length a /= Block.length b = falseW         | otherwise = Block.foldZipWith (\mask x y -> mask `andW` eqW x y) trueW a b+    {-# NOINLINE constEqW #-}  instance ConstEqW (ScrubbedBlock Word) where     constEqW a b         | ScrubbedBlock.length a /= ScrubbedBlock.length b = falseW         | otherwise = ScrubbedBlock.foldZipWith (\mask x y -> mask `andW` eqW x y) trueW a b+    {-# NOINLINE constEqW #-}  instance ConstEqW Bytes where     constEqW = bytesConstEqW+    {-# NOINLINE constEqW #-}  instance ConstEqW ScrubbedBytes where     constEqW = bytesConstEqW+    {-# NOINLINE constEqW #-}  bytesConstEqW :: (ByteArrayAccess bs1, ByteArrayAccess bs2) => bs1 -> bs2 -> BoolW bytesConstEqW a b@@ -196,11 +200,10 @@  hashWith :: forall marking a ba. (HashAlgorithm a, ByteArrayAccess ba) => a -> ba -> Builder marking hashWith a ba = Builder.unsafeCreate (hashDigestSize a) $ \dig ->-    hashMutableInit >>= \ctx -> mask_ $ do+    withScrubbedContext a $ \ctx -> do         hashUpdateChunked (ctx :: MutableContext a) ba-        B.withByteArray ctx $ \pctx -> do+        B.withByteArray ctx $ \pctx ->             hashInternalFinalize (castPtr pctx :: Ptr (Context a)) dig-            fillBytes pctx 0 (B.length ctx)  hashUpdateChunked :: (HashAlgorithm a, ByteArrayAccess ba) => MutableContext a -> ba -> IO () hashUpdateChunked ctx ba = B.withByteArray ba $ goChunked (B.length ba)@@ -212,3 +215,10 @@             hashMutableUpdate ctx (MemView p chunkSize)             goChunked (remaining - chunkSize) (p `plusPtr` chunkSize)         | otherwise = hashMutableUpdate ctx (MemView p remaining)++withScrubbedContext :: HashAlgorithm a => a -> (MutableContext a -> IO b) -> IO b+withScrubbedContext _ f =+    hashMutableInit >>= \ctx -> f ctx `finally` eraseBytes ctx++eraseBytes :: ByteArrayAccess ba => ba -> IO ()+eraseBytes ctx = B.withByteArray ctx $ \p -> fillBytes p 0 (B.length ctx)
src/Crypto/PubKey/ML_DSA.hs view
@@ -27,20 +27,24 @@     -- ** Pre-hash version     --     -- | This version of ML-DSA can be used to sign or verify a message that is-    -- hashed externally.  Typical use case is when the message is too large to-    -- fit into memory, as pre-hashing can be performed through an incremental-    -- API.+    -- hashed externally.     --     -- Note that signatures produced by HashML-DSA are incompatible with the     -- "pure" version above.  So both signing and verification must use the same     -- version of ML-DSA.  Keys can use either version indistinctly but it is     -- advised to restrict them to one version only to avoid ambiguity.+    --+    -- Compared to the "pure" version, HashML-DSA offers resistance against+    -- collision attacks that is slightly reduced due to message hashing being+    -- separete and not mixed directly with the public key as a single step.     , PreHashAlgorithm, signDigest, signDigestWith, verifyDigest      -- ** External µ version     ---    -- | An internal signing and verfication API that operates on a message-    -- representative µ.  For testing purpose only.+    -- | Signing and verfication API that operates on a message representative+    -- µ.  This is a low-level API that enables external and incremental hashing+    -- for the "pure" version, or even alternate constructions.  But it may also+    -- lessen security properties of ML-DSA if not used correctly.     , Mu, externalMu, signExternalMu, signExternalMuWith, verifyExternalMu      -- ** Internal version@@ -63,12 +67,12 @@  import Data.ByteArray (ByteArrayAccess, Bytes, ScrubbedBytes) import qualified Data.ByteArray as B-import qualified Data.Memory.Endian as B  import Data.Word  import Builder import Internal+import qualified Endian as B  -- | ML-DSA-44 (security category 2) data ML_DSA_44 = ML_DSA_44 deriving Show
+ src/Endian.hs view
@@ -0,0 +1,58 @@+-- |+-- Module      : Endian+-- License     : BSD-3-Clause+-- Copyright   : (c) 2026 Olivier Chéron+--+-- Endianness utilities+--+{-# LANGUAGE CPP #-}+module Endian+    ( B.BE, ByteSwap, B.LE, fromLE, toBE, toLE+    ) where++#include "MachDeps.h"++-- Little-endian conversion in `memory` / `ram` is avoided at compile+-- time only for AMD/Intel, here we will short circuit on ARM too+#if (defined(arm_HOST_ARCH) || defined(aarch64_HOST_ARCH)) \+    && !defined(WORDS_BIGENDIAN)+#define MLDSA_FORCE_LITTLE_ENDIAN_ARCH 1+#endif++#ifdef MLDSA_FORCE_LITTLE_ENDIAN_ARCH+import Data.Word+#else+import Data.Memory.Endian (ByteSwap)+#endif++import qualified Data.Memory.Endian as B++#ifdef MLDSA_FORCE_LITTLE_ENDIAN_ARCH+class ByteSwap a where+    byteSwap :: a -> a+instance ByteSwap Word16 where+    byteSwap = byteSwap16+#endif++#ifdef MLDSA_FORCE_LITTLE_ENDIAN_ARCH+fromLE :: B.LE a -> a+fromLE = B.unLE  -- unwrap constructor with no byte swapping+#else+fromLE :: ByteSwap a => B.LE a -> a+fromLE = B.fromLE  -- byte swap if necessary+#endif++#ifdef MLDSA_FORCE_LITTLE_ENDIAN_ARCH+toLE :: a -> B.LE a+toLE = B.LE  -- wrap constructor with no byte swapping+#else+toLE :: ByteSwap a => a -> B.LE a+toLE = B.toLE  -- byte swap if necessary+#endif++toBE :: ByteSwap a => a -> B.BE a+#ifdef MLDSA_FORCE_LITTLE_ENDIAN_ARCH+toBE = B.BE . byteSwap  -- always byte swap+#else+toBE = B.toBE  -- byte swap if necessary+#endif
src/Fusion.hs view
@@ -51,10 +51,10 @@  thawContext :: Fusion a => a -> Context a thawContext a = Context $ thawF a-{-# INLINE [0] thawContext #-}+{-# INLINE CONLIKE [0] thawContext #-}  modifyContext :: (forall s. Mut a s -> ST s ()) -> Context a -> Context a-modifyContext f = bindContext $ \ma -> f ma >> return ma+modifyContext f = bindContext $ \ma -> ma <$ f ma  mapContext :: MapF a b -> Context a -> Context b mapContext m = bindContext (mapUpdate m)
src/Internal.hs view
@@ -23,7 +23,6 @@  import Data.ByteArray (ByteArray, ByteArrayAccess, Bytes, ScrubbedBytes) import qualified Data.ByteArray as B-import qualified Data.Memory.Endian as B  import Data.Bits import Data.Proxy@@ -39,6 +38,7 @@ import Math import Vector (Vector) import qualified Auxiliary as Aux+import qualified Endian as B import qualified Matrix import qualified Vector @@ -83,7 +83,6 @@ data PrivateKey a = PrivateKey     { skPub    :: {-# UNPACK #-} !(PublicKey a)     , skK      :: {-# UNPACK #-} !ScrubbedBytes-    , skTr     :: {-# UNPACK #-} !Bytes     , skE1     :: {-# UNPACK #-} !ScrubbedBytes     -- serialized s1 & s2     , skE2     :: {-# UNPACK #-} !Bytes             -- serialized t0     , skS1     :: Vector (L a) (Tq Sec)@@ -94,6 +93,9 @@ skRho :: PrivateKey a -> Bytes skRho = pkRho . skPub +skTr :: PrivateKey a -> Bytes+skTr = pkTr . skPub+ skT1 :: PrivateKey a -> Vector (K a) (Tq Pub) skT1 = pkT1 . skPub @@ -104,6 +106,7 @@ data PublicKey a = PublicKey     { pkRho :: {-# UNPACK #-} !Bytes     , pkE   :: {-# UNPACK #-} !Bytes        -- serialized t1+    , pkTr  :: Bytes                        -- public-key hash     , pkT1  :: Vector (K a) (Tq Pub)     , pkA   :: Vector (K a) (Vector (L a) (Tq Pub))     }@@ -160,7 +163,7 @@  instance NFData (PublicKey a) where     rnf pk = rnf (pkRho pk) `seq` rnf (pkE pk)-    -- pkT1, pkA omitted because just for caching+    -- pkTr, pkT1, pkA omitted because just for caching  instance NFData (Signature a) where     rnf sig = rnf (sigCt sig) `seq`@@ -181,9 +184,10 @@         guard (B.length input == 32 + 320 * k)         let rho = B.convert $ B.takeView input 32             pe = B.convert $ B.dropView input 32+            tr = Builder.run $ Crypto.h64 (Builder.run $ Builder.public input)             t1 = Vector.create $ \i -> Aux.simpleBitUnpack10 (view320 i)             aa = expandA rho-        Just PublicKey { pkRho = rho, pkE = pe, pkT1 = Aux.ntt <$> t1, pkA = aa }+        Just PublicKey { pkRho = rho, pkE = pe, pkTr = tr, pkT1 = Aux.ntt <$> t1, pkA = aa }       where         params = getParams p         k = kdim params@@ -218,9 +222,9 @@             ss2 = Aux.ntt <$> s2             t = Aux.nttInv <$> Matrix.mmulAdd aa ss1 ss2             (t1, t0) = Vector.unzipWith Aux.powerTwoRound t-            pk = PublicKey { pkRho = rho, pkE = pe, pkT1 = Aux.ntt <$> t1, pkA = aa }+            pk = PublicKey { pkRho = rho, pkE = pe, pkTr = tr, pkT1 = Aux.ntt <$> t1, pkA = aa }             tr = Builder.run $ Crypto.h64 (encode pk :: Bytes)-            sk = PrivateKey { skPub = pk, skK = kk, skTr = tr, skE1 = e1, skE2 = e2, skS1 = ss1, skS2 = ss2, skT0 = Aux.ntt <$> t0 }+            sk = PrivateKey { skPub = pk, skK = kk, skE1 = e1, skE2 = e2, skS1 = ss1, skS2 = ss2, skT0 = Aux.ntt <$> t0 }             pe = Builder.run $ Vector.concatMap Aux.simpleBitPack10 t1         guard $ Crypto.toBool $             Crypto.constEqW t0 t0' `Crypto.andW` Crypto.constEqW tr tr'@@ -275,9 +279,9 @@     ss2 = Aux.ntt <$> s2     t = Aux.nttInv <$> Matrix.mmulAdd aa ss1 ss2     (t1, t0) = Vector.unzipWith Aux.powerTwoRound t-    pk = PublicKey { pkRho = rho, pkE = pe, pkT1 = Aux.ntt <$> t1, pkA = aa }+    pk = PublicKey { pkRho = rho, pkE = pe, pkTr = tr, pkT1 = Aux.ntt <$> t1, pkA = aa }     tr = Builder.run $ Crypto.h64 (encode pk :: Bytes)-    sk = PrivateKey { skPub = pk, skK = kk, skTr = tr, skE1 = e1, skE2 = e2, skS1 = ss1, skS2 = ss2, skT0 = Aux.ntt <$> t0 }+    sk = PrivateKey { skPub = pk, skK = kk, skE1 = e1, skE2 = e2, skS1 = ss1, skS2 = ss2, skT0 = Aux.ntt <$> t0 }     pe = Builder.run $ Vector.concatMap Aux.simpleBitPack10 t1     e1 = Builder.run $             Vector.concatMap (Aux.bitPack (fromIntegral eta) twoEtaBits) s1 <>@@ -293,12 +297,12 @@ sigGen sk m' = sigGenMu sk mu   where     tr = Builder.bytes (skTr sk)-    mu = Crypto.h64 (Builder.run $ Builder.promote tr <> Builder.secret m')+    mu = Builder.memo $ Crypto.h64 (Builder.run $ Builder.promote tr <> Builder.secret m')  sigGenMu :: (ParamSet a, ByteArrayAccess rnd) => PrivateKey a -> Builder Sec -> rnd -> Signature a sigGenMu sk mu rnd = loop 0   where-    rhos = Crypto.h64 (Builder.run $ Builder.bytes (skK sk) <> Builder.secret rnd <> mu)+    rhos = Builder.memo $ Crypto.h64 (Builder.run $ Builder.bytes (skK sk) <> Builder.secret rnd <> mu)      loop kappa         | Crypto.toBool cn1 && Crypto.toBool cn2 =@@ -338,7 +342,7 @@ sigVer :: (ParamSet a, ByteArrayAccess m) => PublicKey a -> m -> Signature a -> Bool sigVer pk m' = sigVerMu pk mu   where-    tr = Crypto.h64 (encode pk :: Bytes)+    tr = Builder.bytes (pkTr pk)     mu = Crypto.h64 (Builder.run $ Builder.promote tr <> Builder.secret m')  sigVerMu :: ParamSet a => PublicKey a -> Builder Sec -> Signature a -> Bool
src/Machine.hs view
@@ -22,19 +22,12 @@     || defined(powerpc64le_HOST_ARCH) #define MLDSA_ALLOW_UNALIGNED_OP 1 --- Little-endian conversion in `memory` / `ram` is avoided at compile--- time only for AMD/Intel, here we will short circuit on ARM too-#if (defined(arm_HOST_ARCH) || defined(aarch64_HOST_ARCH)) \-    && !defined(WORDS_BIGENDIAN)-#define MLDSA_FORCE_LITTLE_ENDIAN_ARCH 1 #endif -#endif- import Control.Exception (assert)  #ifdef MLDSA_ALLOW_UNALIGNED_OP-import qualified Data.Memory.Endian as B+import qualified Endian as B #endif  import Data.Bits@@ -52,18 +45,10 @@ type WordLE = B.LE WordM  fromLE :: WordLE -> WordM-#ifdef MLDSA_FORCE_LITTLE_ENDIAN_ARCH-fromLE = B.unLE  -- unwrap constructor with no byte swapping-#else-fromLE = B.fromLE  -- byte swap if necessary-#endif+fromLE = B.fromLE  toLE :: WordM -> WordLE-#ifdef MLDSA_FORCE_LITTLE_ENDIAN_ARCH-toLE = B.LE  -- wrap constructor with no byte swapping-#else-toLE = B.toLE  -- byte swap if necessary-#endif+toLE = B.toLE  #else 
src/ScrubbedBlock.hs view
@@ -72,7 +72,7 @@ checkPinned b = assertPinned b (ScrubbedBlock b)  scrubbed :: PrimMonad prim => MutableBlock ty (PrimState prim) -> prim (MutableBlock ty (PrimState prim))-scrubbed b = unsafePrimFromIO (scheduleBlockScrubbing b >> return b)+scrubbed b = unsafePrimFromIO (b <$ scheduleBlockScrubbing b)  wakeUpAfterInception :: MutableBlock ty s -> MutableBlock ty RealWorld wakeUpAfterInception = unsafeCoerce  -- sometimes disappointing
tests/get-vectors.sh view
@@ -2,7 +2,7 @@  DESTDIR="`dirname "$0"`" -REF=commit/79e78ba49d1605baaf9acdbf475304af6ae36a59+REF=commit/5eb1feba427e657ea260113558604e628185e2ba CURL=curl  for KEY in keyGen sigGen sigVer; do