ChaChaCiphers.jl
I’m happy to announce ChaChaCiphers.jl
, a pure-Julia implementation of the ChaCha stream cipher family. This package aims to provide
- fast, cryptographically-secure, and reproducible random number generators for CPU and GPU, and
- building blocks that can be used for cryptographic primitives such as ChaCha20-Poly1305.
Usage
To use ChaChaCiphers.jl for cryptographically secure random number generation (CRNG), you can create a new ChaChaStream
instance and feed it in as the rng
parameter to functions like rand
or randn
:
julia> using ChaChaCiphers
julia> rng = ChaCha20Stream();
julia> rand(rng, Float32, 3)
3-element Vector{Float32}:
0.2602576
0.7704005
0.96076345
julia> rand(rng, 1:10)
9
julia> randn(rng)
-0.8894196311020175
The GPU interface is still a WIP, but you can use Random.rand!
on a CuArray
with a CUDAChaChaStream
instance to fill it with random values:
julia> using CUDA, Random
julia> x = CuVector{Float32}(undef, 1024);
julia> rng = CUDAChaCha20Stream();
julia> Random.rand!(rng, x);
Performance
Here are some simple benchmarks to give you an idea of the package’s current performance. Because the security requirements for a CRNG are much higher than those of other PRNGs like Xoshiro, it is by necessity much slower than using the default RNG, although
- it’s still much faster than reading from
RandomDevice()
, and - you can speed things up by using fewer ChaCha rounds (e.g., by using
rng = ChaCha12Stream()
):
CPU performance
shell> sh -c "lscpu | grep 'Model name'"
Model name: Intel(R) Xeon(R) Silver 4210 CPU @ 2.20GHz
julia> using BenchmarkTools, CUDA, Random, ChaChaCiphers
julia> rng = ChaCha20Stream();
julia> urandom = RandomDevice();
julia> x = Vector{UInt8}(undef, 2^20);
julia> @btime Random.rand!(rng, x); # ChaChaCiphers.jl
609.709 μs (4 allocations: 192 bytes)
julia> @btime Random.rand!(x); # Default Xoshiro++ RNG
30.512 μs (0 allocations: 0 bytes)
julia> @btime Random.rand!(urandom, x); # OS CRNG
5.848 ms (0 allocations: 0 bytes)
GPU performance
shell> nvidia-smi --query-gpu=gpu_name --format=csv -i 0
name
Quadro RTX 4000
julia> rng = CUDAChaCha20Stream();
julia> x = CuVector{UInt8}(undef, 2^30);
# Warm-up omitted
julia> CUDA.@elapsed Random.rand!(rng, x)
0.40175077f0
julia> CUDA.@elapsed Random.rand!(x)
0.024985567f0
What’s next
There’s still plenty of work to be done to improve performance, and eventually I’d like to expand the package to include other stream ciphers in the ChaCha family (e.g. XChaCha, and an IETF RFC 8439-compliant implementation of ChaCha20, as well as some more practical system improvements to improve security. On the whole though, I’d say it’s at least in its minimal usable state for CRNG-dependent applications.