Great work, thank you for sharing. I especially appreciate your link to your "Goals" section-- I'm listed them below because they are important and helpful IMHO.
+ An extremely simple CLI that composes well with UNIX pipes, and that works well as a backend for other programs
+ Small copy-pasteable keys, with optional textual keyrings
Support for public/private key pairs and passwords, with multiple recipients
+ The option to encrypt to SSH keys, with built-in GitHub .keys support
+ “Have one joint and keep it well oiled”, no configuration or (much) algorithm agility
+ A good seekable streaming encryption scheme based on modern chunked AEADs, reusable as a general encryption format
"An extremely simple CLI that composes well with UNIX pipes"
Just for fun, I occasionally experiment with proposed "post-quantum" encryption solutions and one in particular called Classic McEliece, from the same author (more or less) as the encryption used in age. Its small and compiles quickly. The interface is elegant and seems impossible to screw up. I have rarely seen anyone outside of the author and his followers use file descriptors in compiled programs in this way. I like it.
In theory I like the idea of making better use of file descriptors other than stdin and stderr. It seems like a powerful feature of *nix that never really caught on, except with that one sysadmin at work who writes insane one-liners with fifteen pipes.
In practice, I find the syntax needlessly obtuse, and numbered file descriptors are rare enough in the real world that most casual observers will have no idea what's going on.
I’m seeing this for the first time and don’t quite get the usefulness of this interface, can you help me?
I mean, file descriptor numbers instead of ordinary options provide some i/o semantics, but I have to remember the descriptor numbers? Is there some mnemonic/logic behind the ids?
> The option to encrypt to SSH keys, with built-in GitHub .keys support
It’s not built in, just composable in. And to the version in the readme I prefer using command substitution personally: it’s clearer when there are multiple recipients, and the infile tends to be much larger than the keys.
The biggest issues wrt github are that it rejects age files (so you have to rename them) and there is no /keys at the repo level whivh would give you the keys of all maintainers, so you have to hunt them by hand.
STREAM is a well-studied construction for online authenticated encryption. It doesn't just involve an auth tag every few kilobytes (or megabytes, or gigabytes). There's also a byte flag (called a "tag" in some implementations) to indicate whether or not there should be additional blocks or not.
This is the same author than mkcert, a great tool that for me is the one stop to make custom certificates for development. It already embeds all knowledge about how modern web browsers expect certs to be in order to work with them, so I don't have to track all the latest whims of each one. Thank you a lot for it!!
Yes, I used the same tool to find these. Well, not to find them exactly (I use HN Search for that), but to quickly assemble them and follow the links from one comment to the next. One of these years this will all be made available to everyone.
The main thing that confuses me with age so far is it feels like there's no way to have private keys that are automatically detected and also encrypted on disk. It seems to have a defacto mechanism for a user-local keys.txt, and sops for example uses this. However, as far as I can tell, keys.txt can't be encrypted. It's probably true that on most developer machines even just read-only filesystem access with the user's permissions would be catastrophic, but I feel a bit uncomfortable with how easy this makes it to extract private keys. Am I missing something? I know there's also a mechanism that can use SSH keys as private keys, but sops doesn't support that yet. (It also doesn't work with SSH agent, but that's still better than nothing I think.)
We did eventually add support for encrypted native key files, despite my skepticism around what threat models it actually addresses. You can now pass to “age -d -i” a file encrypted with “age -p”.
Not having a default keychain location though is a deliberate decision that’s here to stay. age keys are application-specific keys, not universal personal identities. We want to avoid implicit state and make rotation and compartmentalization as easy as possible.
Oh, OK. So if sops wants to support encrypting its own keys.txt file, it would need to be implemented on their end. For some reason, I was under the impression age itself had some logic for keys.txt files.
I understand that it is an ugly and imperfect layer of security to secure keys this way, but I still prefer it over nothing. Maybe applications could try implementing OS-level 'secure' keyring storage; that seems marginally better... No idea, though, I'm no expert on security.
Thanks for age regardless. I'm sure I'll be using it a lot in the future.
> age keys are application-specific keys, not universal personal identities.
Not a courtesy you extended to SSH keys, choosing to instead re-use them for an unrelated purpose, with, so far as I can see, still no proof in 1.0 that this is actually safe, just the usual hand-waving.
I absolutely love the idea of simple and beautiful. OpenSSL/GPG are overwhelming in the knobs/dials.
Couple thoughts that I'll never remember after my 5 minutes of using it but will likely be experienced by a lot of people in their first 5 minutes.
o It's odd that -e (encrypt) can recognize an age-keygen file, but -d (decrypt) errors out with:
age: error: failed to parse recipient file "person.pub": "person.pub": malformed recipient at line 1
I mean, I get it - the recipient should be the key, but you would think that age would be able to recognize its own "# public key: " string and parse it? (Like I said - noticed only in the first 5 minutes of use)
o In the vein of user friendliness/simplicity - why not just default to armored? It doesn't hurt anything to be armored, and if someone really wants a binary output, they could specify it specifically. Given they are both (mostly) equivalent, could as well default to the user friendly version. I get that it's too late to make the change now.
o Not available as a brew install, so I had to armwrestle with MacOS to let me even run it (Security was clever enough to recognize when I was copying something downloaded, had to defeat it with `dd if=age of=age2`)
> o It's odd that -e (encrypt) can recognize an age-keygen file, but -d (decrypt) errors out with:
>
> age: error: failed to parse recipient file "person.pub": "person.pub": malformed recipient at line 1
I'm confused as to how you reached this error message; "age -d" doesn't support recipient files / -R, only identity files (which is what age-keygen produces). It would be helptul to open an issue showing how to reproduce; either there's a bug, or documentation could be improved.
I assume this is because the author really does not want people to use age to encrypt e-mail because e-mail encryption is hopelessly and unfixably broken. By making armored not be the default, it's an extra little thorn in the side.
Thank you for the friction log! This kind of feedback is extremely valuable!
1. It sounds like you were passing an identity file to --recipients-file. We do support using identity files with --encrypt, but you need to pass it to --identity. We can make the error message more helpful when we notice this is happening.
2. Armoring has a lot of space overhead, and most applications don't need it. (age is not for encrypting messages and emails!)
> The author pronounces it [aɡe̞], like the Italian “aghe”.
I've seen a number of READMEs with this kind of pronunciation note recently--I don't think they really help, because I suspect that most readers aren't familiar enough with the IPA (pronunciation) notation.
Heh. I didn't know that about the pronunciation. I can just pronounce it like it's Norwegian! In fact “age” is a (rarely used) Norwegian word, roughly translating as “awe”.
One thing I miss and would imagine probably front and center of an encryption tool, is the algorithm(s) it's using to encrypt. There are multiple mentions of keys, but not the encryption mechanism itself. I've been roaming around the doc for that but couldn't find anything. Is there any pointer to that?
You can find them in the linked specification/design document, but I don’t really believe in putting cryptographic primitives front and center on user facing documentation.
First, the average user doesn’t actually care that much. Picking algorithms is my job, not theirs. They want to get their task done, so what I need to tell them is what the tool does and what are the security properties, not how it works.
Second, the choice of primitives is a relatively small part of cryptographic design. How you compose them and how it addresses a real use case matters much more. I usually get suspicious when I read “uses AES-256” in marketing copy because, like, it doesn’t tell me _how_ it’s used, and doesn’t seem to think it’s important.
It is important if the algorithm gets broken or weakened somehow. Also, I was looking for something like this just yesterday. If the algotirhm was clearely displayed in the readme I would have found it with a web search.
You made a great choice. Thank you. This tool looks awesome. Can't wait to build it.
That kind of thinking is probably applicable to consumer applications, not for security CLI tools. Anyone deciding to encrypt files with anything other than rar/7z passwords probably wants to know how really secure the tool they’re about to choose is. But I may be wrong.
ChaCha20-Poly1305, great. Thank you, I was looking for the algorithm.
Just found a similar tool called hpenc[1], written in C++11 with libsodium yesterday. It uses AES-GCM or ChaCha20 to encrypt files and streams. But this is even better as it's more ergonomic, drop in, I don't have to worry about dependencies and it does much more, like encrypt using ssh or gpg keys.
age has been an alternative plugin to gpg for transparent file encryption and decryption for the chezmoi dotfile manager for a couple of years now. age's CLI is so much nicer and easier to integrate than gpg's. Now with the Go API being stable, I can also bake high-quality modern encryption straight into the binary. Much appreciated.
>age's CLI is so much nicer and easier to integrate than gpg's
Some of that seems to be from omitting features, though. Gpg CLI integration allows passing both the cleartext to be encrypted and a passphrase on pipes. Which means things like --passphrase-fd, which age doesn't seem to have. Makes it simpler, but also means you have to have more clear text in files, at least temporarily.
+ An extremely simple CLI that composes well with UNIX pipes, and that works well as a backend for other programs
+ Small copy-pasteable keys, with optional textual keyrings Support for public/private key pairs and passwords, with multiple recipients
+ The option to encrypt to SSH keys, with built-in GitHub .keys support
+ “Have one joint and keep it well oiled”, no configuration or (much) algorithm agility
+ A good seekable streaming encryption scheme based on modern chunked AEADs, reusable as a general encryption format
Just for fun, I occasionally experiment with proposed "post-quantum" encryption solutions and one in particular called Classic McEliece, from the same author (more or less) as the encryption used in age. Its small and compiles quickly. The interface is elegant and seems impossible to screw up. I have rarely seen anyone outside of the author and his followers use file descriptors in compiled programs in this way. I like it.
Three programs, each only does one thing
To be fair, I should probably add that McEliece arguably fails the "small, copy-pasteable keys" criteria. :)In practice, I find the syntax needlessly obtuse, and numbered file descriptors are rare enough in the real world that most casual observers will have no idea what's going on.
It’s not built in, just composable in. And to the version in the readme I prefer using command substitution personally: it’s clearer when there are multiple recipients, and the infile tends to be much larger than the keys.
The biggest issues wrt github are that it rejects age files (so you have to rename them) and there is no /keys at the repo level whivh would give you the keys of all maintainers, so you have to hunt them by hand.
Previously: https://news.ycombinator.com/item?id=21897192
STREAM is a well-studied construction for online authenticated encryption. It doesn't just involve an auth tag every few kilobytes (or megabytes, or gigabytes). There's also a byte flag (called a "tag" in some implementations) to indicate whether or not there should be additional blocks or not.
https://github.com/FiloSottile/mkcert
Age: A simple, modern and secure file encryption tool - https://news.ycombinator.com/item?id=21895671 - Dec 2019 (197 comments)
Age - The PGP Replacement (alpha) - https://news.ycombinator.com/item?id=21188517 - Oct 2019 (2 comments)
Alpha release of Age-tool – A small command line encryption utility made in Go - https://news.ycombinator.com/item?id=21177063 - Oct 2019 (1 comment)
https://news.ycombinator.com/item?id=27726982 (July 2021)
https://news.ycombinator.com/item?id=27284079 (May 2021)
https://news.ycombinator.com/item?id=27236708 (May 2021)
https://news.ycombinator.com/item?id=26886074 (April 2021)
https://news.ycombinator.com/item?id=26244468 (Feb 2021)
https://news.ycombinator.com/item?id=26158300 (Feb 2021)
Yes, I used the same tool to find these. Well, not to find them exactly (I use HN Search for that), but to quickly assemble them and follow the links from one comment to the next. One of these years this will all be made available to everyone.
Not having a default keychain location though is a deliberate decision that’s here to stay. age keys are application-specific keys, not universal personal identities. We want to avoid implicit state and make rotation and compartmentalization as easy as possible.
I understand that it is an ugly and imperfect layer of security to secure keys this way, but I still prefer it over nothing. Maybe applications could try implementing OS-level 'secure' keyring storage; that seems marginally better... No idea, though, I'm no expert on security.
Thanks for age regardless. I'm sure I'll be using it a lot in the future.
Not a courtesy you extended to SSH keys, choosing to instead re-use them for an unrelated purpose, with, so far as I can see, still no proof in 1.0 that this is actually safe, just the usual hand-waving.
Couple thoughts that I'll never remember after my 5 minutes of using it but will likely be experienced by a lot of people in their first 5 minutes.
o It's odd that -e (encrypt) can recognize an age-keygen file, but -d (decrypt) errors out with:
age: error: failed to parse recipient file "person.pub": "person.pub": malformed recipient at line 1
I mean, I get it - the recipient should be the key, but you would think that age would be able to recognize its own "# public key: " string and parse it? (Like I said - noticed only in the first 5 minutes of use)
o In the vein of user friendliness/simplicity - why not just default to armored? It doesn't hurt anything to be armored, and if someone really wants a binary output, they could specify it specifically. Given they are both (mostly) equivalent, could as well default to the user friendly version. I get that it's too late to make the change now.
o Not available as a brew install, so I had to armwrestle with MacOS to let me even run it (Security was clever enough to recognize when I was copying something downloaded, had to defeat it with `dd if=age of=age2`)
o I do love `age -e -o out.enc -p somefile.txt`
I'm confused as to how you reached this error message; "age -d" doesn't support recipient files / -R, only identity files (which is what age-keygen produces). It would be helptul to open an issue showing how to reproduce; either there's a bug, or documentation could be improved.
> o Not available as a brew install
While age was in beta, it provided a brew tap. But now that 1.0.0 has been released, it has just (3 hours ago!) been added to homebrew-core: https://github.com/Homebrew/homebrew-core/pull/84805
I assume this is because the author really does not want people to use age to encrypt e-mail because e-mail encryption is hopelessly and unfixably broken. By making armored not be the default, it's an extra little thorn in the side.
1. It sounds like you were passing an identity file to --recipients-file. We do support using identity files with --encrypt, but you need to pass it to --identity. We can make the error message more helpful when we notice this is happening.
2. Armoring has a lot of space overhead, and most applications don't need it. (age is not for encrypting messages and emails!)
3. It's now in Homebrew Core :)
4. :D
I've seen a number of READMEs with this kind of pronunciation note recently--I don't think they really help, because I suspect that most readers aren't familiar enough with the IPA (pronunciation) notation.
I suggest including a link to something like this: http://ipa-reader.xyz/?text=a%C9%A1e̞&voice=Joanna
First, the average user doesn’t actually care that much. Picking algorithms is my job, not theirs. They want to get their task done, so what I need to tell them is what the tool does and what are the security properties, not how it works.
Second, the choice of primitives is a relatively small part of cryptographic design. How you compose them and how it addresses a real use case matters much more. I usually get suspicious when I read “uses AES-256” in marketing copy because, like, it doesn’t tell me _how_ it’s used, and doesn’t seem to think it’s important.
You made a great choice. Thank you. This tool looks awesome. Can't wait to build it.
Just found a similar tool called hpenc[1], written in C++11 with libsodium yesterday. It uses AES-GCM or ChaCha20 to encrypt files and streams. But this is even better as it's more ergonomic, drop in, I don't have to worry about dependencies and it does much more, like encrypt using ssh or gpg keys.
https://github.com/vstakhov/hpenc
[1] https://github.com/twpayne/chezmoi
Some of that seems to be from omitting features, though. Gpg CLI integration allows passing both the cleartext to be encrypted and a passphrase on pipes. Which means things like --passphrase-fd, which age doesn't seem to have. Makes it simpler, but also means you have to have more clear text in files, at least temporarily.
You can use /proc/self/fd/N if the program handles files correctly (particularly, no seek).