chacha

The ChaCha20 stream cipher (docs.ppad.tech/chacha).
git clone git://git.ppad.tech/chacha.git
Log | Files | Refs | README | LICENSE

commit 1ecbfd7ddc3731a9ff532bfe181b104d4490061d
parent 46cb61870a875f2c4e1633bc4d2cba1ddffa461d
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 31 Oct 2026 14:25:37 +0400

test: compare the dispatching and pure implementations

Export '_cipher' for testing, and check that 'cipher' agrees with it
for every input length from 0 to 640 bytes, under two keys and two
starting counters.  One counter puts the final block of a 640-byte
input at the maximum counter value, so any native path that handles
several blocks at once is checked at the counter boundary.

Diffstat:
Mlib/Crypto/Cipher/ChaCha20.hs | 1+
Mtest/Main.hs | 23+++++++++++++++++++++++
2 files changed, 24 insertions(+), 0 deletions(-)

diff --git a/lib/Crypto/Cipher/ChaCha20.hs b/lib/Crypto/Cipher/ChaCha20.hs @@ -27,6 +27,7 @@ module Crypto.Cipher.ChaCha20 ( -- testing , ChaCha(..) , _chacha + , _cipher , _parse_key , _parse_nonce , _quarter diff --git a/test/Main.hs b/test/Main.hs @@ -6,6 +6,7 @@ module Main where +import Control.Monad.ST (runST) import qualified Crypto.Cipher.ChaCha20 as ChaCha import qualified Data.ByteString as BS import qualified Data.ByteString.Base16 as B16 @@ -28,6 +29,7 @@ main = defaultMain $ testGroup "ppad-chacha" [ , crypt3 , counter_boundary , counter_overflow + , dispatch ] quarter :: TestTree @@ -188,3 +190,24 @@ counter_overflow = H.testCase "chacha20 encrypt (counter overflow)" $ do plain = BS.replicate 129 0 out = ChaCha.cipher overflow_key con overflow_non plain H.assertEqual mempty (Left ChaCha.CounterOverflow) out + +-- dispatching vs pure + +-- the dispatching 'cipher' agrees with the pure Haskell implementation +-- for every input length from 0 to 640 bytes, covering partial blocks, +-- short tails, and any multi-block paths. the second counter puts the +-- final block of a 640-byte input at the maximum counter value. +dispatch :: TestTree +dispatch = H.testCase "chacha20 encrypt (dispatch vs pure)" $ + for_ keys $ \key -> + for_ [1, maxBound - 9] $ \con -> + for_ [0..640] $ \l -> do + let msg = BS.take l plain + Right out = ChaCha.cipher key con block_non msg + ref = runST $ + ChaCha._cipher (ChaCha._parse_key key) con + (ChaCha._parse_nonce block_non) msg + H.assertEqual ("length " <> show l) ref out + where + keys = [block_key, BS.replicate 32 0xff] + plain = BS.pack [fromIntegral (i * 7 + 3) | i <- [0..639 :: Int]]