diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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.
diff --git a/mldsa.cabal b/mldsa.cabal
--- a/mldsa.cabal
+++ b/mldsa.cabal
@@ -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
diff --git a/src/Auxiliary.hs b/src/Auxiliary.hs
--- a/src/Auxiliary.hs
+++ b/src/Auxiliary.hs
@@ -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
diff --git a/src/Barrier.hs b/src/Barrier.hs
new file mode 100644
--- /dev/null
+++ b/src/Barrier.hs
@@ -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
diff --git a/src/BlockN.hs b/src/BlockN.hs
--- a/src/BlockN.hs
+++ b/src/BlockN.hs
@@ -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 #-}
diff --git a/src/Builder.hs b/src/Builder.hs
--- a/src/Builder.hs
+++ b/src/Builder.hs
@@ -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)
diff --git a/src/Crypto.hs b/src/Crypto.hs
--- a/src/Crypto.hs
+++ b/src/Crypto.hs
@@ -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)
diff --git a/src/Crypto/PubKey/ML_DSA.hs b/src/Crypto/PubKey/ML_DSA.hs
--- a/src/Crypto/PubKey/ML_DSA.hs
+++ b/src/Crypto/PubKey/ML_DSA.hs
@@ -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
diff --git a/src/Endian.hs b/src/Endian.hs
new file mode 100644
--- /dev/null
+++ b/src/Endian.hs
@@ -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
diff --git a/src/Fusion.hs b/src/Fusion.hs
--- a/src/Fusion.hs
+++ b/src/Fusion.hs
@@ -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)
diff --git a/src/Internal.hs b/src/Internal.hs
--- a/src/Internal.hs
+++ b/src/Internal.hs
@@ -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
diff --git a/src/Machine.hs b/src/Machine.hs
--- a/src/Machine.hs
+++ b/src/Machine.hs
@@ -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
 
diff --git a/src/ScrubbedBlock.hs b/src/ScrubbedBlock.hs
--- a/src/ScrubbedBlock.hs
+++ b/src/ScrubbedBlock.hs
@@ -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
diff --git a/tests/get-vectors.sh b/tests/get-vectors.sh
--- a/tests/get-vectors.sh
+++ b/tests/get-vectors.sh
@@ -2,7 +2,7 @@
 
 DESTDIR="`dirname "$0"`"
 
-REF=commit/79e78ba49d1605baaf9acdbf475304af6ae36a59
+REF=commit/5eb1feba427e657ea260113558604e628185e2ba
 CURL=curl
 
 for KEY in keyGen sigGen sigVer; do
