chacha

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

commit 254e76a830d56e383fada03a588fdb9e919f46a9
parent 847775bf37703a0ebf5111c1929c61779554da36
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 31 Oct 2026 14:26:54 +0400

lib: rename the Arm module and kernel to Native

Crypto.Cipher.ChaCha20.Arm becomes Crypto.Cipher.ChaCha20.Native, and
cbits/chacha20_arm.c becomes cbits/chacha20_native.c, exporting
chacha20_block_native, chacha20_cipher_native and
chacha20_native_available.  Nothing in the Haskell module is specific
to ARM beyond the FFI symbols it binds, so the same wrappers can sit
over kernels for another architecture.

Diffstat:
Dcbits/chacha20_arm.c | 199-------------------------------------------------------------------------------
Acbits/chacha20_native.c | 199+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/Crypto/Cipher/ChaCha20.hs | 10+++++-----
Dlib/Crypto/Cipher/ChaCha20/Arm.hs | 82-------------------------------------------------------------------------------
Alib/Crypto/Cipher/ChaCha20/Native.hs | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mppad-chacha.cabal | 4++--
6 files changed, 288 insertions(+), 288 deletions(-)

diff --git a/cbits/chacha20_arm.c b/cbits/chacha20_arm.c @@ -1,199 +0,0 @@ -#include <stddef.h> -#include <stdint.h> -#include <string.h> - -#if defined(__aarch64__) - -#include <arm_neon.h> - -/* - * ChaCha20 NEON kernel using intra-block parallelism. The 16-word - * state matrix - * - * s00 s01 s02 s03 - * s04 s05 s06 s07 - * s08 s09 s10 s11 - * s12 s13 s14 s15 - * - * is held in four 128-bit NEON registers v0..v3, one per row. A - * column quarter-round on (s00, s04, s08, s12), (s01, s05, s09, s13), - * etc., becomes one set of element-wise vector operations on - * (v0, v1, v2, v3) — four quarter-rounds in parallel. Diagonal - * rounds are reached by left-rotating v1, v2, v3 by 1, 2, 3 lanes - * respectively with VEXT before the second quarter-round, then - * rotating back. - */ - -/* 32-bit left rotations. Rotate-by-16 reduces to REV32.u16; the - * others compile to a shift-shift-or pair (the compiler folds rotate- - * by-8 to a TBL with a constant shuffle on some targets). */ -#define ROTL32_16(x) \ - vreinterpretq_u32_u16(vrev32q_u16(vreinterpretq_u16_u32(x))) -#define ROTL32_12(x) \ - vorrq_u32(vshlq_n_u32((x), 12), vshrq_n_u32((x), 20)) -#define ROTL32_8(x) \ - vorrq_u32(vshlq_n_u32((x), 8), vshrq_n_u32((x), 24)) -#define ROTL32_7(x) \ - vorrq_u32(vshlq_n_u32((x), 7), vshrq_n_u32((x), 25)) - -#define QUARTER(v0, v1, v2, v3) \ - do { \ - v0 = vaddq_u32(v0, v1); \ - v3 = veorq_u32(v3, v0); v3 = ROTL32_16(v3); \ - v2 = vaddq_u32(v2, v3); \ - v1 = veorq_u32(v1, v2); v1 = ROTL32_12(v1); \ - v0 = vaddq_u32(v0, v1); \ - v3 = veorq_u32(v3, v0); v3 = ROTL32_8(v3); \ - v2 = vaddq_u32(v2, v3); \ - v1 = veorq_u32(v1, v2); v1 = ROTL32_7(v1); \ - } while (0) - -/* 20-round ChaCha20 core: 10 iterations of (column + diagonal). */ -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 a = s0, b = s1, c = s2, d = s3; - for (int i = 0; i < 10; 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); - c = vextq_u32(c, c, 2); - d = vextq_u32(d, d, 3); - QUARTER(a, b, c, d); - /* shift back. */ - b = vextq_u32(b, b, 3); - c = vextq_u32(c, c, 2); - d = vextq_u32(d, d, 1); - } - *v0 = vaddq_u32(a, s0); - *v1 = vaddq_u32(b, s1); - *v2 = vaddq_u32(c, s2); - *v3 = vaddq_u32(d, s3); -} - -static const uint32_t chacha_constants[4] = { - 0x61707865u, 0x3320646eu, 0x79622d32u, 0x6b206574u -}; - -/* Set up the constant rows of the state from key + nonce. s3 - * (counter + nonce) varies per block and is built inside the loop. */ -static inline void chacha20_setup(const uint8_t key[32], - const uint8_t nonce[12], - uint32x4_t *s0, uint32x4_t *s1, - uint32x4_t *s2, - uint32_t *n0, uint32_t *n1, - uint32_t *n2) { - *s0 = vld1q_u32(chacha_constants); - *s1 = vreinterpretq_u32_u8(vld1q_u8(key)); - *s2 = vreinterpretq_u32_u8(vld1q_u8(key + 16)); - memcpy(n0, nonce + 0, 4); - memcpy(n1, nonce + 4, 4); - memcpy(n2, nonce + 8, 4); -} - -/* - * Generate one 64-byte ChaCha20 keystream block at 'out'. - */ -void chacha20_block_arm(const uint8_t key[32], uint32_t counter, - const uint8_t nonce[12], uint8_t out[64]) { - uint32x4_t s0, s1, s2; - uint32_t n0, n1, n2; - chacha20_setup(key, nonce, &s0, &s1, &s2, &n0, &n1, &n2); - - 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); - - vst1q_u8(out + 0, vreinterpretq_u8_u32(v0)); - vst1q_u8(out + 16, vreinterpretq_u8_u32(v1)); - vst1q_u8(out + 32, vreinterpretq_u8_u32(v2)); - vst1q_u8(out + 48, vreinterpretq_u8_u32(v3)); -} - -/* - * 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. - */ -void chacha20_cipher_arm(const uint8_t key[32], uint32_t counter, - const uint8_t nonce[12], - const uint8_t *in, uint8_t *out, - size_t inlen) { - uint32x4_t s0, s1, s2; - uint32_t n0, n1, n2; - chacha20_setup(key, nonce, &s0, &s1, &s2, &n0, &n1, &n2); - - size_t pos = 0; - while (pos + 64 <= inlen) { - 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); - - uint8x16_t i0 = vld1q_u8(in + pos + 0); - uint8x16_t i1 = vld1q_u8(in + pos + 16); - uint8x16_t i2 = vld1q_u8(in + pos + 32); - uint8x16_t i3 = vld1q_u8(in + pos + 48); - - vst1q_u8(out + pos + 0, - veorq_u8(i0, vreinterpretq_u8_u32(v0))); - vst1q_u8(out + pos + 16, - veorq_u8(i1, vreinterpretq_u8_u32(v1))); - vst1q_u8(out + pos + 32, - veorq_u8(i2, vreinterpretq_u8_u32(v2))); - vst1q_u8(out + pos + 48, - veorq_u8(i3, vreinterpretq_u8_u32(v3))); - - pos += 64; - counter++; - } - - /* trailing partial block (< 64 bytes) */ - if (pos < inlen) { - 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); - - uint8_t block[64]; - vst1q_u8(block + 0, vreinterpretq_u8_u32(v0)); - vst1q_u8(block + 16, vreinterpretq_u8_u32(v1)); - vst1q_u8(block + 32, vreinterpretq_u8_u32(v2)); - vst1q_u8(block + 48, vreinterpretq_u8_u32(v3)); - - size_t remaining = inlen - pos; - for (size_t i = 0; i < remaining; i++) { - out[pos + i] = in[pos + i] ^ block[i]; - } - } -} - -int chacha20_arm_available(void) { - return 1; -} - -#else - -/* stubs for non-aarch64 builds; never reached because dispatch is - * gated on 'chacha20_arm_available' returning 0 */ - -void chacha20_block_arm(const uint8_t *key, uint32_t counter, - const uint8_t *nonce, uint8_t *out) { - (void)key; (void)counter; (void)nonce; (void)out; -} - -void chacha20_cipher_arm(const uint8_t *key, uint32_t counter, - const uint8_t *nonce, - const uint8_t *in, uint8_t *out, - size_t inlen) { - (void)key; (void)counter; (void)nonce; - (void)in; (void)out; (void)inlen; -} - -int chacha20_arm_available(void) { - return 0; -} - -#endif diff --git a/cbits/chacha20_native.c b/cbits/chacha20_native.c @@ -0,0 +1,199 @@ +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#if defined(__aarch64__) + +#include <arm_neon.h> + +/* + * ChaCha20 NEON kernel using intra-block parallelism. The 16-word + * state matrix + * + * s00 s01 s02 s03 + * s04 s05 s06 s07 + * s08 s09 s10 s11 + * s12 s13 s14 s15 + * + * is held in four 128-bit NEON registers v0..v3, one per row. A + * column quarter-round on (s00, s04, s08, s12), (s01, s05, s09, s13), + * etc., becomes one set of element-wise vector operations on + * (v0, v1, v2, v3) — four quarter-rounds in parallel. Diagonal + * rounds are reached by left-rotating v1, v2, v3 by 1, 2, 3 lanes + * respectively with VEXT before the second quarter-round, then + * rotating back. + */ + +/* 32-bit left rotations. Rotate-by-16 reduces to REV32.u16; the + * others compile to a shift-shift-or pair (the compiler folds rotate- + * by-8 to a TBL with a constant shuffle on some targets). */ +#define ROTL32_16(x) \ + vreinterpretq_u32_u16(vrev32q_u16(vreinterpretq_u16_u32(x))) +#define ROTL32_12(x) \ + vorrq_u32(vshlq_n_u32((x), 12), vshrq_n_u32((x), 20)) +#define ROTL32_8(x) \ + vorrq_u32(vshlq_n_u32((x), 8), vshrq_n_u32((x), 24)) +#define ROTL32_7(x) \ + vorrq_u32(vshlq_n_u32((x), 7), vshrq_n_u32((x), 25)) + +#define QUARTER(v0, v1, v2, v3) \ + do { \ + v0 = vaddq_u32(v0, v1); \ + v3 = veorq_u32(v3, v0); v3 = ROTL32_16(v3); \ + v2 = vaddq_u32(v2, v3); \ + v1 = veorq_u32(v1, v2); v1 = ROTL32_12(v1); \ + v0 = vaddq_u32(v0, v1); \ + v3 = veorq_u32(v3, v0); v3 = ROTL32_8(v3); \ + v2 = vaddq_u32(v2, v3); \ + v1 = veorq_u32(v1, v2); v1 = ROTL32_7(v1); \ + } while (0) + +/* 20-round ChaCha20 core: 10 iterations of (column + diagonal). */ +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 a = s0, b = s1, c = s2, d = s3; + for (int i = 0; i < 10; 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); + c = vextq_u32(c, c, 2); + d = vextq_u32(d, d, 3); + QUARTER(a, b, c, d); + /* shift back. */ + b = vextq_u32(b, b, 3); + c = vextq_u32(c, c, 2); + d = vextq_u32(d, d, 1); + } + *v0 = vaddq_u32(a, s0); + *v1 = vaddq_u32(b, s1); + *v2 = vaddq_u32(c, s2); + *v3 = vaddq_u32(d, s3); +} + +static const uint32_t chacha_constants[4] = { + 0x61707865u, 0x3320646eu, 0x79622d32u, 0x6b206574u +}; + +/* Set up the constant rows of the state from key + nonce. s3 + * (counter + nonce) varies per block and is built inside the loop. */ +static inline void chacha20_setup(const uint8_t key[32], + const uint8_t nonce[12], + uint32x4_t *s0, uint32x4_t *s1, + uint32x4_t *s2, + uint32_t *n0, uint32_t *n1, + uint32_t *n2) { + *s0 = vld1q_u32(chacha_constants); + *s1 = vreinterpretq_u32_u8(vld1q_u8(key)); + *s2 = vreinterpretq_u32_u8(vld1q_u8(key + 16)); + memcpy(n0, nonce + 0, 4); + memcpy(n1, nonce + 4, 4); + memcpy(n2, nonce + 8, 4); +} + +/* + * Generate one 64-byte ChaCha20 keystream block at 'out'. + */ +void chacha20_block_native(const uint8_t key[32], uint32_t counter, + const uint8_t nonce[12], uint8_t out[64]) { + uint32x4_t s0, s1, s2; + uint32_t n0, n1, n2; + chacha20_setup(key, nonce, &s0, &s1, &s2, &n0, &n1, &n2); + + 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); + + vst1q_u8(out + 0, vreinterpretq_u8_u32(v0)); + vst1q_u8(out + 16, vreinterpretq_u8_u32(v1)); + vst1q_u8(out + 32, vreinterpretq_u8_u32(v2)); + vst1q_u8(out + 48, vreinterpretq_u8_u32(v3)); +} + +/* + * 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. + */ +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) { + uint32x4_t s0, s1, s2; + uint32_t n0, n1, n2; + chacha20_setup(key, nonce, &s0, &s1, &s2, &n0, &n1, &n2); + + size_t pos = 0; + while (pos + 64 <= inlen) { + 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); + + uint8x16_t i0 = vld1q_u8(in + pos + 0); + uint8x16_t i1 = vld1q_u8(in + pos + 16); + uint8x16_t i2 = vld1q_u8(in + pos + 32); + uint8x16_t i3 = vld1q_u8(in + pos + 48); + + vst1q_u8(out + pos + 0, + veorq_u8(i0, vreinterpretq_u8_u32(v0))); + vst1q_u8(out + pos + 16, + veorq_u8(i1, vreinterpretq_u8_u32(v1))); + vst1q_u8(out + pos + 32, + veorq_u8(i2, vreinterpretq_u8_u32(v2))); + vst1q_u8(out + pos + 48, + veorq_u8(i3, vreinterpretq_u8_u32(v3))); + + pos += 64; + counter++; + } + + /* trailing partial block (< 64 bytes) */ + if (pos < inlen) { + 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); + + uint8_t block[64]; + vst1q_u8(block + 0, vreinterpretq_u8_u32(v0)); + vst1q_u8(block + 16, vreinterpretq_u8_u32(v1)); + vst1q_u8(block + 32, vreinterpretq_u8_u32(v2)); + vst1q_u8(block + 48, vreinterpretq_u8_u32(v3)); + + size_t remaining = inlen - pos; + for (size_t i = 0; i < remaining; i++) { + out[pos + i] = in[pos + i] ^ block[i]; + } + } +} + +int chacha20_native_available(void) { + return 1; +} + +#else + +/* stubs for non-aarch64 builds; never reached because dispatch is + * 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; +} + +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) { + (void)key; (void)counter; (void)nonce; + (void)in; (void)out; (void)inlen; +} + +int chacha20_native_available(void) { + return 0; +} + +#endif diff --git a/lib/Crypto/Cipher/ChaCha20.hs b/lib/Crypto/Cipher/ChaCha20.hs @@ -36,7 +36,7 @@ module Crypto.Cipher.ChaCha20 ( ) where import Control.Monad.ST -import qualified Crypto.Cipher.ChaCha20.Arm as Arm +import qualified Crypto.Cipher.ChaCha20.Native as Native import qualified Data.Bits as B import Data.Bits ((.|.), (.<<.), (.^.)) import qualified Data.ByteString as BS @@ -293,8 +293,8 @@ block block key@(BI.PS _ _ kl) counter nonce@(BI.PS _ _ nl) | kl /= 32 = Left InvalidKey | nl /= 12 = Left InvalidNonce - | Arm.chacha20_arm_available = - Right (Arm.block key counter nonce) + | Native.chacha20_native_available = + Right (Native.block key counter nonce) | otherwise = pure $ runST $ do let k = _parse_key key n = _parse_nonce nonce @@ -354,8 +354,8 @@ cipher | kl /= 32 = Left InvalidKey | nl /= 12 = Left InvalidNonce | pl > room = Left CounterOverflow - | Arm.chacha20_arm_available = - Right (Arm.cipher raw_key counter raw_nonce plaintext) + | Native.chacha20_native_available = + Right (Native.cipher raw_key counter raw_nonce plaintext) | otherwise = pure $ runST $ do let key = _parse_key raw_key non = _parse_nonce raw_nonce diff --git a/lib/Crypto/Cipher/ChaCha20/Arm.hs b/lib/Crypto/Cipher/ChaCha20/Arm.hs @@ -1,82 +0,0 @@ -{-# OPTIONS_HADDOCK hide #-} -{-# LANGUAGE BangPatterns #-} - --- | --- Module: Crypto.Cipher.ChaCha20.Arm --- Copyright: (c) 2025 Jared Tobin --- License: MIT --- Maintainer: Jared Tobin <jared@ppad.tech> --- --- ARM NEON support for the ChaCha20 stream cipher. - -module Crypto.Cipher.ChaCha20.Arm ( - chacha20_arm_available - , block - , cipher - ) where - -import qualified Data.ByteString as BS -import qualified Data.ByteString.Internal as BI -import Data.Word (Word8, Word32) -import Foreign.C.Types (CInt(..), CSize(..)) -import Foreign.ForeignPtr (withForeignPtr) -import Foreign.Ptr (Ptr, plusPtr) -import System.IO.Unsafe (unsafeDupablePerformIO) - --- ffi ------------------------------------------------------------------------ - -foreign import ccall unsafe "chacha20_block_arm" - c_chacha20_block - :: Ptr Word8 -> Word32 -> Ptr Word8 -> Ptr Word8 -> IO () - -foreign import ccall unsafe "chacha20_cipher_arm" - c_chacha20_cipher - :: Ptr Word8 -> Word32 -> Ptr Word8 - -> Ptr Word8 -> Ptr Word8 -> CSize -> IO () - -foreign import ccall unsafe "chacha20_arm_available" - c_chacha20_arm_available :: IO CInt - --- utilities ------------------------------------------------------------------ - -fi :: (Integral a, Num b) => a -> b -fi = fromIntegral -{-# INLINE fi #-} - --- api ------------------------------------------------------------------------ - --- | Are ARM NEON extensions available? -chacha20_arm_available :: Bool -chacha20_arm_available = - unsafeDupablePerformIO c_chacha20_arm_available /= 0 -{-# NOINLINE chacha20_arm_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 _) = - BI.unsafeCreate 64 $ \dst -> - withForeignPtr kfp $ \kp0 -> - withForeignPtr nfp $ \np0 -> - c_chacha20_block (kp0 `plusPtr` koff) - counter - (np0 `plusPtr` noff) - dst - --- | XOR the plaintext with the ChaCha20 keystream derived from the --- given (already-validated) key, counter, and nonce. -cipher - :: BS.ByteString -> Word32 -> BS.ByteString -> BS.ByteString - -> BS.ByteString -cipher (BI.PS kfp koff _) counter (BI.PS nfp noff _) - (BI.PS pfp poff plen) = - BI.unsafeCreate plen $ \dst -> - withForeignPtr kfp $ \kp0 -> - withForeignPtr nfp $ \np0 -> - withForeignPtr pfp $ \pp0 -> - c_chacha20_cipher (kp0 `plusPtr` koff) - counter - (np0 `plusPtr` noff) - (pp0 `plusPtr` poff) - dst - (fi plen) diff --git a/lib/Crypto/Cipher/ChaCha20/Native.hs b/lib/Crypto/Cipher/ChaCha20/Native.hs @@ -0,0 +1,82 @@ +{-# OPTIONS_HADDOCK hide #-} +{-# LANGUAGE BangPatterns #-} + +-- | +-- Module: Crypto.Cipher.ChaCha20.Native +-- Copyright: (c) 2025 Jared Tobin +-- License: MIT +-- Maintainer: Jared Tobin <jared@ppad.tech> +-- +-- ARM NEON support for the ChaCha20 stream cipher. + +module Crypto.Cipher.ChaCha20.Native ( + chacha20_native_available + , block + , cipher + ) where + +import qualified Data.ByteString as BS +import qualified Data.ByteString.Internal as BI +import Data.Word (Word8, Word32) +import Foreign.C.Types (CInt(..), CSize(..)) +import Foreign.ForeignPtr (withForeignPtr) +import Foreign.Ptr (Ptr, plusPtr) +import System.IO.Unsafe (unsafeDupablePerformIO) + +-- ffi ------------------------------------------------------------------------ + +foreign import ccall unsafe "chacha20_block_native" + c_chacha20_block + :: Ptr Word8 -> Word32 -> Ptr Word8 -> Ptr Word8 -> IO () + +foreign import ccall unsafe "chacha20_cipher_native" + c_chacha20_cipher + :: Ptr Word8 -> Word32 -> Ptr Word8 + -> Ptr Word8 -> Ptr Word8 -> CSize -> IO () + +foreign import ccall unsafe "chacha20_native_available" + c_chacha20_native_available :: IO CInt + +-- utilities ------------------------------------------------------------------ + +fi :: (Integral a, Num b) => a -> b +fi = fromIntegral +{-# INLINE fi #-} + +-- api ------------------------------------------------------------------------ + +-- | Are ARM NEON extensions available? +chacha20_native_available :: Bool +chacha20_native_available = + 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 _) = + BI.unsafeCreate 64 $ \dst -> + withForeignPtr kfp $ \kp0 -> + withForeignPtr nfp $ \np0 -> + c_chacha20_block (kp0 `plusPtr` koff) + counter + (np0 `plusPtr` noff) + dst + +-- | XOR the plaintext with the ChaCha20 keystream derived from the +-- given (already-validated) key, counter, and nonce. +cipher + :: BS.ByteString -> Word32 -> BS.ByteString -> BS.ByteString + -> BS.ByteString +cipher (BI.PS kfp koff _) counter (BI.PS nfp noff _) + (BI.PS pfp poff plen) = + BI.unsafeCreate plen $ \dst -> + withForeignPtr kfp $ \kp0 -> + withForeignPtr nfp $ \np0 -> + withForeignPtr pfp $ \pp0 -> + c_chacha20_cipher (kp0 `plusPtr` koff) + counter + (np0 `plusPtr` noff) + (pp0 `plusPtr` poff) + dst + (fi plen) diff --git a/ppad-chacha.cabal b/ppad-chacha.cabal @@ -37,13 +37,13 @@ library exposed-modules: Crypto.Cipher.ChaCha20 other-modules: - Crypto.Cipher.ChaCha20.Arm + Crypto.Cipher.ChaCha20.Native build-depends: base >= 4.9 && < 5 , bytestring >= 0.9 && < 0.13 , primitive >= 0.8 && < 0.10 c-sources: - cbits/chacha20_arm.c + cbits/chacha20_native.c if arch(aarch64) cc-options: -march=armv8-a if flag(sanitize)