ppad-aead 0.3.3 → 0.3.4
raw patch · 4 files changed
+50/−4 lines, 4 filesdep ~ppad-poly1305PVP ok
version bump matches the API change (PVP)
Dependency ranges changed: ppad-poly1305
API changes (from Hackage documentation)
Files
- CHANGELOG +5/−0
- lib/Crypto/AEAD/ChaCha20Poly1305.hs +5/−2
- lib/Data/Barrier.hs +36/−0
- ppad-aead.cabal +4/−2
CHANGELOG view
@@ -1,5 +1,10 @@ # Changelog +- 0.3.4 (2026-07-17)+ * Adds an optimization-opaque barrier to MAC equality comparison+ to prevent LLVM from introducing variable-time instructions when+ compiling with the LLVM backend.+ - 0.3.3 (2026-07-17) * Uses a different algorithm for constant-time MAC comparison. This version seems to produce less microarchitectural variance on
lib/Crypto/AEAD/ChaCha20Poly1305.hs view
@@ -25,6 +25,7 @@ , _poly1305_key_gen ) where +import Data.Barrier (barrier) import qualified Crypto.Cipher.ChaCha20 as ChaCha20 import qualified Crypto.MAC.Poly1305 as Poly1305 import Data.Bits ((.>>.))@@ -42,7 +43,9 @@ -- the bytewise XORs into an accumulator directly, rather than via -- packZipWith, so no intermediate ByteString holding the -- (secret-derived) difference bytes is ever materialised on the--- heap.+-- heap. The accumulator is routed through 'barrier' before the+-- zero-test so the LLVM backend cannot recover the array-equality+-- idiom and short-circuit on the first mismatch (see "Data.Barrier"). ct_eq :: BS.ByteString -> BS.ByteString -> Bool ct_eq a@(BI.PS _ _ la) b@(BI.PS _ _ lb) | la /= lb = False@@ -50,7 +53,7 @@ where go :: Word8 -> Int -> Bool go !acc !i- | i == la = acc == 0+ | i == la = barrier acc == 0 | otherwise = let !x = BU.unsafeIndex a i !y = BU.unsafeIndex b i
+ lib/Data/Barrier.hs view
@@ -0,0 +1,36 @@+{-# OPTIONS_HADDOCK hide #-}++-- |+-- Module: Data.Barrier+-- Copyright: (c) 2025 Jared Tobin+-- License: MIT+-- Maintainer: Jared Tobin <jared@ppad.tech>+--+-- An optimisation barrier for constant-time code.++module Data.Barrier (+ barrier+ ) where++import Data.Word (Word8)++-- | Identity on 'Word8', but opaque to the optimiser. A constant-time+-- accumulate-then-compare (e.g. an OR-fold of bytewise XORs, tested+-- against zero) routes its accumulator through this before the+-- zero-test, so the compiler cannot recognise it as an array-equality+-- test and lower it to a short-circuiting byte comparison (which would+-- leak the mismatch position).+--+-- Both properties are required and must not be \"tidied\" away:+--+-- * @NOINLINE@ -- if GHC inlines it, the LLVM backend regains the+-- accumulator's definition and short-circuits again.+-- * a /separate/ module -- a caller then compiles @barrier@ to an+-- external call it cannot see through. Inline it into the caller+-- and the barrier is gone.+--+-- Guarded by the substratum + censor constant-time checks; weakening+-- either property re-introduces the timing leak.+barrier :: Word8 -> Word8+barrier x = x+{-# NOINLINE barrier #-}
ppad-aead.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: ppad-aead-version: 0.3.3+version: 0.3.4 synopsis: A pure AEAD-ChaCha20-Poly1305 construction license: MIT license-file: LICENSE@@ -33,11 +33,13 @@ ghc-options: -fllvm -O2 exposed-modules: Crypto.AEAD.ChaCha20Poly1305+ other-modules:+ Data.Barrier build-depends: base >= 4.9 && < 5 , bytestring >= 0.9 && < 0.13 , ppad-chacha >= 0.2.2 && < 0.3- , ppad-poly1305 >= 0.4.4 && < 0.5+ , ppad-poly1305 >= 0.4.5 && < 0.5 test-suite aead-tests type: exitcode-stdio-1.0