Loup-Vaillant wrote this on Lobste.rs in a dumb rant about my Matrix disclosure:
Personally I would actively avoid the check,
Hmm. What a weird thing to say.
Loup-Vaillant wrote a cryptography library called Monocypher, which famously had an EdDSA vulnerability mostly caused by their insistence on rolling their own custom EdDSA variant to avoid SHA512.
"I wonder how Monocypher holds up in 2026?"
Who said that? Well, anyway:
@soatok Feuding crypto-experts hate-reviewing each other's code is exactly the kind of vibe we need. Talk about adversary testing! 😎
@ticho I honestly welcome their feedback, no matter how dickish it will be, because it's a good way to learn your own blindspots before you ship to prod
@soatok because who checks for buffer lengths in c anyways it just wastes cycles
wat
also pretty sure "user error" is exactly how exploits are born
@soatok making input validation (with many preconditions and requiring specific knowledge) a user's responsibility sounds like a recipe for disaster
@soatok ugh I was using Monocypher for some things :( I’ll switch to uhh.. sodium I guess?
@david_chisnall @codecat Yeah @jedisct1 is one of my favorite crypto developers
@soatok
OMG. These two paragraphs are one after the other in their documentation (https://monocypher.org/manual/#CAVEATS ). Do they not see how tightly linked they are??
> CAVEATS
> Monocypher does not perform any input validation. Any deviation from the specified input and output length ranges results in undefined behaviour. Make sure your inputs are correct.
>
> SECURITY CONSIDERATIONS
> Using cryptography securely is difficult. Flaws that never manifest under normal use might be exploited by a clever adversary
@inex @soatok "Look, I only gave the user a foot-gun. Most users know how to not use the foot-gun. I mean yes, it is a gun; and yes, it is pointed automatically at their foot; and yes, it is loaded and has a hair trigger; but users should know better. I mean they are programmers, for heaven's sake, they should know about trigger discipline."
@soatok It's kind of crazy how there's crypto library devs that think non-crypto devs want to use something that will silently ruin the lives of users and end your whole career if you look at it wrong.
I know enough about crypto to know that I don't want to fiddle with crypto directly and I want boring libraries that will instead explicitly tell me to fuck off and try something else if I look at it wrong.
@soatok
> closed as not planned.
Lmao.
> The absence of input validation is core to the design of Monocypher, and Well documented. This allows Monocypher to simplify error handling and maximise portability. What you found was normal and expected.
Oh my dog
@lady_alys @soatok using crypto is difficult, even more so when you voluntarily don't validate inputs. Oh my dog. This lib should be in a kind of oss security blacklist!
@rusty__shackleford @soatok Trying to determine if this is bad snark on their part or the output of an AI agent
@cwebber @rusty__shackleford The "spell out the acronyms used in the filenames" part does gesture suggestively towards "AI"
The heel-turn on me allegedly not contacting them without an "You're absolutely right!" tells me that, even if it is AI, they at least edited the sycophancy out of it.
/Cinny
@rusty__shackleford @soatok personally my raccryptography libraries should just randomly silently explode because i didn’t check something it extremely trivially could check itself
@soatok Wait, so the entire input validation scheme is "don't call it wrong?"
That's... well, that's a choice you can make, I guess.
@wordshaper Our Threat Model is "You must only accept secure inputs if you want secure outputs".
@soatok good thing this code doesn’t have to operate in an adversarial environment. Something unfortunate could happen.
Sloccount counts under 2000 lines of code, small enough to allow audits. The binaries can be under 50KB, small enough for many embedded targets.
“Measuring software development by lines of code is like measuring aircraft design by weight”
Just that in this case, proving that too few is just as bad as too many.
That's not uncommon in C. The Java Native Interface has a design rationale document that says that it doesn't, for example, check null pointers because it's impossible to check for the general case of invalid pointers.
I don't really agree with this philosophy, but it did provide a nice showcase for CHERI (the JNI was explicitly designed to not be a trust boundary, so being able to turn it into a defensible one was great).
I'm not sure that's an improvement. You defined the macro as:
+#define MONOCYPHER_CHECK(cond) do { if (!(cond)) return; } while (0)
But this is returning without reporting any error. And that's necessary because the functions that they're in return void. But now they will simply not do the operation and fail. So now the outputs are full of uninitialised values.
EDIT: Ah, I see you mentioned that limitation in the comment at the end.
And that's why functions returning void are bad code smell in crypto libraries.
@david_chisnall @soatok @lain@lain.com @inex Indeed this seems worse. You need to either abort, longjmp to an error hander, or at least setup the structures in a form to defer the error til it's reportable later.
@dalias @david_chisnall @inex Yeah, the existing codebase is not amenable to good programming practices. I'm going to recommend libhydrogen instead.
@soatok @david_chisnall @inex If valid input parameters are actually part of the interface contract, aborting seems perfectly reasonable and the best-hardened choice. Current behavior is also reasonable (as long as contract is clearly documented) but poor in terms of hardening. I doubt it matters unless there's a viable scenario where these parameters come from external input not the calling program text.
To be fully transparent: I have turned down job offers before because I do not feel comfortable writing C. Almost everyone I've met that believes they can write secure C code is overconfident, and that scares me. So I'm certain that a better patch is possible.
That said:
You're going to get an invalid output if you proceed with invalid parameters. Silently aborting immediately versus allowing stack corruption is a losing position to be in.
@soatok @dalias @david_chisnall @inex not to toot my own horn or anything, it can be done, only because the following are not optional for me:
• Valgrind/Address Sanitizer
• gcov
• Obsessive use of RAII
• Insane number of tests
and this philosophy must be present from the beginning. look at Monocypher...good lord
@soatok Someone mentioned libhydrogen as what you would use if you considered using monocypher, and I'd like to point out: Reading libhydrogen's code, they have very simple input validation that would be easily replicable here, and yet the developer says "No, it would break portability to check" ?????
Libhydrogen is portable, so ?????
@soatok @dalias @david_chisnall @inex my attitude towards the C code I write is "I make no security guarantees, this fucker probably leaks memory somewhere too, and if you want to use my code securely then I urge you to reconsider"
@rusty__shackleford @soatok ROFLSTOMP!
The documentation clearly states that if the user supplies a string longer than MAX_PASSWORD_LEN, the buffers will overwrite system memory with the contents of the password, allowing remote shell access. This is not a bug, and changing it would mean I would have to write code. Plus I already wrote the docs.
@soatok @dalias @david_chisnall @inex Secure C and C++ code is a lie made up by Keith the Rat to make you bring him more cheese.
@soatok I am truly baffled that someone who considers themselves an engineer — a CRYPTOGRAPHIC engineer! — would read “the value MUST be between x and y” and consider it not their implementation’s responsibility to enforce but the user’s
@wolfcoder @soatok hang on what /century/ are we in? not like we are still programming Z80 or 6502 and trying to squeeze out every last bit of performance, surely a few extra clock cycles won't break the bank these days?
@vfrmedia @soatok everything* starts with c, if you're writing a fundamental library, a video game engine, part of an operating system, etc. you do want to be as lightweight as possible.
you do want to fight the attitude of not optimizing because then we get Electron apps and e-waste.
not buffer checking (for whatever reason) however saves you very little vs. how dangerous it is, i'll even range check when i'm writing Z80 or 6502 programs just to save headaches.
@vfrmedia @soatok and even then I would not be surprised if someone found an exploit in my game, its the attitude upon being made aware of said exploit that is very very important.
I'd trust software that had 1000s of reported vulns if the author was responsibly mitigating them vs. one with like 3 but the author was like "meh you're just using it wrong"
@soatok it's so nice to see people still making their own bespoke collections of footguns. I thought OpenSSL was the last place to find so many great ones in one place but here we are!
I guess I should just tap the sign whenever I encounter this sort of personality:
https://soatok.blog/2026/02/25/cryptography-engineering-has-an-intrinsic-duty-of-care/
@soatok in this article you in passing mention something that has frustrated me for some time in software engineering as someone with a bit more of a hardware background, and that is how much important stuff doesn't build on formal specifications, even big infrastructure projects! And when I have brought this up I'm often met with something along the lines of "but that is not very agile" or "we moved away from waterfall". Sure that small backyard shed you can yolo together, but why are we doing the same thing for the highway bridges of the software world?
@soatok to be completely fair, they do say in their documentation they don't perform input validation, so that's just more of a design choice.
I am guessing the idea is that whoever uses this is already supposed to follow a standard that dictates input validation? Which would be funny because iirc misra does say you should validate function parameters, but that's just one standard I guess.
I don't see anything on their readme which is a little concerning, and I don't fully get why not have macros that can be turned on/off for input validation.
@soatok @dalias @david_chisnall @inex oh and also: maintain your test harnesses in another language, preferably through a memory profiling+debugging tool because no leaks, no use-after-free, no out-of-explicit-allocation-access should be acceptable. oh, and your memory profiling tools should know about any custom allocators you've made, too, and yes, that means your little arena and buddy allocators
@xan @soatok @david_chisnall @inex I'm confused about the value of test harnesses being in another language. Do you just mean not executing in the same process domain where memory corruption in the code being tested could impact the test harness and its detection/reporting of failures? Because that's easily handled just by separating these domains. Or do you have some other reason?
@dalias @soatok @david_chisnall @inex yeah the domain/process isolation is the main thing here; i guess i specified "language" here because i'm putting the final touches on the FFI portions of a programming language i'm implementing with a very thin/general VM and so i've got all that on my mind
@soatok @wordshaper
Jeez, don't these people understand there's a difference between "Implement according to the spec" and "Implement according to the spec in a way that prevents misuse"?
@soatok This has me cratching my head. If you're going to write applications in C then you will need to do input validation. It's just part of it, otherwise all guarantees fly out of the window. And if you're going to use 3th party libraries then you need to know what inputs they want and what they do and don't validate.
The library cleary states what they need and that they don't validate.
So the application programmer knows they need to validate themselves. Something they were already considering and doing because they are writing C code. I don't see the problem here at all.
You could argue validating input is too easy to forget and it's all a footgun that's too easy to misuse. But that is just the C programming language in general. At that point you're not arguing against this library but against C as a whole. (Which is valid but not what the blog post did)
That said, some asserts in there would be very welcome.
@soatok or to put in a different way:
If you program in C, and only use libraries that do input validation. Does that mean you don't need to care at all about input validation?
No, clearly the application programmer writing C code needs to keep this in mind at all times. So a library explicitly stating "we don't do it, so make sure you do" seems very reasonable?
@0xabad1dea @soatok "The key MUST be a 128, 192 or 256 bits value" "well it's up to whoever uses this library to ensure that, I'll write the implementation so it doesn't require this and just stick a note in the documentation that a puppy is killed if it's not"
@0xabad1dea @soatok “why would I lock my safe?? it’s the front door’s job to keep thieves out, duh.”
@soatok @0xabad1dea linkedin page: “Head of Cyber at SecurityWarfare, Inc”
@wolfcoder @vfrmedia @soatok even in the hypothetical situation where saving half a cycle on a bounds check mattered, the right thing to do would be to litter the code base with debug asserts or, even better, come up with an api where the bounds check isn't needed.
C isn't a forgiving language and a good programmer doesn't trust that their own code does what it's supposed to unless they can prove it, much less anyone else's.
@deetwenty @soatok I think the most frustrating thing I heard from my boss on Monday is the sentiment of "Oh, the transition to AI coding means that we have to throw away all of the Agile we've been working on and basically go back to waterfall. The best way to use it is to write out your specifications first."
So, the planning that we should have been doing a long time ago is only worth bothering to do once the robots are here?
This is how I know we're in hell.
@deetwenty @soatok Thanks for adding in that analogy to help me understand.
I don't deal in software/hardware engineering for a living, but I do have a mechanical engineering degree and professional background. The designs I make, use standardized materials (ASME/ASTM specification) and are designed and manufactured according to authoritative design codes (ASME B31.3, ASME BPVC VIII-1, PED).
Using standard materials and design codes is a must, for the sake of consistent designs/products that have adequate safety factors built in. And also so customers can audit/inspect designs themselves to ensure suitability. In some jurisdictions, these codes even have the force of law behind them.
The fact that (from what you say) following standards isn't as common a practice in software engineering, is alarming. How is there supposed to be any independent verification for functionality and safety if everything is locked behind a trade secret?
@Intaglio_Dragon @soatok it is even worse with very few exceptions (healthcare mostly) there aren't even professional standards at all!
@drwho @bersl2 @deetwenty @soatok This sounds so much better than the agile junk. Why aren't we using this? This sounds like engineering instead of programming.
@gudenau @drwho @bersl2 @deetwenty @soatok the big problem with waterfall is that it assumes you change nothing along the way, and anyone who has worked on a software team beholden to client demands knows that this is basically impossible. agile has serious problems too, but waterfall is just as difficult to get right in practice.
@soatok I did some digging into another one of their open issues - being his own issue created in 2023 regarding the viability of implementing an embedded variant tuned for the limited resources of lower-end IoT devices. While I'm not at all familiar with Monocypher's history, I can't help but personally think "why is there this obsession with ensuring everything is 100% compatible?"
I would like to imagine that the folks working with the normal version aren't going to be the same as the embedded one, or that if they are, that they'll be very familiar with the extreme resource constraints that comes with all of these teeny tiny microcontrollers, and would know how to optimize as best as possible - if that involves calling things differently than the normal variant in order to keep the library as lightweight as possible, so be it. When you're working with a constrained environment you take whatever you can get, regardless of what that looks like.
Which always has a decent chance of being some dumb restriction you didn't think was possible, or makes zero sense whatsoever but that's just how the platform is.
@soatok God, reading through their incredibly short manual, they're even aware that not all Cortex M architecture chips have constant-time multiplication (being M0 lineup and M3's)... which to me seems like that's a good reason to have an embedded version for at least those devices where you have to get tricky about doing things.
I'm also briefly looking at the Xtensa ISA (used for ESP32's), and they too had a problem where it was possible for compiled code to not be constant-time, actually. The "fix" is manually ensuring that it's using a low-memory mode (or going to Assembly) to enforce constant-time behavior because otherwise the compiler might get a little too smart and break that sometimes.
You would think that "certain low-power architectures lack the ability to do this stuff consistently" would be a reason to offer an embedded variant tooled to avoid those problems, even if it's at the cost of breaking one-to-one compatibility with the normal variant.
@senil @soatok The truth is that there aren't a whole lot of embedded style chips that need to do cryptography, trying to shoehorn a general purpose cryptographic library into them is a fool's errand. If you need cryptography on such chips you are generally much better off with a specialised library that implement only primitives suitable for the chip.
@NohatCoder @soatok Oh yeah, I agree, there aren't many use cases where you'd need that kind of thing. Buuuut it just seems weird to me that, for the rare cases where you'd need one (and the dev is insistent on using Monocypher for... reasons), they refuse to consider implementing a variant that at least ships the absolute basics that tries to remain mostly-compatible - or that they even "consider" that something worth exploring now, two-ish years later, even though their current portable version is still too heavy for the truly low-power devices while still risking not being secured on those architectures.
It seems like they're very aware that some folks do use Monocypher in that way (whether they should or shouldn't on hardware as limited as the Cortex M0's is a different debate, but I do agree that one should really use something much more specific), but also don't want to actually implement it in a way that ensures it'd work securely.
IDK, it just seems weird to me that Loup-Vaillant acknowledges that embedded devices are something some folks try to use this tool for, is open to trying to implement some version of it, but also refuses to do the one thing that would make it marginally more viable (whether it should actually be done or not) in the form of breaking direct API compatibility with the normal versions. Either implementing a more embedded-device-friendly version is on the table, which means breaking existing API compatibility to focus on the lowest common denominator in terms of what preserves constant-time behavior; or it shouldn't be considered, the issue closed, and discouraged from use on certain ISA's.
Just feels like a weird spec choice to have this question in the air.
@bersl2 @soatok and the funny thing is that it should be possible to do the spec in an agile way, with feedback not only from the customer but also from implementing a (reference) implementation (which especially initially should focus on correctness over performance). There is nothing in agile way of working that says you can't have a spec (and other such documentation!)
The only thing that prevents us from doing it that way is that it isn't "going fast and breaking things" (but slow and steady, while keeping things flexible)
@gudenau @drwho @bersl2 @deetwenty @soatok for one, "waterfall" in that sense never existed. It was used as an example by the author on "how not to do it" and people somehow got it the other way around.
what happens today is that most "agile" is really more a "we dont want to bother with specs"-mode. Agile should be what works for the team, if it feels bothersome or in the way, your process owner should change things up
@brahms @gudenau @drwho @bersl2 @deetwenty Also, version your goddamn specs.
c2sp.org is a better model than IETF RFCs.
@soatok wow I did not check your original link before.
>in my opinion such a tiny threat is not worth the additional complexity of even a single if statement.
WHAT THE FLYING FUCK
@f4grx @soatok There is this argument that even if a zero key is in some way bad, you don't really need to check if your rng produces all zeroes when generating a key, because the probability that this happens is so low as to be effectively impossible.
However, when you accept someone else's key as part of some interaction they could have maliciously set it to 0. It is not obvious that that bestows any major ability, but the threat analysis gets complicated, it is easier to just do the check.
@NohatCoder @soatok if an important protection is easy to check, it has to be implemented. Not implementing the simple checks is... Somewhat... Criminal, in a way? Like, you know you should do it, it's easy to do, the consequences could be critical... And yet you dont do it. Why!
@f4grx @soatok The thing is you can reasonably argue that it is not important, the cost of doing the check is just so low that doing it simply to eliminate some analysis complexity is worth it.
The mere fact that someone can break the secrecy of a transaction in which they partake is not really a vulnerability, because that is always the case, no protocol can prevent that someone simply chooses to share their private key with the world.
@NohatCoder @f4grx Right, the main reason the Matrix thing was an issue was because of how group key management was implemented, and the all-zero DH broke secrecy for everyone else using Megolm