packages feed

ppad-chacha 0.3.1 → 0.3.2

raw patch · 10 files changed

+582/−58 lines, 10 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Crypto.Cipher.ChaCha: ChaCha12 :: Rounds
+ Crypto.Cipher.ChaCha: ChaCha20 :: Rounds
+ Crypto.Cipher.ChaCha: ChaCha8 :: Rounds
+ Crypto.Cipher.ChaCha: CounterOverflow :: Error
+ Crypto.Cipher.ChaCha: InvalidKey :: Error
+ Crypto.Cipher.ChaCha: InvalidNonce :: Error
+ Crypto.Cipher.ChaCha: _keystream_into :: Rounds -> Ptr Word8 -> Word32 -> Ptr Word8 -> Ptr Word8 -> Int -> IO ()
+ Crypto.Cipher.ChaCha: data Error
+ Crypto.Cipher.ChaCha: data Rounds
+ Crypto.Cipher.ChaCha: instance GHC.Classes.Eq Crypto.Cipher.ChaCha.Rounds
+ Crypto.Cipher.ChaCha: instance GHC.Show.Show Crypto.Cipher.ChaCha.Rounds
+ Crypto.Cipher.ChaCha: keystream :: Rounds -> ByteString -> Word32 -> ByteString -> Int -> Either Error ByteString

Files

CHANGELOG view
@@ -1,5 +1,9 @@ # Changelog +- 0.3.2 (2026-11-14)+  * Adds Crypto.Cipher.ChaCha, with ChaCha keystreams of 8, 12, or 20+    rounds.+ - 0.3.1 (2026-10-31)   * Uses SSE2 on x86_64, along with AVX2 on CPUs that support it,     detected at runtime. With AVX2, 'cipher' is roughly 3.5x faster on
bench/Main.hs view
@@ -8,6 +8,7 @@  import Control.DeepSeq import Criterion.Main+import qualified Crypto.Cipher.ChaCha as ChaCha import qualified Crypto.Cipher.ChaCha20 as ChaCha20 import qualified Data.ByteString as BS import qualified Data.ByteString.Base16 as B16@@ -21,6 +22,7 @@ main :: IO () main = defaultMain [     suite+  , keystream   ]  plain :: BS.ByteString@@ -47,5 +49,13 @@     bench "cipher" $ nf (ChaCha20.cipher key 1 non) plain   , bench "cipher (1024B)" $ nf (ChaCha20.cipher key 1 non) plain_1k   , bench "cipher (4096B)" $ nf (ChaCha20.cipher key 1 non) plain_4k+  ]++keystream :: Benchmark+keystream =+  bgroup "ppad-chacha (keystream, 1024B)" [+    bench "ChaCha8" $ nf (ChaCha.keystream ChaCha.ChaCha8 key 1 non) 1024+  , bench "ChaCha12" $ nf (ChaCha.keystream ChaCha.ChaCha12 key 1 non) 1024+  , bench "ChaCha20" $ nf (ChaCha.keystream ChaCha.ChaCha20 key 1 non) 1024   ] 
bench/Weight.hs view
@@ -7,6 +7,7 @@ module Main where  import Control.DeepSeq+import qualified Crypto.Cipher.ChaCha as ChaCha import qualified Crypto.Cipher.ChaCha20 as ChaCha20 import qualified Data.ByteString as BS import GHC.Generics@@ -21,6 +22,7 @@ main = mainWith $ do   block   cipher+  keystream  block :: Weigh () block =@@ -46,3 +48,13 @@           (ChaCha20.cipher key 1 non) bs2         func "cipher (4096B input)"           (ChaCha20.cipher key 1 non) bs3++keystream :: Weigh ()+keystream =+  let !key = BS.replicate 32 0x88+      !non = BS.replicate 12 0x00+  in  wgroup "keystream" $ do+        func "keystream (ChaCha8, 1024B)"+          (ChaCha.keystream ChaCha.ChaCha8 key 1 non) 1024+        func "keystream (ChaCha20, 1024B)"+          (ChaCha.keystream ChaCha.ChaCha20 key 1 non) 1024
cbits/chacha20_native.c view
@@ -65,13 +65,15 @@         v1 = veorq_u32(v1, v2); v1 = ROTL32_7(v1);        \     } while (0) -/* 20-round ChaCha20 core: 10 iterations of (column + diagonal). */+/* ChaCha core: 'drounds' iterations of (column + diagonal), e.g.+ * 10 for ChaCha20. */ static inline void chacha20_core(uint32x4_t *v0, uint32x4_t *v1,                                   uint32x4_t *v2, uint32x4_t *v3,                                   uint32x4_t s0, uint32x4_t s1,-                                  uint32x4_t s2, uint32x4_t s3) {+                                  uint32x4_t s2, uint32x4_t s3,+                                  int drounds) {     uint32x4_t a = s0, b = s1, c = s2, d = s3;-    for (int i = 0; i < 10; i++) {+    for (int i = 0; i < drounds; i++) {         QUARTER(a, b, c, d);         /* shift rows: row 1 left 1, row 2 left 2, row 3 left 3.    */         b = vextq_u32(b, b, 1);@@ -106,10 +108,12 @@ }  /*- * Generate one 64-byte ChaCha20 keystream block at 'out'.+ * Generate one 64-byte keystream block at 'out', using 'drounds'+ * double rounds (10 for ChaCha20).  */ void chacha20_block_native(const uint8_t key[32], uint32_t counter,-                           const uint8_t nonce[12], uint8_t out[64]) {+                           const uint8_t nonce[12], uint8_t out[64],+                           int drounds) {     uint32x4_t s0, s1, s2;     uint32_t n0, n1, n2;     chacha20_setup(key, nonce, &s0, &s1, &s2, &n0, &n1, &n2);@@ -117,7 +121,7 @@     uint32_t s3_in[4] = { counter, n0, n1, n2 };     uint32x4_t s3 = vld1q_u32(s3_in);     uint32x4_t v0, v1, v2, v3;-    chacha20_core(&v0, &v1, &v2, &v3, s0, s1, s2, s3);+    chacha20_core(&v0, &v1, &v2, &v3, s0, s1, s2, s3, drounds);      vst1q_u8(out + 0,  vreinterpretq_u8_u32(v0));     vst1q_u8(out + 16, vreinterpretq_u8_u32(v1));@@ -126,14 +130,15 @@ }  /*- * Encrypt/decrypt 'inlen' bytes at 'in' into 'out' using ChaCha20- * with the given key, starting counter, and nonce.  Stream cipher,- * so the same routine decrypts.+ * Encrypt/decrypt 'inlen' bytes at 'in' into 'out' using ChaCha with+ * 'drounds' double rounds (10 for ChaCha20) and the given key,+ * starting counter, and nonce.  Stream cipher, so the same routine+ * decrypts.  'in' and 'out' may be the same buffer.  */ void chacha20_cipher_native(const uint8_t key[32], uint32_t counter,                             const uint8_t nonce[12],                             const uint8_t *in, uint8_t *out,-                            size_t inlen) {+                            size_t inlen, int drounds) {     uint32x4_t s0, s1, s2;     uint32_t n0, n1, n2;     chacha20_setup(key, nonce, &s0, &s1, &s2, &n0, &n1, &n2);@@ -143,7 +148,7 @@         uint32_t s3_in[4] = { counter, n0, n1, n2 };         uint32x4_t s3 = vld1q_u32(s3_in);         uint32x4_t v0, v1, v2, v3;-        chacha20_core(&v0, &v1, &v2, &v3, s0, s1, s2, s3);+        chacha20_core(&v0, &v1, &v2, &v3, s0, s1, s2, s3, drounds);          uint8x16_t i0 = vld1q_u8(in + pos +  0);         uint8x16_t i1 = vld1q_u8(in + pos + 16);@@ -168,7 +173,7 @@         uint32_t s3_in[4] = { counter, n0, n1, n2 };         uint32x4_t s3 = vld1q_u32(s3_in);         uint32x4_t v0, v1, v2, v3;-        chacha20_core(&v0, &v1, &v2, &v3, s0, s1, s2, s3);+        chacha20_core(&v0, &v1, &v2, &v3, s0, s1, s2, s3, drounds);          uint8_t block[64];         vst1q_u8(block +  0, vreinterpretq_u8_u32(v0));@@ -219,13 +224,15 @@         v1 = _mm_xor_si128(v1, v2); v1 = ROTL32_N(v1, 7);        \     } while (0) -/* 20-round ChaCha20 core: 10 iterations of (column + diagonal). */+/* ChaCha core: 'drounds' iterations of (column + diagonal), e.g.+ * 10 for ChaCha20. */ static inline void chacha20_core(__m128i *v0, __m128i *v1,                                  __m128i *v2, __m128i *v3,                                  __m128i s0, __m128i s1,-                                 __m128i s2, __m128i s3) {+                                 __m128i s2, __m128i s3,+                                 int drounds) {     __m128i a = s0, b = s1, c = s2, d = s3;-    for (int i = 0; i < 10; i++) {+    for (int i = 0; i < drounds; i++) {         QUARTER(a, b, c, d);         /* shift rows: row 1 left 1, row 2 left 2, row 3 left 3.    */         b = _mm_shuffle_epi32(b, _MM_SHUFFLE(0, 3, 2, 1));@@ -347,14 +354,15 @@         v1 = _mm256_xor_si256(v1, v2); v1 = ROTL32_N_AVX2(v1, 7);   \     } while (0) -/* 20-round ChaCha20 core over two blocks. */+/* ChaCha core over two blocks. */ __attribute__((target("avx2"))) static inline void chacha20_core_avx2(__m256i *v0, __m256i *v1,                                       __m256i *v2, __m256i *v3,                                       __m256i s0, __m256i s1,-                                      __m256i s2, __m256i s3) {+                                      __m256i s2, __m256i s3,+                                      int drounds) {     __m256i a = s0, b = s1, c = s2, d = s3;-    for (int i = 0; i < 10; i++) {+    for (int i = 0; i < drounds; i++) {         QUARTER_AVX2(a, b, c, d);         b = _mm256_shuffle_epi32(b, _MM_SHUFFLE(0, 3, 2, 1));         c = _mm256_shuffle_epi32(c, _MM_SHUFFLE(1, 0, 3, 2));@@ -370,13 +378,14 @@     *v3 = _mm256_add_epi32(d, s3); } -/* 20-round ChaCha20 core over four blocks, as two interleaved+/* ChaCha core over four blocks, as two interleaved  * pairs so that their independent dependency chains overlap.        */ __attribute__((target("avx2")))-static inline void chacha20_core4_avx2(__m256i v[8], const __m256i s[8]) {+static inline void chacha20_core4_avx2(__m256i v[8], const __m256i s[8],+                                       int drounds) {     __m256i a = s[0], b = s[1], c = s[2], d = s[3];     __m256i e = s[4], f = s[5], g = s[6], h = s[7];-    for (int i = 0; i < 10; i++) {+    for (int i = 0; i < drounds; i++) {         QUARTER_AVX2(a, b, c, d);         QUARTER_AVX2(e, f, g, h);         b = _mm256_shuffle_epi32(b, _MM_SHUFFLE(0, 3, 2, 1));@@ -438,7 +447,7 @@                                    uint32_t counter,                                    const uint8_t nonce[12],                                    const uint8_t *in, uint8_t *out,-                                   size_t inlen) {+                                   size_t inlen, int drounds) {     __m256i s0 = _mm256_broadcastsi128_si256(         _mm_loadu_si128((const __m128i *)chacha_constants));     __m256i s1 = _mm256_broadcastsi128_si256(@@ -462,7 +471,7 @@                               (int)c3, (int)n0, (int)n1, (int)n2)         };         __m256i v[8];-        chacha20_core4_avx2(v, s);+        chacha20_core4_avx2(v, s, drounds);         chacha20_xor2_avx2(in + pos, out + pos, v[0], v[1], v[2], v[3]);         chacha20_xor2_avx2(in + pos + 128, out + pos + 128,                            v[4], v[5], v[6], v[7]);@@ -475,7 +484,7 @@             (int)counter, (int)n0, (int)n1, (int)n2,             (int)c1,      (int)n0, (int)n1, (int)n2);         __m256i v0, v1, v2, v3;-        chacha20_core_avx2(&v0, &v1, &v2, &v3, s0, s1, s2, s3);+        chacha20_core_avx2(&v0, &v1, &v2, &v3, s0, s1, s2, s3, drounds);         chacha20_xor2_avx2(in + pos, out + pos, v0, v1, v2, v3);         pos += 128;         counter += 2;@@ -484,17 +493,19 @@ }  /*- * Generate one 64-byte ChaCha20 keystream block at 'out'.+ * Generate one 64-byte keystream block at 'out', using 'drounds'+ * double rounds (10 for ChaCha20).  */ void chacha20_block_native(const uint8_t key[32], uint32_t counter,-                           const uint8_t nonce[12], uint8_t out[64]) {+                           const uint8_t nonce[12], uint8_t out[64],+                           int drounds) {     __m128i s0, s1, s2;     uint32_t n0, n1, n2;     chacha20_setup(key, nonce, &s0, &s1, &s2, &n0, &n1, &n2);      __m128i s3 = chacha20_row3(counter, n0, n1, n2);     __m128i v0, v1, v2, v3;-    chacha20_core(&v0, &v1, &v2, &v3, s0, s1, s2, s3);+    chacha20_core(&v0, &v1, &v2, &v3, s0, s1, s2, s3, drounds);      _mm_storeu_si128((__m128i *)(out +  0), v0);     _mm_storeu_si128((__m128i *)(out + 16), v1);@@ -503,14 +514,15 @@ }  /*- * Encrypt/decrypt 'inlen' bytes at 'in' into 'out' using ChaCha20- * with the given key, starting counter, and nonce.  Stream cipher,- * so the same routine decrypts.+ * Encrypt/decrypt 'inlen' bytes at 'in' into 'out' using ChaCha with+ * 'drounds' double rounds (10 for ChaCha20) and the given key,+ * starting counter, and nonce.  Stream cipher, so the same routine+ * decrypts.  'in' and 'out' may be the same buffer.  */ void chacha20_cipher_native(const uint8_t key[32], uint32_t counter,                             const uint8_t nonce[12],                             const uint8_t *in, uint8_t *out,-                            size_t inlen) {+                            size_t inlen, int drounds) {     __m128i s0, s1, s2;     uint32_t n0, n1, n2;     chacha20_setup(key, nonce, &s0, &s1, &s2, &n0, &n1, &n2);@@ -518,14 +530,15 @@     /* block pairs via AVX2 where available, the rest via SSE2 */     size_t pos = 0;     if (inlen >= 128 && have_avx2()) {-        pos = chacha20_cipher_avx2(key, counter, nonce, in, out, inlen);+        pos = chacha20_cipher_avx2(key, counter, nonce, in, out, inlen,+                                   drounds);         counter += (uint32_t)(pos / 64);     }      while (pos + 64 <= inlen) {         __m128i s3 = chacha20_row3(counter, n0, n1, n2);         __m128i v0, v1, v2, v3;-        chacha20_core(&v0, &v1, &v2, &v3, s0, s1, s2, s3);+        chacha20_core(&v0, &v1, &v2, &v3, s0, s1, s2, s3, drounds);          __m128i i0 = _mm_loadu_si128((const __m128i *)(in + pos +  0));         __m128i i1 = _mm_loadu_si128((const __m128i *)(in + pos + 16));@@ -549,7 +562,7 @@     if (pos < inlen) {         __m128i s3 = chacha20_row3(counter, n0, n1, n2);         __m128i v0, v1, v2, v3;-        chacha20_core(&v0, &v1, &v2, &v3, s0, s1, s2, s3);+        chacha20_core(&v0, &v1, &v2, &v3, s0, s1, s2, s3, drounds);          uint8_t block[64];         _mm_storeu_si128((__m128i *)(block +  0), v0);@@ -574,16 +587,17 @@  * gated on 'chacha20_native_available' returning 0                 */  void chacha20_block_native(const uint8_t *key, uint32_t counter,-                           const uint8_t *nonce, uint8_t *out) {-    (void)key; (void)counter; (void)nonce; (void)out;+                           const uint8_t *nonce, uint8_t *out,+                           int drounds) {+    (void)key; (void)counter; (void)nonce; (void)out; (void)drounds; }  void chacha20_cipher_native(const uint8_t *key, uint32_t counter,                             const uint8_t *nonce,                             const uint8_t *in, uint8_t *out,-                            size_t inlen) {+                            size_t inlen, int drounds) {     (void)key; (void)counter; (void)nonce;-    (void)in; (void)out; (void)inlen;+    (void)in; (void)out; (void)inlen; (void)drounds; }  int chacha20_native_available(void) {
+ lib/Crypto/Cipher/ChaCha.hs view
@@ -0,0 +1,132 @@+{-# OPTIONS_HADDOCK prune #-}+{-# LANGUAGE BangPatterns #-}++-- |+-- Module: Crypto.Cipher.ChaCha+-- Copyright: (c) 2026 Jared Tobin+-- License: MIT+-- Maintainer: Jared Tobin <jared@ppad.tech>+--+-- ChaCha keystreams with 8, 12, or 20 rounds, using the state layout+-- of [RFC 8439](https://datatracker.ietf.org/doc/html/rfc8439).+--+-- The reduced-round variants are intended for uses such as+-- random number generation; use "Crypto.Cipher.ChaCha20" for+-- encryption.++module Crypto.Cipher.ChaCha (+    -- * Round counts+    Rounds(..)++    -- * Keystream+  , keystream++    -- * Error information+  , Error(..)++    -- internal+  , _keystream_into+  ) where++import Control.Monad.ST (runST)+import Crypto.Cipher.ChaCha20 (Error(..))+import qualified Crypto.Cipher.ChaCha20 as ChaCha20+import qualified Crypto.Cipher.ChaCha20.Native as Native+import qualified Data.ByteString as BS+import qualified Data.ByteString.Internal as BI+import qualified Data.ByteString.Unsafe as BU+import Data.Word (Word8, Word32, Word64)+import Foreign.ForeignPtr (withForeignPtr)+import Foreign.Marshal.Utils (copyBytes)+import Foreign.Ptr (Ptr, castPtr, plusPtr)++-- utils ----------------------------------------------------------------------++fi :: (Integral a, Num b) => a -> b+fi = fromIntegral+{-# INLINE fi #-}++-- rounds ---------------------------------------------------------------------++-- | The number of ChaCha rounds.+data Rounds =+    ChaCha8  -- ^ 8 rounds+  | ChaCha12 -- ^ 12 rounds+  | ChaCha20 -- ^ 20 rounds, as specified by RFC 8439+  deriving (Eq, Show)++-- number of double rounds+drounds :: Rounds -> Int+drounds r = case r of+  ChaCha8  -> 4+  ChaCha12 -> 6+  ChaCha20 -> 10+{-# INLINE drounds #-}++-- keystream ------------------------------------------------------------------++-- | The ChaCha keystream with the given number of rounds, starting+--   at the given block counter.+--+--   The key must be exactly 256 bits, and the nonce exactly 96 bits.+--   The counter must leave room for the requested number of bytes;+--   non-positive numbers of bytes yield the empty string.+--+--   >>> import qualified Data.ByteString as BS+--   >>> import qualified Data.ByteString.Base16 as B16+--   >>> let key = BS.replicate 32 0+--   >>> let non = BS.replicate 12 0+--   >>> fmap B16.encode (keystream ChaCha8 key 0 non 16)+--   Right "3e00ef2f895f40d67f5bb8e81f09a5a1"+keystream+  :: Rounds         -- ^ number of rounds+  -> BS.ByteString  -- ^ 256-bit key+  -> Word32         -- ^ 32-bit counter+  -> BS.ByteString  -- ^ 96-bit nonce+  -> Int            -- ^ number of bytes+  -> Either Error BS.ByteString -- ^ keystream+keystream r key@(BI.PS kfp koff kl) counter nonce@(BI.PS nfp noff nl) len+  | kl /= 32      = Left InvalidKey+  | nl /= 12      = Left InvalidNonce+  | len <= 0      = Right BS.empty+  | fi len > room = Left CounterOverflow+  | Native.chacha20_native_available =+      Right $ BI.unsafeCreate len $ \dst ->+        withForeignPtr kfp $ \kp ->+        withForeignPtr nfp $ \np ->+          Native.keystream_into (drounds r) (kp `plusPtr` koff) counter+            (np `plusPtr` noff) dst len+  | otherwise = Right $ runST $+      ChaCha20._cipher_rounds (drounds r)+        (ChaCha20._parse_key key) counter (ChaCha20._parse_nonce nonce)+        (BS.replicate len 0)+  where+    !room = (0x100000000 - fi counter) * 64 :: Word64++-- | Write keystream bytes to the destination pointer, for the key and+--   nonce at the given pointers.+--+--   Unchecked: the key and nonce pointers must address 32 and 12+--   bytes respectively, the destination must have room for the given+--   number of bytes and must not overlap the key or nonce, and the+--   counter must leave room for the bytes.+_keystream_into+  :: Rounds    -- ^ number of rounds+  -> Ptr Word8 -- ^ 256-bit key+  -> Word32    -- ^ 32-bit counter+  -> Ptr Word8 -- ^ 96-bit nonce+  -> Ptr Word8 -- ^ destination+  -> Int       -- ^ number of bytes+  -> IO ()+_keystream_into r kp counter np dst len+  | Native.chacha20_native_available =+      Native.keystream_into (drounds r) kp counter np dst len+  | otherwise = do+      key <- BS.packCStringLen (castPtr kp, 32)+      non <- BS.packCStringLen (castPtr np, 12)+      let !out = runST $+            ChaCha20._cipher_rounds (drounds r)+              (ChaCha20._parse_key key) counter+              (ChaCha20._parse_nonce non) (BS.replicate len 0)+      BU.unsafeUseAsCString out $ \src ->+        copyBytes dst (castPtr src) len
lib/Crypto/Cipher/ChaCha20.hs view
@@ -28,6 +28,7 @@   , ChaCha(..)   , _chacha   , _cipher+  , _cipher_rounds   , _parse_key   , _parse_nonce   , _quarter@@ -257,15 +258,17 @@   _quarter state 03 04 09 14 {-# INLINEABLE _rounds #-} +-- block function with the given number of double rounds _block   :: PrimMonad m-  => ChaCha (PrimState m)+  => Int+  -> ChaCha (PrimState m)   -> Word32   -> m BS.ByteString-_block state@(ChaCha s) counter = do+_block drounds state@(ChaCha s) counter = do   _chacha_counter state counter   i <- PA.freezePrimArray s 0 16-  for_ [1..10 :: Int] (const (_rounds state))+  for_ [1..drounds] (const (_rounds state))   for_ [0..15 :: Int] $ \idx -> do     let iv = PA.indexPrimArray i idx     sv <- PA.readPrimArray s idx@@ -294,7 +297,7 @@   | kl /= 32 = Left InvalidKey   | nl /= 12 = Left InvalidNonce   | Native.chacha20_native_available =-      Right (Native.block key counter nonce)+      Right (Native.block 10 key counter nonce)   | otherwise = pure $ runST $ do       let k = _parse_key key           n = _parse_nonce nonce@@ -355,7 +358,7 @@     | nl /= 12  = Left InvalidNonce     | pl > room = Left CounterOverflow     | Native.chacha20_native_available =-        Right (Native.cipher raw_key counter raw_nonce plaintext)+        Right (Native.cipher 10 raw_key counter raw_nonce plaintext)     | otherwise = pure $ runST $ do         let key = _parse_key raw_key             non = _parse_nonce raw_nonce@@ -370,7 +373,19 @@   -> Nonce   -> BS.ByteString   -> m BS.ByteString-_cipher key counter nonce plaintext = do+_cipher = _cipher_rounds 10+{-# INLINE _cipher #-}++-- stream cipher with the given number of double rounds+_cipher_rounds+  :: PrimMonad m+  => Int+  -> Key+  -> Word32+  -> Nonce+  -> BS.ByteString+  -> m BS.ByteString+_cipher_rounds drounds key counter nonce plaintext = do   ChaCha initial <- _chacha key counter nonce   state@(ChaCha s) <- _chacha_alloc @@ -380,10 +395,10 @@               BS.toStrict (BSB.toLazyByteString acc)           | otherwise -> do               PA.copyMutablePrimArray s 0 initial 0 16-              stream <- _block state j+              stream <- _block drounds state j               let cip = BS.packZipWith (.^.) chunk stream               loop (acc <> BSB.byteString cip) (j + 1) etc    loop mempty counter plaintext-{-# INLINE _cipher #-}+{-# INLINE _cipher_rounds #-} 
lib/Crypto/Cipher/ChaCha20/Native.hs view
@@ -13,6 +13,7 @@     chacha20_native_available   , block   , cipher+  , keystream_into   ) where  import qualified Data.ByteString as BS@@ -20,6 +21,7 @@ import Data.Word (Word8, Word32) import Foreign.C.Types (CInt(..), CSize(..)) import Foreign.ForeignPtr (withForeignPtr)+import Foreign.Marshal.Utils (fillBytes) import Foreign.Ptr (Ptr, plusPtr) import System.IO.Unsafe (unsafeDupablePerformIO) @@ -27,12 +29,12 @@  foreign import ccall unsafe "chacha20_block_native"   c_chacha20_block-    :: Ptr Word8 -> Word32 -> Ptr Word8 -> Ptr Word8 -> IO ()+    :: Ptr Word8 -> Word32 -> Ptr Word8 -> Ptr Word8 -> CInt -> IO ()  foreign import ccall unsafe "chacha20_cipher_native"   c_chacha20_cipher     :: Ptr Word8 -> Word32 -> Ptr Word8-    -> Ptr Word8 -> Ptr Word8 -> CSize -> IO ()+    -> Ptr Word8 -> Ptr Word8 -> CSize -> CInt -> IO ()  foreign import ccall unsafe "chacha20_native_available"   c_chacha20_native_available :: IO CInt@@ -51,10 +53,11 @@   unsafeDupablePerformIO c_chacha20_native_available /= 0 {-# NOINLINE chacha20_native_available #-} --- | One 64-byte ChaCha20 keystream block for the given (already----   validated) key, counter, and nonce.-block :: BS.ByteString -> Word32 -> BS.ByteString -> BS.ByteString-block (BI.PS kfp koff _) counter (BI.PS nfp noff _) =+-- | One 64-byte keystream block, using the given number of double+--   rounds, for the given (already-validated) key, counter, and nonce.+block+  :: Int -> BS.ByteString -> Word32 -> BS.ByteString -> BS.ByteString+block drounds (BI.PS kfp koff _) counter (BI.PS nfp noff _) =   BI.unsafeCreate 64 $ \dst ->     withForeignPtr kfp $ \kp0 ->     withForeignPtr nfp $ \np0 ->@@ -62,13 +65,15 @@                        counter                        (np0 `plusPtr` noff)                        dst+                       (fi drounds) --- | XOR the plaintext with the ChaCha20 keystream derived from the---   given (already-validated) key, counter, and nonce.+-- | XOR the plaintext with the keystream derived, using the given+--   number of double rounds, from the given (already-validated) key,+--   counter, and nonce. cipher-  :: BS.ByteString -> Word32 -> BS.ByteString -> BS.ByteString+  :: Int -> BS.ByteString -> Word32 -> BS.ByteString -> BS.ByteString   -> BS.ByteString-cipher (BI.PS kfp koff _) counter (BI.PS nfp noff _)+cipher drounds (BI.PS kfp koff _) counter (BI.PS nfp noff _)        (BI.PS pfp poff plen) =   BI.unsafeCreate plen $ \dst ->     withForeignPtr kfp $ \kp0 ->@@ -80,3 +85,15 @@                         (pp0 `plusPtr` poff)                         dst                         (fi plen)+                        (fi drounds)++-- | Write 'len' bytes of keystream, using the given number of double+--   rounds, for the 32-byte key and 12-byte nonce at the given+--   pointers, to the destination, which must not overlap them. The+--   counter must leave room for 'len' bytes.+keystream_into+  :: Int -> Ptr Word8 -> Word32 -> Ptr Word8 -> Ptr Word8 -> Int+  -> IO ()+keystream_into drounds kp counter np dst len = do+  fillBytes dst 0 len+  c_chacha20_cipher kp counter np dst dst (fi len) (fi drounds)
ppad-chacha.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               ppad-chacha-version:            0.3.1+version:            0.3.2 synopsis:           A fast ChaCha20 stream cipher license:            MIT license-file:       LICENSE@@ -35,7 +35,8 @@   if flag(llvm)     ghc-options: -fllvm -O2   exposed-modules:-      Crypto.Cipher.ChaCha20+      Crypto.Cipher.ChaCha+    , Crypto.Cipher.ChaCha20   other-modules:       Crypto.Cipher.ChaCha20.Native   build-depends:@@ -55,6 +56,7 @@   default-language:    Haskell2010   hs-source-dirs:      test   main-is:             Main.hs+  other-modules:       Vectors    ghc-options:     -rtsopts -Wall -O2
test/Main.hs view
@@ -2,20 +2,26 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE UnboxedTuples #-}  module Main where  import Control.Monad.ST (runST)+import qualified Crypto.Cipher.ChaCha as Reduced import qualified Crypto.Cipher.ChaCha20 as ChaCha import qualified Data.ByteString as BS import qualified Data.ByteString.Base16 as B16+import qualified Data.ByteString.Internal as BI+import qualified Data.ByteString.Unsafe as BU import Data.Foldable (for_) import Data.Maybe (fromJust) import qualified Data.Primitive.PrimArray as PA import Data.Word (Word32)+import Foreign.Ptr (castPtr) import Test.Tasty import qualified Test.Tasty.HUnit as H+import Vectors (V(..), vectors)  main :: IO () main = defaultMain $ testGroup "ppad-chacha" [@@ -30,6 +36,12 @@   , counter_boundary   , counter_overflow   , dispatch+  , testGroup "keystream" [+        keystream_vectors+      , keystream_errors+      , keystream_dispatch+      , keystream_into+      ]   ]  quarter :: TestTree@@ -211,3 +223,67 @@   where     keys = [block_key, BS.replicate 32 0xff]     plain = BS.pack [fromIntegral (i * 7 + 3) | i <- [0..639 :: Int]]++-- keystream++hex :: BS.ByteString -> BS.ByteString+hex bs = case B16.decode bs of+  Nothing -> error "ppad-chacha (test): invalid hex"+  Just x  -> x++-- the vectors place a 64-bit IV in the last two words of the state,+-- which is the RFC 8439 layout with a zero first nonce word+keystream_vectors :: TestTree+keystream_vectors = testGroup "draft-strombergson vectors" $+  fmap check vectors+  where+    check V {..} =+      H.testCase (v_case <> " (" <> show v_rounds <> ")") $ do+        let non = BS.replicate 4 0 <> hex v_iv+            out = Reduced.keystream v_rounds (hex v_key) 0 non 128+        H.assertEqual mempty (Right (hex v_keystream)) out++keystream_errors :: TestTree+keystream_errors = H.testCase "keystream errors" $ do+  let key = BS.replicate 32 0+      non = BS.replicate 12 0+  H.assertEqual "key" (Left ChaCha.InvalidKey)+    (Reduced.keystream Reduced.ChaCha8 (BS.take 31 key) 0 non 64)+  H.assertEqual "nonce" (Left ChaCha.InvalidNonce)+    (Reduced.keystream Reduced.ChaCha8 key 0 (BS.take 11 non) 64)+  H.assertEqual "boundary" (Right 128) $+    fmap BS.length+      (Reduced.keystream Reduced.ChaCha8 key (maxBound - 1) non 128)+  H.assertEqual "overflow" (Left ChaCha.CounterOverflow)+    (Reduced.keystream Reduced.ChaCha8 key (maxBound - 1) non 129)+  H.assertEqual "zero length" (Right mempty)+    (Reduced.keystream Reduced.ChaCha8 key 0 non 0)+  H.assertEqual "negative length" (Right mempty)+    (Reduced.keystream Reduced.ChaCha8 key 0 non (-1))++-- the dispatching 'keystream' agrees with the pure Haskell+-- implementation for every round count and every length from 0 to+-- 640 bytes+keystream_dispatch :: TestTree+keystream_dispatch = H.testCase "keystream (dispatch vs pure)" $+  for_ rounds $ \(r, d) ->+  for_ [1, maxBound - 9] $ \con ->+  for_ [0..640] $ \l -> do+    let Right out = Reduced.keystream r block_key con block_non l+        ref = runST $+          ChaCha._cipher_rounds d (ChaCha._parse_key block_key) con+            (ChaCha._parse_nonce block_non) (BS.replicate l 0)+    H.assertEqual (show r <> ", length " <> show l) ref out+  where+    rounds = [(Reduced.ChaCha8, 4), (Reduced.ChaCha12, 6),+              (Reduced.ChaCha20, 10)]++keystream_into :: TestTree+keystream_into = H.testCase "_keystream_into" $+  for_ [Reduced.ChaCha8, Reduced.ChaCha12, Reduced.ChaCha20] $ \r -> do+    let Right ref = Reduced.keystream r block_key 7 block_non 1000+    out <- BI.create 1000 $ \dst ->+      BU.unsafeUseAsCString block_key $ \kp ->+      BU.unsafeUseAsCString block_non $ \np ->+        Reduced._keystream_into r (castPtr kp) 7 (castPtr np) dst 1000+    H.assertEqual (show r) ref out
+ test/Vectors.hs view
@@ -0,0 +1,242 @@+{-# LANGUAGE OverloadedStrings #-}++-- Keystream vectors for ChaCha with 8, 12, and 20 rounds and 256-bit+-- keys, from etc/draft-strombergson-chacha-test-vectors-01.txt. Each+-- keystream covers two blocks, at counters 0 and 1, with the 64-bit+-- IV in the last two words of the state. All values are hex-encoded.++module Vectors (+    V(..)+  , vectors+  ) where++import Crypto.Cipher.ChaCha (Rounds(..))+import qualified Data.ByteString as BS++data V = V {+    v_case      :: !String+  , v_rounds    :: !Rounds+  , v_key       :: !BS.ByteString+  , v_iv        :: !BS.ByteString+  , v_keystream :: !BS.ByteString+  }++vectors :: [V]+vectors = [+    V "TC1" ChaCha8+      "0000000000000000000000000000000000000000000000000000000000000000"+      "0000000000000000"+      (mconcat [+          "3e00ef2f895f40d67f5bb8e81f09a5a12c840ec3ce9a7f3b181be188ef711a1e"+        , "984ce172b9216f419f445367456d5619314a42a3da86b001387bfdb80e0cfe42"+        , "d2aefa0deaa5c151bf0adb6c01f2a5adc0fd581259f9a2aadcf20f8fd566a26b"+        , "5032ec38bbc5da98ee0c6f568b872a65a08abf251deb21bb4b56e5d8821e68aa"+        ])+  , V "TC1" ChaCha12+      "0000000000000000000000000000000000000000000000000000000000000000"+      "0000000000000000"+      (mconcat [+          "9bf49a6a0755f953811fce125f2683d50429c3bb49e074147e0089a52eae155f"+        , "0564f879d27ae3c02ce82834acfa8c793a629f2ca0de6919610be82f411326be"+        , "0bd58841203e74fe86fc71338ce0173dc628ebb719bdcbcc151585214cc089b4"+        , "42258dcda14cf111c602b8971b8cc843e91e46ca905151c02744a6b017e69316"+        ])+  , V "TC1" ChaCha20+      "0000000000000000000000000000000000000000000000000000000000000000"+      "0000000000000000"+      (mconcat [+          "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7"+        , "da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"+        , "9f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32ee7aed"+        , "29b721769ce64e43d57133b074d839d531ed1f28510afb45ace10a1f4b794d6f"+        ])+  , V "TC2" ChaCha8+      "0100000000000000000000000000000000000000000000000000000000000000"+      "0000000000000000"+      (mconcat [+          "cf5ee9a0494aa9613e05d5ed725b804b12f4a465ee635acc3a311de8740489ea"+        , "289d04f43c7518db56eb4433e498a1238cd8464d3763ddbb9222ee3bd8fae3c8"+        , "b4355a7d93dd8867089ee643558b95754efa2bd1a8a1e2d75bcdb32015542638"+        , "291941feb49965587c4fdfe219cf0ec132a6cd4dc067392e67982fe53278c0b4"+        ])+  , V "TC2" ChaCha12+      "0100000000000000000000000000000000000000000000000000000000000000"+      "0000000000000000"+      (mconcat [+          "12056e595d56b0f6eef090f0cd25a20949248c2790525d0f930218ff0b4ddd10"+        , "a6002239d9a454e29e107a7d06fefdfef0210feba044f9f29b1772c960dc29c0"+        , "0c7366c5cbc604240e665eb02a69372a7af979b26fbb78092ac7c4b88029a7c8"+        , "54513bc217bbfc7d90432e308eba15afc65aeb48ef100d5601e6afba257117a9"+        ])+  , V "TC2" ChaCha20+      "0100000000000000000000000000000000000000000000000000000000000000"+      "0000000000000000"+      (mconcat [+          "c5d30a7ce1ec119378c84f487d775a8542f13ece238a9455e8229e888de85bbd"+        , "29eb63d0a17a5b999b52da22be4023eb07620a54f6fa6ad8737b71eb0464dac0"+        , "10f656e6d1fd55053e50c4875c9930a33f6d0263bd14dfd6ab8c70521c19338b"+        , "2308b95cf8d0bb7d202d2102780ea3528f1cb48560f76b20f382b942500fceac"+        ])+  , V "TC3" ChaCha8+      "0000000000000000000000000000000000000000000000000000000000000000"+      "0100000000000000"+      (mconcat [+          "2b8f4bb3798306ca5130d47c4f8d4ed13aa0edccc1be6942090faeeca0d7599b"+        , "7ff0fe616bb25aa0153ad6fdc88b954903c22426d478b97b22b8f9b1db00cf06"+        , "470bdffbc488a8b7c701ebf4061d75c5969186497c95367809afa80bd843b040"+        , "a79abc6e73a91757f1db73c8eacfa543b38f289d065ab2f3032d377b8c37fe46"+        ])+  , V "TC3" ChaCha12+      "0000000000000000000000000000000000000000000000000000000000000000"+      "0100000000000000"+      (mconcat [+          "64b8bdf87b828c4b6dbaf7ef698de03df8b33f635714418f9836ade59be12969"+        , "46c953a0f38ecffc9ecb98e81d5d99a5edfc8f9a0a45b9e41ef3b31f028f1d0f"+        , "559db4a7f222c442fe23b9a2596a88285122ee4f1363896ea77ca150912ac723"+        , "bff04b026a2f807e03b29c02077d7b06fc1ab9827c13c8013a6d83bd3b52a26f"+        ])+  , V "TC3" ChaCha20+      "0000000000000000000000000000000000000000000000000000000000000000"+      "0100000000000000"+      (mconcat [+          "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32"+        , "111e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b"+        , "5305e5e44aff19b235936144675efbe4409eb7e8e5f1430f5f5836aeb49bb532"+        , "8b017c4b9dc11f8a03863fa803dc71d5726b2b6b31aa32708afe5af1d6b69058"+        ])+  , V "TC4" ChaCha8+      "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"+      "ffffffffffffffff"+      (mconcat [+          "e163bbf8c9a739d18925ee8362dad2cdc973df05225afb2aa26396f2a9849a4a"+        , "445e0547d31c1623c537df4ba85c70a9884a35bcbf3dfab077e98b0f68135f54"+        , "81d4933f8b322ac0cd762c27235ce2b31534e0244a9a2f1fd5e94498d47ff108"+        , "790c009cf9e1a348032a7694cb28024cd96d3498361edb1785af752d187ab54b"+        ])+  , V "TC4" ChaCha12+      "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"+      "ffffffffffffffff"+      (mconcat [+          "04bf88dae8e47a228fa47b7e6379434ba664a7d28f4dab84e5f8b464add20c3a"+        , "caa69c5ab221a23a57eb5f345c96f4d1322d0a2ff7a9cd43401cd536639a615a"+        , "5c9429b55ca3c1b55354559669a154aca46cd761c41ab8ace385363b95675f06"+        , "8e18db5a673c11291bd4187892a9a3a33514f3712b26c13026103298ed76bc9a"+        ])+  , V "TC4" ChaCha20+      "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"+      "ffffffffffffffff"+      (mconcat [+          "d9bf3f6bce6ed0b54254557767fb57443dd4778911b606055c39cc25e674b836"+        , "3feabc57fde54f790c52c8ae43240b79d49042b777bfd6cb80e931270b7f50eb"+        , "5bac2acd86a836c5dc98c116c1217ec31d3a63a9451319f097f3b4d6dab07787"+        , "19477d24d24b403a12241d7cca064f790f1d51ccaff6b1667d4bbca1958c4306"+        ])+  , V "TC5" ChaCha8+      "5555555555555555555555555555555555555555555555555555555555555555"+      "5555555555555555"+      (mconcat [+          "7cb78214e4d3465b6dc62cf7a1538c88996952b4fb72cb6105f1243ce3442e29"+        , "75a59ebcd2b2a598290d7538491fe65bdbfefd060d88798120a70d049dc2677d"+        , "d48ff5a2513e497a5d54802d7484c4f1083944d8d0d14d6482ce09f7e5ebf20b"+        , "29807d62c31874d02f5d3cc85381a745ecbc60525205e300a76961bfe51ac07c"+        ])+  , V "TC5" ChaCha12+      "5555555555555555555555555555555555555555555555555555555555555555"+      "5555555555555555"+      (mconcat [+          "a600f07727ff93f3da00dd74cc3e8bfb5ca7302f6a0a2944953de00450eecd40"+        , "b860f66049f2eaed63b2ef39cc310d2c488f5d9a241b615dc0ab70f921b91b95"+        , "140eff4aa495ac61289b6bc57de072419d09daa7a7243990daf348a8f2831e59"+        , "7cf379b3b284f00bda27a4c68085374a8a5c38ded62d1141cae0bb838ddc2232"+        ])+  , V "TC5" ChaCha20+      "5555555555555555555555555555555555555555555555555555555555555555"+      "5555555555555555"+      (mconcat [+          "bea9411aa453c5434a5ae8c92862f564396855a9ea6e22d6d3b50ae1b3663311"+        , "a4a3606c671d605ce16c3aece8e61ea145c59775017bee2fa6f88afc758069f7"+        , "e0b8f676e644216f4d2a3422d7fa36c6c4931aca950e9da42788e6d0b6d1cd83"+        , "8ef652e97b145b14871eae6c6804c7004db5ac2fce4c68c726d004b10fcaba86"+        ])+  , V "TC6" ChaCha8+      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"+      "aaaaaaaaaaaaaaaa"+      (mconcat [+          "40f9ab86c8f9a1a0cdc05a75e5531b612d71ef7f0cf9e387df6ed6972f0aae21"+        , "311aa581f816c90e8a99de990b6b95aac92450f4e112712667b804c99e9c6eda"+        , "f8d144f560c8c0ea36880d3b77874c9a9103d147f6ded386284801a4ee158e5e"+        , "a4f9c093fc55fd344c33349dc5b699e21dc83b4296f92ee3ecabf3d51f95fe3f"+        ])+  , V "TC6" ChaCha12+      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"+      "aaaaaaaaaaaaaaaa"+      (mconcat [+          "856505b01d3b47aae03d6a97aa0f033a9adcc94377babd8608864fb3f625b6e3"+        , "14f086158f9f725d811eeb953b7f747076e4c3f639fa841fad6c9a709e621397"+        , "6dd6ee9b5e1e2e676b1c9e2b82c2e96c1648437bff2f0126b74e8ce0a9b06d17"+        , "20ac0b6f09086f28bc201587f0535ed9385270d08b4a9382f18f82dbde18210e"+        ])+  , V "TC6" ChaCha20+      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"+      "aaaaaaaaaaaaaaaa"+      (mconcat [+          "9aa2a9f656efde5aa7591c5fed4b35aea2895dec7cb4543b9e9f21f5e7bcbcf3"+        , "c43c748a970888f8248393a09d43e0b7e164bc4d0b0fb240a2d72115c4808906"+        , "72184489440545d021d97ef6b693dfe5b2c132d47e6f041c9063651f96b623e6"+        , "2a11999a23b6f7c461b2153026ad5e866a2e597ed07b8401dec63a0934c6b2a9"+        ])+  , V "TC7" ChaCha8+      "00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100"+      "0f1e2d3c4b5a6978"+      (mconcat [+          "db43ad9d1e842d1272e4530e276b3f568f8859b3f7cf6d9d2c74fa53808cb515"+        , "7a8ebf46ad3dcc4b6c7dadde131784b0120e0e22f6d5f9ffa7407d4a21b695d9"+        , "c5dd30bf55612fab9bdd118920c19816470c7f5dcd42325dbbed8c57a56281c1"+        , "44cb0f03e81b3004624e0650a1ce5afaf9a7cd8163f6dbd72602257dd96e471e"+        ])+  , V "TC7" ChaCha12+      "00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100"+      "0f1e2d3c4b5a6978"+      (mconcat [+          "7ed12a3a63912ae941ba6d4c0d5e862e568b0e5589346935505f064b8c2698db"+        , "f7d850667d8e67be639f3b4f6a16f92e65ea80f6c7429445da1fc2c1b9365040"+        , "e32e50c4106f3b3da1ce7ccb1e7140b153493c0f3ad9a9bcff077ec4596f1d0f"+        , "29bf9cbaa502820f732af5a93c49eee33d1c4f12af3b4297af91fe41ea9e94a2"+        ])+  , V "TC7" ChaCha20+      "00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100"+      "0f1e2d3c4b5a6978"+      (mconcat [+          "9fadf409c00811d00431d67efbd88fba59218d5d6708b1d685863fabbb0e961e"+        , "ea480fd6fb532bfd494b2151015057423ab60a63fe4f55f7a212e2167ccab931"+        , "fbfd29cf7bc1d279eddf25dd316bb8843d6edee0bd1ef121d12fa17cbc2c574c"+        , "ccab5e275167b08bd686f8a09df87ec3ffb35361b94ebfa13fec0e4889d18da5"+        ])+  , V "TC8" ChaCha8+      "c46ec1b18ce8a878725a37e780dfb7351f68ed2e194c79fbc6aebee1a667975d"+      "1ada31d5cf688221"+      (mconcat [+          "838751b42d8ddd8a3d77f48825a2ba752cf4047cb308a5978ef274973be374c9"+        , "6ad848065871417b08f034e681fe46a93f7d5c61d1306614d4aaf257a7cff08b"+        , "16f2fda170cc18a4b58a2667ed962774af792a6e7f3c77992540711a7a136d7e"+        , "8a2f8d3f93816709d45a3fa5f8ce72fde15be7b841acba3a2abd557228d9fe4f"+        ])+  , V "TC8" ChaCha12+      "c46ec1b18ce8a878725a37e780dfb7351f68ed2e194c79fbc6aebee1a667975d"+      "1ada31d5cf688221"+      (mconcat [+          "1482072784bc6d06b4e73bdc118bc0103c7976786ca918e06986aa251f7e9cc1"+        , "b2749a0a16ee83b4242d2e99b08d7c20092b80bc466c87283b61b1b39d0ffbab"+        , "d94b116bc1ebdb329b9e4f620db695544a8e3d9b68473d0c975a46ad966ed631"+        , "e42aff530ad5eac7d8047adfa1e5113c91f3e3b883f1d189ac1c8fe07ba5a42b"+        ])+  , V "TC8" ChaCha20+      "c46ec1b18ce8a878725a37e780dfb7351f68ed2e194c79fbc6aebee1a667975d"+      "1ada31d5cf688221"+      (mconcat [+          "f63a89b75c2271f9368816542ba52f06ed49241792302b00b5e8f80ae9a473af"+        , "c25b218f519af0fdd406362e8d69de7f54c604a6e00f353f110f771bdca8ab92"+        , "e5fbc34e60a1d9a9db17345b0a402736853bf910b060bdf1f897b6290f01d138"+        , "ae2c4c90225ba9ea14d518f55929dea098ca7a6ccfe61227053c84e49a4a3332"+        ])+  ]