I am trying to add a simple Hybrid cryptosystem to an app I am creating. As motivating example, suppose all I want to do is encrypt a symmetric key with a public RSA + OAEP key, and then (on a different machine that has the private key) recover the symmetric key.
Does there exist a standard, officially supported cryptography package written in nim that offers:
An example of something that is EXACTLY what I am looking for, but written in python, is https://cryptography.io/en/latest/ (more specifically, their hazmat module exposes RSA +OAEP).
https://nim-lang.org/docs/openssl.html looks like a decent candidate since I could always statically link openssl binaries but it appears to be missing bindings for rsautil effectively preventing access directly to RSA encrypt/decrypt + sign/verify calls.
Does there exist a standard, officially supported cryptography package written in nim
I don't see "written in Nim" as an advantage. Crypto primitives should not be reimplemented lightly, since they're a big source of security vulnerabilities. The code doesn't even have to be buggy to cause problems: something 'minor' like timing differences, or variations in how it uses CPU caches, can be used in an attack.
Ideally you'd use a simple Nim wrapper around a well-known, mature, audited crypto library. (Probably written in C, because most such libraries are.)
If I were going to start using crypto in Nim, I'd probably go with monocypher or libSodium.
Regarding your cryptographic primitives needs we would need to know more about your application:
What kind of cryptography do you need?
What is your threat model?
No, not really. OpenSSL is a cancerous abomination and a catastrophy generator.
Well, it's a bit less bad than OpenSSL. But: hardware support can be a critical element.
Yes, it's probably the best solution for general needs and common (non expert) users.
- "monocypher is a new alternative to libSodium that's got a smaller codebase. It's less mature but it did recently pass a security audit. It has a Nim wrapper too."
monocypher is not a full replacement for libsodium and far less battle proven.
I hope he not attached at all because RSA is inferior to and less desirable than ECC, especially Montgomery and Edwards based, in basically all respects.
Some general remarks
First what is probably the most important: For many (usually large) organisations and corporations NIST approved algos/protocols are an absolute must. If one doesn't operate globally and/or needs no insurance approval etc. one should avoid anything and everything that's tainted by NIST. One major reason for that is the fact that the correct spelling of NIST turned out to often be "NSA". (Side note: just very recently NIST published the PQ round 3 candidates. 3 out of 4 en/de-cryption candidates have a serious shortcoming; they can decrypt what they encrypted in 1 out of 2 to the 100 to (best case) 2 to the 160 cases. That is not a good decision. While one might accept such an algo for being extremely attractive otherwise, one certainly would not want to pick 3 out of 4 algos with that problem). Accordingly I (and quite a few others in our field) actually consider NIST approval as a red flag.
Second point: try to avoid SSL if any possible for quite a few reasons. One being that SSL was designed and developed for a use case that high likely is not yours. Another one being that there is plenty indications and evidence for SSL implementations not being safe and secure (and actually often poorly implemented). Another - and obvious - reason is that size matters; the more code the more and the more likely errors.
Linked to that: Try to use the smallest solution that meets the criteria. The question whether some library has a Nim binding/wrapper is less of hardly even secondary importance because those are trivial to generate.
Finally, the advice to not implement crypto in Nim is sound in my opinion, unless you really know very well what you are doing and verify the generated code.
No, not really. OpenSSL is a cancerous abomination and a catastrophy generator.
I tend to agree; I was trying to be polite earlier :)
Second point: try to avoid SSL if any possible for quite a few reasons.
I didn't use to agree with this; I figured it's best to use a battle-tested longtime industry standard that plays well with middleware. But my team and I have spent the last several months implementing cross-platform TLS support for a P2P protocol, and it's such a giant pain in the butt.
NOISE looks like a decent alternative, from my layman's perspective. Still kind of complex, but I haven't seen any simpler general-purpose recipes. Two protocols I'm researching, Dat and Scuttlebutt, implement their own handshake and messaging protocols on top of primitives provided by libSodium.
- implementing cross-platform TLS support for a P2P protocol, and it's such a giant pain in the butt.
Sorry but that's not the relevant point from a security perspective. From the practical point of view it of course can be a killer. What, however, is an important point is that complexity begets bugs which shows in virtually al SSL/TLS libraries (with OpenSSL being the worst of course).
- decent alternative
Depends on what "decent" is to mean.
Tips: First and mainly get rid of the association "security ~ SSL/TLS". Not only is the basis of that association shaky and not only have we seen multiple gross failures but that association also tends to bend ones mind and approach.
Be sure to have a clear picture of what you really want/need and what Eve looks like (what the security context is). Starting with an aggressive Dolev-Yao model might feel cool but it also means that you are unlikely to achieve any proper solution. So be sure to have a reasonable definition of Eve (against what you want to be resilient).
There are tools available that can greatly support you in protocol (and hence context) analysis. Unfortunately though they are frighteningly difficult to use or at least seem to be for most developers. However with a solid background in both security and math they allow you to verify (and optimize) your design.
Stick to a reasonable minimum. SSL/TLS promises to deliver (and contains code) for next to everything incl. the kitchen sink. This translates to an unnecessary large code base (which translates to more bugs/higher failure probablity) and also makes it unnecessarily hard to use.
Example: You want "something like/similar to a TLS handshake + KEX"? That can be done with libsodium. You want fast payload crypto? That can be done with libsodium too. You want the usual assortment of "the right way to do things" like e.g. constant time comparisons? libsodium delivers.
Now, of course libsodium goes only that far (otherwise I wouldn't have a job /grin) but I'd put it like this: libsodium is damn good enough for 90% of the jobs out there and if yours happens to fall in the remaining 10%, tough luck, then you must spend some years learning and getting experience in real and hard security (and learn Ada (for the time being. Nim might become a reasonable option)). But from what I see here it seems that you can get your project done using libsodium and some good basic understanding (e.g. first MAC or first encrypt?).
About the only reason I myself don't use libsodium for many jobs is that they (a) have a somewhat poor variety of algos (but a quite reasonable one for most non hardcore security/crypto developers) and, more gravely, (b) that they tend to go with the most common and "cool" algos rather than the best ones. But again, for most non-expert jobs libsodium is damn good enough, has no critical flaw, and is well respected for good reasons.
I wrote the https://github.com/FedericoCeratto/nim-libsodium https://github.com/FedericoCeratto/nim-gnutls wrappers for the reasons listed so far: implementing cryptography from scratch is always very risky, also OpenSSL is difficult to use securely.
It's also important to receive fast security updates from the OS without having to rebuild entire applications.
I'm happy to improve the libsodium wrapper and contributions are welcome.
At this point, any GNU tools should actively be avoided for any cryptographic purposes. There isn't any crypto-related GNU project that should be used.
See:
@federico3, it’s great to have Nim wrappers! I’d like to see higher level APIs, though. In particular, I feel that a public API should never use 'ptr’ unless absolutely necessary ... in this case, ‘array[byte]’ would be a better choice for keys/digests/nonces, and ‘seq[byte]’ for cipher/clear-text.
Actually the best would be to use ‘distinct’ types — one of my gripes about the libSodium C API is that it has no type checking, making it possible to pass the wrong type of key, or a key where a digest should go, with no warnings.
- At this point, any GNU tools should actively be avoided for any cryptographic purposes.
Yes, absolutely. In fact, if there is any worse clusterf_ck than OpenSSL it's GnuTLS and other Gnu crypto.
@snej
- feel that a public API should never use 'ptr’ unless absolutely necessary
I happen to have written wrappers around quite a few libsodium procedures and I'm happy to report that not a single ptr was needed. Of course var, ref, (open)arrays end up as pointers but looking at it from Nim's side they are non-ptr and well understood abstractions.
- one of my gripes about the libSodium C API is that it has no type checking, making it possible to pass the wrong type of key, or a key where a digest should go, with no warnings.
Well ...
how do you want libsodium to check for that?
Easy:
typedef struct { uint8_t bytes[32]; } CryptoBoxPublicKey;
typedef struct { uint8_t bytes[32]; } CryptoBoxPrivateKey;
typedef struct { uint8_t bytes[32]; } GenericHash;
int crypto_box_keypair(CryptoBoxPublicKey *pk, CryptoBoxPrivateKey *sk);
This shouldn't be controversial; it's just the way one designs a good C API. (I'm speaking from 30 years experience designing C/C++/Obj-C APIs.) ¯_(ツ)_/¯
a wrapper is and should be (but) a wrapper and it should not introduce functional differences.
Yes, but it should be at least somewhat idiomatic for its language. If I just wanted a raw translation I'd use c2nim. Untyped pointers are dangerous, and most modern languages don't let you use them this freely.
Frankly, if someone confuses digests, keys, etc, simply staying away from crypto coding probably is a better solution.
Wow, I hear some hubris there. Everyone makes dumb mistakes sometimes. A lot of critical security failures came from one engineer having a bad day and making a dumb mistake. And since C does not name parameters at the call site, it's quite easy to mix up the order when a function takes five const unsigned char * parameters, as crypto_secretbox_open_detached does ... and that's almost undetectable in a code review unless the reviewer knows the API like the back of their hand or cross-references every single call.
Re "parameter checking" - a) that wasn't the level I was talking about, b) what have you gained except some feeling safer based on pure formality?, c) your approach merely shifts the problem from parameter position towards declaration, but most importantly, d) sorry, that's not how security and crypto work in real life. You may have decades of experience in coding but I'm speaking based on many years of relevant (ITSec, crypto) experience.
As for your theories: Yes, they are good, as theories, but your position is but one of many in lots of "how one should do it" (e.g. an interface) discussions.
- If I just wanted a raw translation I'd use c2nim.
Sorry, just another theory. In real life c2nim does the crude work but one will almost always want to then enhance it.
- Untyped pointers are dangerous
Sure, I fully agree. But void pointers are what happens to be used in many places and no amount of "but it's bad" lamenting will somehow magically change that. One reason among other being the fact that void pointers are (often) used for sound reasons like e.g. some algo/routine working with uint8 anyway and not caring or needing to care whether it gets passed in a pointer to uint8, int8, or int64, etc.
- Wow, I hear some hubris there.
And I hear birds singing. What's that to do with our topic?
- veryone makes dumb mistakes sometimes. A lot of critical security failures came from one engineer having a bad day and making a dumb mistake.
You'll probably be angry again but, pardon me, that's an on-dit and not a particularly smart one at that. Reason: No, that's just part 1; part 2 and more important is that people do not check and verify their code and those who do usually don't do it correctly but e.g. by only unit testing. Read Dijkstra, he has a lot of interesting things to say about (i.a.) that.
Don't get me wrong, much of what you say are good things but they are not the solution but rather just potentially helpful elements. The answer to "does my code do what it's supposed to do and only, exactly, and fully that?" is proper design and proper verification.
Re "parameter checking" - a) that wasn't the level I was talking about,
I was the one who brought up parameters, so it sounds like you didn't understand my point to begin with, which is simply that it's a very bad idea to use raw/void pointers for highly distinct values with well-defined sizes. Obviously that's not the only thing that can go wrong when using a crypto API, but it's something that's trivially easy to fix, so one wonders why it exists.
In my experience, crypto/security engineers tend to make terrible API designers — libSodium is far from the worst example. Apple's Keychain APIs are excruciatingly bad, and Intel's old CDSA (which Apple used to use) is pretty impenetrable too.
Read Dijkstra, he has a lot of interesting things to say about (i.a.) that.
I assume Dijkstra has written a lot. :-) Is there anything specific you can recommend?
people do not check and verify their code and those who do usually don't do it correctly but e.g. by only unit testing.
The answer to "does my code do what it's supposed to do and only, exactly, and fully that?" is proper design and proper verification.
I also think that design is important. If I can't easily understand my code, I don't trust it and try to design/write it differently. That said, "good design", or as you say it, "proper design", is a vague goal to act on. Can you recommend anything specific to read? (That ties in with my former paragraph, but is more generic.)
I guess by "proper verification" you mean formal verification as discussed in this thread?
- I assume Dijkstra has written a lot. :-) Is there anything specific you can recommend?
You assume correctly :) As for recommendations, well there are of course the widely known bonmots but frankly, no. So my recommendation is to read at the very least some of his papers (as well as at least some of Wirth's, B. Meyer's, and Hoare's).
- proper design", is a vague goal to act on. Can you recommend anything specific to read?
When I say "proper" I quite invariably mean "formal". One tool that seems to be quite well accepted is TLA(++) although I personally don't use it.
- my designing is often more intuitive than formal
What I guess (and hope) you mean should be mentally split into the solution approach (which often is or seems to be intuitive) and the design which should be considered to be the step from "that's the way I'll go" to engineering.
"design" and how to learn it - My personal answer: Think - a lot! - about what we software engineers are (hint: basically translators), what software is (hint: math -> hardware), and what design is (hint above). I personally do not know one book that can guide you in a fruitful and solid manner. The best way (attention: highly subjective!) I know is the one I chose, which is to read books by great minds (see my recommendation above) and to not go only for the content but also for the way those people tick. I don't doubt for a second that there highly likely are dozens of books pretending to teach "good design" but I'm afraid, none of it really does it.
- I guess by "proper verification" you mean formal verification as discussed in this thread?
Yes and No. Yes, when I say "proper" I usually mean "formal". And No because when looking properly one can't but notice that verification is two things, one element of the formal chain, but also a kind of mindset and interwoven with the other elements.
Sorry for my poor expressing my thoughts but I'm afraid I'm a lousy teacher and writer (and also a little worried how much "philosophy" is considered to be too much here.