Thanks for sharing, always happy when my projects inspire alternatives addressing different parts of the design space. Here are a few quick comments based on skimming the documentation, let me know if I misinterpreted anything.
- signing support
This has always been a non-goal for age. It makes the UX significantly more complex, but it's good if different tools have different goals.
I can't quite make out from https://www.kryptor.co.uk/specification if it does proper signcryption, sign-then-encrypt (vulnerable to signature stripping and re-signing), or encrypt-then-sign (vulnerable to decrypt-reencrypt-forward, like OpenPGP). If the latter two, it's a missed opportunity to offer more security than age+minisign can offer and I encourage the author to look into it!
As https://www.kryptor.co.uk/security-limitations#post-quantum-... acknowledges, "the asymmetric algorithms in Kryptor aren't post-quantum secure". There is support for adding a pre-shared symmetric key, although I did not find the pre-shared key in the usage section, but I would argue that is not asymmetric encryption.
In this sense, I would actually argue that Kryptor is just as post-quantum secure as age: age's symmetric encryption (the passphrase mode) is post-quantum (see https://words.filippo.io/dispatches/post-quantum-age/). We don't support adding a pre-shared symmetric key to asymmetric encryption, but if you have a secure channel to establish a pre-shared key, you should just use passphrase mode.
This is a pretty wonky topic. age as a whole is key committing (you can't make a file that decrypts with two age identities as different plaintexts, some academic researchers tried!). Our file key encryption is not (https://github.com/FiloSottile/age/commit/2194f6962c8bb3bca8...) which means that if you host an online service that accepts an age file and decrypts it with a passphrase and returns an error if it's incorrect, an attacker can do a bruteforce two passphrases at a time instead of one at a time. Given the online oracle is already unusual as a setting, I am not interested in adding complexity to solve this one.
It's not the default because most threat models don't need it: if you have FDE, who's an attacker that can read files from your disk but not replace the age binary in $PATH?
- indistinguishability from random
Not an age goal, actually we very intentionally put "age-encryption.org/v1" in the header so you can run file(1), and specify the type of the recipients to help plugins disambiguate files. The default recipient type doesn't leak any other metadata (i.e. you can't link age files encrypted to the same recipient).
Massive fan of age and congrats on its success!! On size padding, I know you've patiently listened to me before on this, but I'll always take a chance to advocate for an approach like PURBs ( https://petsymposium.org/2019/files/papers/issue4/popets-201... ).
Let's say you're in a country that suppresses certain material, like copies of the Bible, or the Hacker Manifesto, or whatever; if the authorities find an encrypted file that closely matches the size of that material; that could do you in. But maybe a more realistic case is how the size of maps tiles alone is enough to figure out where you are looking on a map (https://ioactive.com/ssl-traffic-analysis-on-google-maps/), or the size of streaming video segments (https://www.cs.cornell.edu/~shmat/shmat_usenix17.pdf) gives away what you are watching. Both real-world examples of size side-channels. It's not un-imaginable that someone could use a tool like age to build bigger systems like that, where the leaks creep in.
My age v2 note for padding says “use Padmé, see Colm’s comments” :)
I’m not really sold on the UR part of PURBs, though: age wants to avoid asking for a passphrase if the file is not passphrase encrypted, and age-plugin-yubikey wants to avoid asking for a PIN if it’s for the wrong YubiKey. These are tradeoffs and it’s not obvious the very end of the spectrum (uniform random) is the right spot.
_o/ hi all, age author here! age is the one of my projects that grew most organically into an ecosystem. It's always great to see what people build with it. Happy to answer any questions.
- an official TypeScript implementation https://github.com/FiloSottile/typage (based on libsodium.js in the latest version, and on pure-js Noble libraries on main)
Age is great. I used the rust crate to write an ftp server that encrypts the files before they hit disk (specific use case is having a drop box for my network scanner) and I love the simplicity and composability it provides.
One feature request: it would be awesome to have paraphrase encryption for age private keys.
Combining Passage, pass-otp, and age into a single Go app has actually been on my personal hobby-project TODO list (but I haven't gotten around to it yet).
Integrating age plugins into this binary is not something I would do, though. Besides the fact that the plugins are separate projects with their own release schedules, they are also in different languages (plugin-yubikey is Rust, plugin-se is Swift, ...), and you would need to fork them anyway for communication. I guess you could bundle the binaries together in a package, and make sure the search path searches for the bundled binaries as a fallback.
Age is designed for a single purpose: Encryption and decryption of files. To create digital signatures use another specialized tool like minisign instead.
Specialized tools are simpler than one do-it-all tool.
> If you encrypt and then sign, an attacker can strip your signature, replace it with their own, and make it look like they encrypted the file even if they don't actually know the contents.
> If you sign and then encrypt, the recipient can decrypt the file, keep your signature, and encrypt it to a different recipient, making it look like you intended to send the file to them.
Both cases assume that the user doesn't understand what a signature means. In either case it means that the signer certified the thing signed. Are paper signatures getting so rare that we are collectively starting to forget this?
Elligator is a bidirectional map from random bytes to elliptic curve points, which is mainly useful for censorship resistance. Its state-of-the-art protocol integration as far as I know is obfs4 (https://gitlab.com/yawning/obfs4), one of the Tor circumvention pluggable transports (https://tb-manual.torproject.org/circumvention/). The others rely on disguising as other protocols rather than looking random.
Elligator implementations have a history of subtle bugs, arguably because there was not a spec, only a paper, although it looks like there are some third-party test vectors now.
In general the "inverse map" from random bytes to point is used only for censorship-resistance use cases, but the "direct map" turning random bytes (like a CSPRNG output or a hash) into a point is useful for a number of purposes in cryptography, like VRFs. That led to the direct map being specified more rigorously, like in https://www.rfc-editor.org/rfc/rfc9496.html#name-element-der... and https://datatracker.ietf.org/doc/html/rfc9380.
IMHO a map from a fixed amount of random bytes should be part of the fundamental group abstraction, and that's what Ristretto provides. The CFRG approach is slightly different, providing full domain-separated hash "suites" that go straight into a curve point.
As a historical note, the first time I recall seeing the anti-censorship use case was in “Telex” [1] which uses it to steganographically embed a ciphertext into a TLS random nonce, so that a router in the middle can detect whether the connection should go to its terminus or be routed somewhere else. However the authors of that paper didn’t have a standard primitive for doing this and so they had to make their own approach. Ironically, an even older use of the general approach is the NSA’s Dual EC DRBG generator [2], which has to encode elliptic curve points into random-looking bit sequences as part of its design as a PRNG. They ended up homebrewing that using standard curves by dropping several bits of each X coordinate, which in theory is all they need because there’s no need to go in the inverse direction. However the conjecture is that Dual EC is backdoored, and the backdoor requires an inverse mapping from bitstrings back to points; this has to be achieved by brute-forcing every possible point and testing.
None of this has anything to do with your comment but I love the history of these steganographic tricks.
Koblitz in "Elliptic Curve Cryptosystems" [1] dedicated section 3 to how to embed binary strings into points and back, which is of course necessary for elliptic curve ElGamal.
Interestingly, the RCE fix was "smuggled" in public almost a month ago.
When PerSourcePenalties are enabled, sshd(8) will monitor the exit
status of its child pre-auth session processes. Through the exit
status, it can observe situations where the session did not
authenticate as expected. These conditions include when the client
repeatedly attempted authentication unsucessfully (possibly indicating
an attack against one or more accounts, e.g. password guessing), or
when client behaviour caused sshd to crash (possibly indicating
attempts to exploit sshd).
When such a condition is observed, sshd will record a penalty of some
duration (e.g. 30 seconds) against the client's address.
It's not really a reversable patch that gives anything away to attackers: it changes the binary architecture in a way that has the side-effect of removing the specific vulnerability and also mitigates the whole exploit class, if I understand it correctly. Very clever.
That's a previously-announced feature for dealing with junk connections that also happens to mitigate this vulnerability because it makes it harder to win the race. Discussed previously https://news.ycombinator.com/item?id=40610621
The ones you link are the "minimal patches for those can't/don't want to upgrade". The commit I am linking to is taken straight from the advisory.
On June 6, 2024, this signal handler race condition was fixed by commit
81c1099 ("Add a facility to sshd(8) to penalise particular problematic
client behaviours"), which moved the async-signal-unsafe code from
sshd's SIGALRM handler to sshd's listener process, where it can be
handled synchronously:
https://github.com/openssh/openssh-portable/commit/81c1099d22b81ebfd20a334ce986c4f753b0db29
Because this fix is part of a large commit (81c1099), on top of an even
larger defense-in-depth commit (03e3de4, "Start the process of splitting
sshd into separate binaries"), it might prove difficult to backport. In
that case, the signal handler race condition itself can be fixed by
removing or commenting out the async-signal-unsafe code from the
sshsigdie() function
The cleverness here is that this commit is both "a previously-announced feature for dealing with junk connections", and a mitigation for the exploit class against similar but unknown vulnerabilities, and a patch for the specific vulnerability because it "moved the async-signal-unsafe code from sshd's SIGALRM handler to sshd's listener process, where it can be handled synchronously".
The cleverness is that it fixes the vulnerability as part of doing something that makes sense on its own, so you wouldn't know it's the patch even looking at it.
Interesting that this comment has remained the topmost one for 2 days despite being incorrect and being corrected in the message right below it. I wonder if people are only reading the first message in the thread and upvoting and then leaving with the wrong impression.
Assuming you’re referring to the birthday bound on blocks (https://sweet32.info) that’s a limit on blocks encrypted with a single key. XAES derives large keys per message, so it achieves what are commonly referred to as “better-than-birthday” bounds.
There is no space for 256 bits: 192 bits is 96 bits from the underlying nonce space, and 96 bits that go into the 128-bit CMAC block (along with the necessary prefix). We could make the CMAC input longer, but then we'd have to run the AES-256 block function more times (and we'd hit some annoying key control issues in the CMAC KDF).
This is actually similar to why XChaCha20Poly1305 has 192-bit nonces, and consistency with the other major extended-nonce AEAD is another mild advantage.
Reducing security below 128 bits in order to save a block of AES will anger the gods and surely we will be made to pay. Turn back now, while there is still time.
I don't disagree, actually. I was copying the NIST source document notation with 0¹²⁸ and 0¹²⁰10000111, but it probably does more harm than good. `X` was just me being too clever. (In my defense, `X` is formatted differently from variables in the original, and all variables are defined.)
Good for you, you probably do not live in a country under digital colonialism where the gov allowed facebook et al to force internet providers to tax the pop with absurdly low and expensive data limits and then "not count" things like facebook and whatsapp and one music app.
In most of the global south, 100% of business have a whatsapp. In those places it pretty much replaced telephone and the green whatsapp icon is now what the current young generation recognize as the we did the black telephone outline on a business front next to a number.
and if you own a business, or is self employed, it is even worse: you live by that app.
The lack of encryption between myself and a business is less offensive than replacing an open standard (plain old telephone systems, eMail) with a proprietary and closed one, backed by a single, private corporation
i completely agree with your sentiment, but i will also say this.
As an expat, this feature has enabled me to transact with locals from the convenience of my phone, even though i don't have any local line and i will not bother to get a local SIM card, nor do i want to have a US SIM and a local one interchangeably.
It also enables me to be very effective when requesting services on demand, and cutting thru the on-hold time, disconnected calls, or the needless chitchat.
I have many bad things to say about WA, but making living more difficult in a foreign country is not one of them.
Telephony is an artificial monopoly. There's not a single reason why you cannot call everyone around the globe for mostly free, as whatsapp proves. Yet here we are.
- signing support
This has always been a non-goal for age. It makes the UX significantly more complex, but it's good if different tools have different goals.
I can't quite make out from https://www.kryptor.co.uk/specification if it does proper signcryption, sign-then-encrypt (vulnerable to signature stripping and re-signing), or encrypt-then-sign (vulnerable to decrypt-reencrypt-forward, like OpenPGP). If the latter two, it's a missed opportunity to offer more security than age+minisign can offer and I encourage the author to look into it!
- sender authentication
I wrote about this. tl;dr age has authentication, but I am not sure what a non-sharp UX around it would be, so I don't advertise it. https://words.filippo.io/dispatches/age-authentication/
- post-quantum security
As https://www.kryptor.co.uk/security-limitations#post-quantum-... acknowledges, "the asymmetric algorithms in Kryptor aren't post-quantum secure". There is support for adding a pre-shared symmetric key, although I did not find the pre-shared key in the usage section, but I would argue that is not asymmetric encryption.
In this sense, I would actually argue that Kryptor is just as post-quantum secure as age: age's symmetric encryption (the passphrase mode) is post-quantum (see https://words.filippo.io/dispatches/post-quantum-age/). We don't support adding a pre-shared symmetric key to asymmetric encryption, but if you have a secure channel to establish a pre-shared key, you should just use passphrase mode.
age does have a third-party fully post-quantum asymmetric encryption plugin (https://github.com/keisentraut/age-plugin-sntrup761x25519) and I plan to make an ML-KEM one once the standard is out.
- key commitment
This is a pretty wonky topic. age as a whole is key committing (you can't make a file that decrypts with two age identities as different plaintexts, some academic researchers tried!). Our file key encryption is not (https://github.com/FiloSottile/age/commit/2194f6962c8bb3bca8...) which means that if you host an online service that accepts an age file and decrypts it with a passphrase and returns an error if it's incorrect, an attacker can do a bruteforce two passphrases at a time instead of one at a time. Given the online oracle is already unusual as a setting, I am not interested in adding complexity to solve this one.
- private key encryption
age supports that! https://github.com/FiloSottile/age?tab=readme-ov-file#passph...
It's not the default because most threat models don't need it: if you have FDE, who's an attacker that can read files from your disk but not replace the age binary in $PATH?
- indistinguishability from random
Not an age goal, actually we very intentionally put "age-encryption.org/v1" in the header so you can run file(1), and specify the type of the recipients to help plugins disambiguate files. The default recipient type doesn't leak any other metadata (i.e. you can't link age files encrypted to the same recipient).
- size padding
This is a good idea and slated for age v2.