I am also curious about your motivation. Are you planning to use NistyPQC.jl in other projects and develop other cryptographic libraries in Julia?
For now I just want to better understand NIST’s PQC algorithms. Hence, my decision to implement them from scratch. Julia seemed a good choice to do that, so as not to get lost in implementation details.
It is quite interesting to see how you have modularised code. […] I put the cryptographic parameters within a type parameter and then explicitly write every function signature with where. That though has a hurdle that type parameters need to be bitstypes which complicates things.
I also started out with packing parameters into types. But as you hint at, dealing with the more complex parameters for some of the algorithms (like floating-point numbers, hash functions etc.) then easily results in cluttered code. In contrast, the “amusing” metaprogramming solution makes the code look more concise by avoiding convoluted type annotations. On the downside, it probably increases compile time and compiled size. But I don’t consider this to be an issue. Moreover, currently many methods will be defined separately once for each parameter set, even in cases where resolving parameters at compile time gives no significant gain in performance. However, for readability and consistency among the algorithms, I deliberately didn’t factor them out to be shared among the modules for different parameter sets.
All that said, I’m open for suggestions how to modularize in a different, perhaps more Julian way.
Some minor things I noticed that you were using @inbounds. Does it improve performance in your situation?
It does. Anyway, since the implementations are not meant to be highly optimized for speed, I will follow your suggestion and reactivate bounds checking in the future.
I also noticed the use of @eval in get_hashers function in NistyPQC.jl/src/SLHDSA/Parameters.jl at main · erich-9/NistyPQC.jl · GitHub. Perhaps you can put that code outside evaluating for all possible n or refactor to use n and possibly omega as a type parameter for a hasher which is constructing.
Each of the files Parameters.jl
merely automates setting up the different variants of the respective algorithm and computes derived parameters which typically are hard-coded in real-world implementations. So I don’t bother too much about them.
But I agree that get_hashers
is one of the functions that deserves an overhaul. For example switching from hash functions to hash contexts here might be a good idea to improve performance a bit. In fact, SLH-DSA is one of the slowest algorithms due to the high cost of the many hashing operations.
I also noticed the use of default_rng(), which is unsafe.
I’d like to stick with Julia’s default. So this was a deliberate choice. The RNG used by the package can easily be changed with NistyPQC.set_rng
.
I’m well aware that default_rng()
is not cryptographically secure. But then the package is not intended to be used thoughtlessly for security relevant applications anyway.
It would actually be good to CSPRG in a separate package from CryptoGroups.jl and also implement one used within a FIPS standard. If you are interested in that kind of thing let me know.
Yes, definitely interested. As a matter of fact, for testing I needed CTR_DRBG
with AES256
to check against “known answer tests”. Since I didn’t find a Julia package providing it, I wrote my own ad-hoc implementation here: test/Utilities/NistDRBG.jl
Finally, thank you very much for taking a look at the code and for your feedback!
I’ll check out your CryptoGroups.jl
project later.