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 +7/−0
- bench/Main.hs +14/−7
- lib/Crypto/MAC/Poly1305.hs +46/−4
- ppad-poly1305.cabal +1/−1
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