nsec icon indicating copy to clipboard operation
nsec copied to clipboard

Support for more algorithms in libsodium

Open samuel-lucas6 opened this issue 3 years ago • 4 comments

The current state of libsodium bindings for .NET is a mess. For example, here's the date of the last commit in each repo:

Besides ASodium, which was only recently developed, all of these bindings are no longer being maintained. I would also argue that it's unlikely that ASodium will be maintained in the long-term like NSec has been. Furthermore, these implementations have awful naming in my opinion and don't employ the memory protection that NSec does. Therefore, at first glance, it seems that NSec is the best libsodium binding for .NET.

However, NSec doesn't support a lot of the algorithms available in libsodium, which is a serious problem considering that it's really the only maintained binding. I completely understand why for some of them (e.g. not supporting unauthenticated ChaCha20 to encourage AEAD use, although that's unfortunate for building custom constructions), but others should be present. For instance, as briefly discussed in #27, there's no password-based KDF (aka there's no Argon2). Several of your arguments for not adding Argon2 simply don't hold up. For the parameter selection, you could just encourage the libsodium defaults and enforce sensible lower limits for the memory size and iterations. The algorithm has received wide adoption at this point, so it frankly doesn't matter whether the RFC is in the draft stage or not. Lastly, I don't see how dealing with out-of-memory exceptions is an issue when none of the other bindings seem to consider it to be a problem.

Another algorithm that should be added is XChaCha20-Poly1305, which is again widely used at this point and a more sensible option than ChaCha20-Poly1305 in many cases. I suspect your argument for not adding this is that the RFC is again in the draft stage. However, that RFC isn't very good anyway since it specifies a smaller counter than libsodium and Monocypher. Moreover, if an algorithm is accessible in libsodium, then it's probably being used, which is what's important rather than whether or not something has an RFC, especially considering how difficult it is to write an RFC and get it past the draft stage.

Then there's no secretstream support, which would save people the hassle of implementing their own stream encryption, not to mention the mistakes that often brings. There's also no crypto_box support despite that being quite popular from what I've seen, although I can understand not including this since it does rely on XSalsa20-Poly1305. There's only HKDF support rather than support for the default KDF in libsodium (salted BLAKE2b). The recommendation for generating random bytes/numbers is to use System.Security.Cryptography.RandomNumberGenerator rather than providing simple functions for libsodium, which would save people a few lines of code. Finally, there doesn't seem to be any constant time functions, and there's no padding function.

In sum, a lot of very useful algorithms in libsodium appear to be missing without justification for their absence. I can understand wanting to keep the library small, but the lack of a password-based KDF, especially when the only one available in .NET is PBKDF2, is ridiculous and so is the fact that XChaCha20-Poly1305 is missing. The presence of most of the other functions I've discussed more or less comes down to whether or not this library is trying to prevent people from making mistakes, which is implied by the 'NSec wants you to fall into the pit of success' claim.

PS, I apologise if I've made any inaccurate claims about the library. I haven't actually used NSec besides benchmarking the HKDF implementation because it doesn't include algorithms like Argon2 and XChaCha20.

samuel-lucas6 avatar Jul 25 '21 14:07 samuel-lucas6

Thanks for sharing your thoughts on this topic. I agree that the state of libsodium bindings for .NET is not very satisfying: libsodium-net is dead, the development of libsodium-core seems to have faded, and I share your concerns regarding the long-term maintenance of the other forks.

NSec is maybe a bit different from the other bindings in that it’s first and foremost an attempt to provide a well-designed cryptographic API for .NET and based on libsodium only secondarily (so not really a "binding"). I agree that Argon2id and XChaCha20-Poly1305 should be part of NSec. I was hoping for a faster progress to RFC for those (I’m not really sure why they got stuck), but it doesn’t seem to make much sense to keep waiting. Lower-layer primitives such as secretstream are really out of scope for NSec, however. (Although I could imagine those being used to implement some higher-level primitives in NSec provided there’s a well reviewed, preferably standardized specification.)

Given that there are still quite a few users of the ancient libsodium-net and libsodium-core packages, I was thinking if it’d make sense to maintain a compatible, "legacy" API as a NSec project (i.e., no new features but fixing bugs for people who are stuck with it). Alternatively, it would be possible to extend the internal libsodium binding that NSec is using (the Interop project) with all the missing functions and to expose that as a low-level/"use at your own risk" API (i.e., auto-generated with little to no error checking for people who know what they’re doing). Both options would mean a lot of effort for little reward, though...

ektrah avatar Jul 26 '21 21:07 ektrah

I'm glad you agree. I don't know what the IETF is doing either, but from what I've seen, it doesn't seem worth the effort of making an RFC in many cases. The formatting requirements are even more complicated than APA style, and several decent ideas have stayed as drafts. Scott Arciszewski apparently got ghosted by the IETF. Standardisation is obviously important, but the process appears to be very messy.

Fair enough about keeping secretstream out of scope. A 'legacy' API and 'use at your own risk' API are interesting ideas, but you make a good point. I suppose it comes down to how many people are likely to use them.

I actually started working on my own libsodium binding called Geralt, originally just to fix the naming in libsodium-core. However, I was thinking of starting over and turning it into a misuse resistant library with only the latest algorithms. The problem is that I can't say whether I'd be able to maintain it well enough for it to be anything more than personal project. Several calls to libsodium (e.g. secretstream) also look difficult to implement, it would be more basic than NSec in terms of probably not having memory protection and just passing around byte arrays, and it wouldn't completely fill the gap since there would be no legacy algorithms.

samuel-lucas6 avatar Jul 27 '21 09:07 samuel-lucas6

Nice work. I’d be interested in how you’re planning to make libsodium misuse resistant (or if you’re planning to just drop everything that isn’t?).

Overall, I think it’s clear that there is a need for something besides NSec; otherwise, there wouldn’t be yet another fork of libsodium-core every couple of months. I would be happy to contribute to cleaning up that mess, e.g., by maintaining an additional API under the NSec umbrella. I guess the difficult question is how that API should look like and how to get people on board with it.

Maybe one way could be to reach out to projects with a dependency on libsodium-core or one of the forks and try to come up with a common API proposal for a long-term successor to libsodium-core?

ektrah avatar Jul 28 '21 12:07 ektrah

Nice work. I’d be interested in how you’re planning to make libsodium misuse resistant (or if you’re planning to just drop everything that isn’t?).

Thanks. I need to get back to it after some smaller projects I'm working on. The plan for that still needs to be worked out if I'm honest, but I might follow Google Tink's example. I need to look into how they handle nonces for things like ChaCha20-Poly1305, but I was thinking of offering functions with Streams as well as byte arrays for messages, which would be useful for encrypting files for example. Then I was thinking of restricting the BLAKE2b output size to 256-bit or 512-bit, enforcing 16 or 32+ MiB of RAM for the Argon2id memory size, replacing crypto_box with X25519 and XChaCha20-Poly1305, and having verify functions for BLAKE2b as a MAC. That kind of thing.

In terms of the available primitives, I probably won't bother with AES-GCM, SHA2, HMAC, scrypt, Argon2i, Poly1305, ChaCha20, XChaCha20, Salsa20, XSalsa20, XSalsa20-Poly1305, Ed25519 to Curve25519, SipHash, or AEGIS. Keeping it simple like Monocypher has done will make things easier. I might offer HKDF though because the personal size limit for BLAKE2 is strange, or I could use the message parameter for context information rather than letting the user fiddle with the personal.

Overall, I think it’s clear that there is a need for something besides NSec; otherwise, there wouldn’t be yet another fork of libsodium-core very couple of months. I would be happy to contribute to cleaning up that mess, e.g., by maintaining an additional API under the NSec umbrella. I guess the difficult question is how that API should look like and how to get people on board with it.

Maybe one way could be to reach out to projects with a dependency on libsodium-core or one of the other forks and try to come up with a common API proposal for a long-term successor to libsodium-core?

Agreed. I expect people want to stick with byte arrays but just have better naming and bugs fixed. I'd be content with that even though having memory protection is a good security strategy. Reaching out is a nice idea, but it would be time-consuming and probably difficult to get appropriate feedback.

samuel-lucas6 avatar Jul 28 '21 20:07 samuel-lucas6

Coming back to the original issue, support for a few more algorithms in libsodium has been added to NSec and NSec.Experimental (see the commits linked above). I'll close this issue, but please feel free to open a new issue if you think that additional algorithms should be provided.

ektrah avatar Nov 27 '22 13:11 ektrah