packages feed

ppad-poly1305 0.4.2 → 0.4.3

raw patch · 4 files changed

+68/−12 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

CHANGELOG view
@@ -1,5 +1,12 @@ # Changelog +- 0.4.3 (2026-07-06)+  * The constant-time 'Eq' on 'MAC' now folds over the two buffers+    directly, rather than materialising an intermediate ByteString+    holding their XOR on the heap. Semantics are unchanged.++  * Improves the performance of constant-time MAC comparison.+ - 0.4.2 (2026-05-16)   * Features order-of-magnitude performance improvements, especially     on ARM platforms where NEON intrinsics are available.
bench/Main.hs view
@@ -43,11 +43,18 @@  suite :: Benchmark suite =-  bgroup "ppad-poly1305" [-    bench "mac (small key)" $ nf (Poly1305.mac key_small) msg-  , bench "mac (mid key)" $ nf (Poly1305.mac key_mid) msg-  , bench "mac (big key)" $ nf (Poly1305.mac key_big) msg-  , bench "mac (1024B msg)" $ nf (Poly1305.mac key_big) msg_1k-  , bench "mac (4096B msg)" $ nf (Poly1305.mac key_big) msg_4k-  ]+  let !mac0 = fromJust (Poly1305.mac key_big msg)+      !mac1 = fromJust (Poly1305.mac key_big msg_1k)+      !mac2 = fromJust (Poly1305.mac key_big msg)+  in  bgroup "ppad-poly1305" [+        bench "mac (small key)" $ nf (Poly1305.mac key_small) msg+      , bench "mac (mid key)" $ nf (Poly1305.mac key_mid) msg+      , bench "mac (big key)" $ nf (Poly1305.mac key_big) msg+      , bench "mac (1024B msg)" $ nf (Poly1305.mac key_big) msg_1k+      , bench "mac (4096B msg)" $ nf (Poly1305.mac key_big) msg_4k+      , bgroup "MAC comparison" [+          bench "mac, unequal" $ whnf (mac0 ==) mac1+        , bench "mac, equal" $ whnf (mac0 ==) mac2+        ]+      ] 
lib/Crypto/MAC/Poly1305.hs view
@@ -31,7 +31,7 @@ import qualified Data.ByteString as BS import qualified Data.ByteString.Internal as BI import qualified Data.ByteString.Unsafe as BU-import Data.Word (Word8)+import Data.Word (Word8, Word64) import Data.Word.Limb (Limb(..)) import qualified Data.Word.Limb as L import Data.Word.Wider (Wider(..))@@ -134,6 +134,24 @@ clamp r = r `W.and` 0x0ffffffc0ffffffc0ffffffc0fffffff {-# INLINE clamp #-} +-- | Assemble the 64-bit word at the given byte offset, little-endian.+--+--   Byte order is immaterial to an equality test as long as both+--   operands are assembled the same way; this is used only by the+--   constant-time 'MAC' comparison. The length is not checked.+word64le :: BS.ByteString -> Int -> Word64+word64le bs m =+  let ix k = fromIntegral (BU.unsafeIndex bs (m + k)) :: Word64+  in        ix 0+      B..|. (ix 1 `B.unsafeShiftL` 0x08)+      B..|. (ix 2 `B.unsafeShiftL` 0x10)+      B..|. (ix 3 `B.unsafeShiftL` 0x18)+      B..|. (ix 4 `B.unsafeShiftL` 0x20)+      B..|. (ix 5 `B.unsafeShiftL` 0x28)+      B..|. (ix 6 `B.unsafeShiftL` 0x30)+      B..|. (ix 7 `B.unsafeShiftL` 0x38)+{-# INLINE word64le #-}+ -- | A Poly1305 message authentication code. -- --   Note that you should compare MACs for equality using the 'Eq'@@ -154,9 +172,33 @@   --   --   Runs in variable-time only for invalid inputs.   (MAC a@(BI.PS _ _ la)) == (MAC b@(BI.PS _ _ lb))-    | la /= lb  = False-    | otherwise =-        BS.foldl' (B..|.) 0 (BS.packZipWith B.xor a b) == 0+      | la /= lb   = False+      | la == 16   =+          -- fully-unrolled, fixed OR-tree over two 64-bit lanes. A+          -- standard 16-byte tag folds through no loop-carried+          -- accumulator, so there is no per-byte accumulator whose+          -- nonzero span tracks the position of a differing byte:+          -- the comparison time is independent of where (or whether)+          -- the tags differ.+          let !d0 = B.xor (word64le a 0) (word64le b 0)+              !d1 = B.xor (word64le a 8) (word64le b 8)+          in  (d0 B..|. d1) == 0+      | otherwise  = go 0 0+    where+      -- byte-serial fallback for nonstandard tag lengths. 'mac'+      -- always yields 16 bytes, so this path is unreachable through+      -- it; it exists only to keep the instance total for MACs built+      -- directly via the exported constructor. The fused fold ORs the+      -- bytewise XORs into an accumulator directly, rather than via+      -- packZipWith, so no intermediate ByteString holding the+      -- (secret-derived) difference bytes is ever materialised.+      go :: Word8 -> Int -> Bool+      go !acc !i+        | i == la   = acc == 0+        | otherwise =+            let !x = BU.unsafeIndex a i+                !y = BU.unsafeIndex b i+            in  go (acc B..|. B.xor x y) (i + 1)  -- | Produce a Poly1305 MAC for the provided message, given the --   provided key.
ppad-poly1305.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               ppad-poly1305-version:            0.4.2+version:            0.4.3 synopsis:           A fast Poly1305 MAC license:            MIT license-file:       LICENSE