chacha

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

commit 334ac0c530e5aca019ca25f483a1bb4593b61326
parent 6e77092c32087fdb8f4754e30ff93f344baa7142
Author: Jared Tobin <jared@jtobin.io>
Date:   Thu,  6 Mar 2025 15:03:30 +0400

lib: chacha20_encrypt passing

Diffstat:
Mlib/Crypto/Cipher/ChaCha.hs | 43++++++++++++++++++++++++++++++++++---------
Mtest/Main.hs | 25+++++++++++++++++++++++++
2 files changed, 59 insertions(+), 9 deletions(-)

diff --git a/lib/Crypto/Cipher/ChaCha.hs b/lib/Crypto/Cipher/ChaCha.hs @@ -6,7 +6,7 @@ module Crypto.Cipher.ChaCha where import qualified Data.Bits as B -import Data.Bits ((.|.)) +import Data.Bits ((.|.), (.<<.), (.^.)) import qualified Data.ByteString as BS import qualified Data.ByteString.Builder as BSB import qualified Data.ByteString.Internal as BI @@ -116,7 +116,7 @@ rotateL# w i `or#` ((word32ToWord# w) `uncheckedShiftRL#` (32# -# i))) {-# INLINE rotateL# #-} --- chacha block function ------------------------------------------------------ +-- chacha20 block function ---------------------------------------------------- data Key = Key { k0 :: {-# UNPACK #-} !Word32 @@ -197,7 +197,7 @@ chacha key counter nonce = do pure (ChaCha arr) - +-- two full rounds (eight quarter rounds) rounds :: PrimMonad m => ChaCha (PrimState m) @@ -212,17 +212,24 @@ rounds state = do quarter state 02 07 08 13 quarter state 03 04 09 14 +-- XX avoid builders here; know the length of this serialize :: PrimMonad m => ChaCha (PrimState m) -> m BS.ByteString serialize (ChaCha m) = do - let loop acc j - | j == 16 = pure (BS.toStrict (BSB.toLazyByteString acc)) - | otherwise = do - v <- PA.readPrimArray m j - loop (acc <> BSB.word32LE v) (j + 1) - loop mempty 0 + w64_0 <- w64 <$> PA.readPrimArray m 00 <*> PA.readPrimArray m 01 + w64_1 <- w64 <$> PA.readPrimArray m 02 <*> PA.readPrimArray m 03 + w64_2 <- w64 <$> PA.readPrimArray m 04 <*> PA.readPrimArray m 05 + w64_3 <- w64 <$> PA.readPrimArray m 06 <*> PA.readPrimArray m 07 + w64_4 <- w64 <$> PA.readPrimArray m 08 <*> PA.readPrimArray m 09 + w64_5 <- w64 <$> PA.readPrimArray m 10 <*> PA.readPrimArray m 11 + w64_6 <- w64 <$> PA.readPrimArray m 12 <*> PA.readPrimArray m 13 + w64_7 <- w64 <$> PA.readPrimArray m 14 <*> PA.readPrimArray m 15 + pure . BS.toStrict . BSB.toLazyByteString . mconcat $ + [w64_0, w64_1, w64_2, w64_3, w64_4, w64_5, w64_6, w64_7] + where + w64 a b = BSB.word64LE (fi a .|. (fi b .<<. 32)) chacha20_block :: PrimMonad m @@ -240,5 +247,23 @@ chacha20_block key counter nonce = do PA.writePrimArray s idx (iv + sv) serialize state +-- chacha20 encryption -------------------------------------------------------- + +chacha20_encrypt + :: PrimMonad m + => BS.ByteString + -> Word32 + -> BS.ByteString + -> BS.ByteString + -> m BS.ByteString +chacha20_encrypt key counter nonce = loop mempty counter where + loop acc j bs = case BS.splitAt 64 bs of + (chunk@(BI.PS _ _ l), etc) + | l == 0 && BS.length etc == 0 -> pure $ + BS.toStrict (BSB.toLazyByteString acc) + | otherwise -> do + stream <- chacha20_block key j nonce + let cip = BS.packZipWith (.^.) chunk stream + loop (acc <> BSB.byteString cip) (j + 1) etc diff --git a/test/Main.hs b/test/Main.hs @@ -22,6 +22,7 @@ main = defaultMain $ testGroup "ppad-chacha" [ , chacha20_block_init , chacha20_rounds , chacha20_block + , chacha20_encrypt ] quarter :: TestTree @@ -105,3 +106,27 @@ chacha20_block = H.testCase "chacha20 block function" $ do H.assertEqual mempty e o +crypt_plain :: BS.ByteString +crypt_plain = case B16.decode "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e" of + Nothing -> error "bang" + Just x -> x + +crypt_cip :: BS.ByteString +crypt_cip = case B16.decode "6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74a35be6b40b8eedf2785e42874d" of + Nothing -> error "bang" + Just x -> x + +crypt_non :: BS.ByteString +crypt_non = case B16.decode "000000000000004a00000000" of + Nothing -> error "bang" + Just x -> x + +chacha20_encrypt :: TestTree +chacha20_encrypt = H.testCase "chacha20 encrypt" $ do + o <- ChaCha.chacha20_encrypt block_key 1 crypt_non crypt_plain + H.assertEqual mempty crypt_cip o + + + + +