I’m currently adding multi-thread support for Argon2 in Monocypher. Without depending on any multi-thread API at all. I’m sticking to the “pure portable C99 with zero dependency” that makes this little library so easy to integrate pretty much everywhere.
Instead I chopped up the API into an init()update()final() structure that let users implement parallelism however they want:
The constraints for Monocypher are more or less, dependable C with zero dependencies. For me that’s C89 with volatile, // style comments, and declaring local variables anywhere. My only dependencies are stddef.h and stdint.h, from which I take size_t and the fixed size integers. I have no explicit runtime dependency, not even LibC — though compilers do sometime turn a loop of mine into a memcpy(3) call.
There’s not much I can do under such drastic constraints:
No networking.
No I/O.
No explicit program aborts.
No heap allocation.
No multi-threading.
Monocypher does not, and will not, do multithreading. Only support it.
The code you see above is what I plan to ship:
A toolkit for users to write their own implementation of Argon2.
An single-thread implementation that uses that toolkit. The comments there aren’t pseudocode, they’re just comments, meant to document how one may parallelise Argon2.
Basically, Monocypher will “support” multi-threading by leaving it as an exercise to the user. That’s cheating, I know, but as far as I can tell multi-threading is the easy part. Here’s my pthreads code, minus error handling to reduce clutter:
I’ll need to add proper error handling, account for EAGAIN where appropriate, and perhaps add a couple asserts to guard the functions that are supposed to never fail. The result will go to the user manual, for users to copy & paste, or adapt to their own multi-threading API.
I use a similar approach with EdDSA, by the way. In Monocypher3 I had direct support for an incremental signature API, but the only way I could make it work with both BLAKE2b EdDSA and Ed25519 was a full blows v-table. It did the job, but it ultimately proved very inflexible.
Then one day a company approached me to ad Ed25519ph to Monocypher. Pretty quickly it was clear to me that my incremental interface with swappable hash couldn’t do it. I had to take my source code apart to assemble what they needed. It took me but a day, which they graciously paid full rate for.
That work gave me an idea: what if instead of using a v-table to swap in the parts I want into the full algorithm, I instead cut the algorithm into coarse constituent parts, and then use those parts to assemble whatever variation of the algorithm I, or my users, may need? Sure it falls short of direct support, but who cares, as long as it gives us the capability?
I think I managed to make it work. Heres’ Ed25519 and Ed25519ph, from the toolkit and SHA-512:
It’s not trivial. It’s not even easy, which is why it (optionally) ships with Monocypher. But it’s possible: one can look at the Ed25519 and Ed25519ph specs, look at Monocypher’s API (which you will note hides away the elliptic curve stuff), and come up with pretty much this code.
Argon2 by contrast is genuinely easy. I’m very happy about that, because it removes a major weakness from Monocypher. With that, it will get the compatibility and security that comes with processing parallel lanes in parallel. (One thing that makes password hashing & key derivation so tricky, is how its security is directly proportional to the speed of its implementation.)
Now I need to do some vectorisation… In pure C99. Can’t have it all, but I did manage to get significant gains from auto-vectorisation alone. With any luck, I may even saturate the memory bus.
I’m currently adding multi-thread support for Argon2 in Monocypher. Without depending on any multi-thread API at all. I’m sticking to the “pure portable C99 with zero dependency” that makes this little library so easy to integrate pretty much everywhere.
Instead I chopped up the API into an
init()update()final()structure that let users implement parallelism however they want:The single thread implementation goes like this:
Early experiments with pthreads suggest this should easily accommodate any multi-thread API.
Next step is documentation, and provide a production worthy code example.
score 1 · canonical
How do you do the sync point in C99? C11 has mechanisms for establishing the happens-before relationship here, C99 does not.
score 1 · canonical
That’s the thing: I don’t.
The constraints for Monocypher are more or less, dependable C with zero dependencies. For me that’s C89 with
volatile,//style comments, and declaring local variables anywhere. My only dependencies arestddef.handstdint.h, from which I takesize_tand the fixed size integers. I have no explicit runtime dependency, not even LibC — though compilers do sometime turn a loop of mine into amemcpy(3)call.There’s not much I can do under such drastic constraints:
Monocypher does not, and will not, do multithreading. Only support it.
The code you see above is what I plan to ship:
Basically, Monocypher will “support” multi-threading by leaving it as an exercise to the user. That’s cheating, I know, but as far as I can tell multi-threading is the easy part. Here’s my pthreads code, minus error handling to reduce clutter:
I’ll need to add proper error handling, account for EAGAIN where appropriate, and perhaps add a couple asserts to guard the functions that are supposed to never fail. The result will go to the user manual, for users to copy & paste, or adapt to their own multi-threading API.
I use a similar approach with EdDSA, by the way. In Monocypher3 I had direct support for an incremental signature API, but the only way I could make it work with both BLAKE2b EdDSA and Ed25519 was a full blows v-table. It did the job, but it ultimately proved very inflexible.
Then one day a company approached me to ad Ed25519ph to Monocypher. Pretty quickly it was clear to me that my incremental interface with swappable hash couldn’t do it. I had to take my source code apart to assemble what they needed. It took me but a day, which they graciously paid full rate for.
That work gave me an idea: what if instead of using a v-table to swap in the parts I want into the full algorithm, I instead cut the algorithm into coarse constituent parts, and then use those parts to assemble whatever variation of the algorithm I, or my users, may need? Sure it falls short of direct support, but who cares, as long as it gives us the capability?
I think I managed to make it work. Heres’ Ed25519 and Ed25519ph, from the toolkit and SHA-512:
It’s not trivial. It’s not even easy, which is why it (optionally) ships with Monocypher. But it’s possible: one can look at the Ed25519 and Ed25519ph specs, look at Monocypher’s API (which you will note hides away the elliptic curve stuff), and come up with pretty much this code.
Argon2 by contrast is genuinely easy. I’m very happy about that, because it removes a major weakness from Monocypher. With that, it will get the compatibility and security that comes with processing parallel lanes in parallel. (One thing that makes password hashing & key derivation so tricky, is how its security is directly proportional to the speed of its implementation.)
Now I need to do some vectorisation… In pure C99. Can’t have it all, but I did manage to get significant gains from auto-vectorisation alone. With any luck, I may even saturate the memory bus.
score 1 · canonical