Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Show HN: Age 1.0 – Simple, modern and secure file encryption (github.com/filosottile)
494 points by FiloSottile on Sept 6, 2021 | hide | past | favorite | 88 comments


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.

Three programs, each only does one thing

     usage: cmkeypair 5>publickey 9>secretkey

     usage: cmencrypt <message 4<publickey >ciphertext

     usage: cmdecrypt <ciphertext 8<secretkey >message
To be fair, I should probably add that McEliece arguably fails the "small, copy-pasteable keys" criteria. :)


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?


Fair question. Im not the author so can only make a guess (other readers of HN surely know far more than I about these subjects). However I suspect the rationale is related to whats been called "Bernstein chaining".

https://cr.yp.to/proto/ucspi.txt

http://www.catb.org/~esr/writings/taoup/html/ch06s06.html

https://skarnet.org/software/execline/grammar.html

https://www.oilshell.org/blog/2017/01/13.html


Hmmm, now I don’t get how Bernstein chaining relates to file descriptor interface. I’m curious about specifically the `4<infile` you seem to be admiring, you just observed its unusualness or do you see how its more powerful than the more common `-input infile`?


Nice. TIL...


> 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.


How do you do AEAD with seeking? Tags (MACs) every few kilobytes?


https://web.cs.ucdavis.edu/~rogaway/papers/oae.pdf (Section 7)

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.


You don't happen to know of a slightly more approachable specification?

A search for anything like "stream cipher construction" only lands me on the traditional "stream vs block" discussions.


A Poly1305 tag every few 64 kilobytes. An encrypted chunk in age is thus 64 KiB + 16 bytes.


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!!

https://github.com/FiloSottile/mkcert


Agree, mkcert is awesome!


One big past thread and two tinies:

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)


@dang do you have a tool that helps you find dups and format your posts sorted by date/# of comments like this? If not someone should make you one...


I do! Let me dig up the links where I described this... Edit - here you go.

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.


Did not disappoint; as usual.


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.


> For some reason, I was under the impression age itself had some logic for keys.txt files.

An early version of the draft spec did include a default keys.txt path, and I implemented it in rage. However, during the beta phase discussions we made the decision Filippo described above, and I removed support for a default path in rage 0.5.0.


Where do you store the key that decrypts your key file?


Hardware? I assume if someone was concerned about key access they wouldn't want keys on their filesystem at all but move them into an HSM instead. Since age identities can come from standard input I assume it'd be feasible to put together a workflow there coming from one of the various cli utilities for interacting with keys. There is already a YubiKey specific age plugin [0] getting worked on as well. Currently in beta but looks interesting. Hopefully that will continue to expand to cover other common options. HSM support is pretty important for a modern encryption utility IMO but unfortunately the landscape is pretty all over the place too, so makes sense to just leave it to plugins or as part of a unix flow.

----

0: https://github.com/str4d/age-plugin-yubikey


You can encrypt with a passphrase.


Then why not encrypt the files directly with the passphrase? I think this is where the author mentions “questionable” security improvement from supporting key files.


> 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 I do love `age -e -o out.enc -p somefile.txt`


> 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.

> 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


> why not just default to armored?

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!)

3. It's now in Homebrew Core :)

4. :D


> 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.

I suggest including a link to something like this: http://ipa-reader.xyz/?text=a%C9%A1e̞&voice=Joanna


The README does include a link to Google Translate to vocalize the name, but thanks for sharing this site!


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.


The vast majority of GPG users weren't aware of the encryption details in use either. As recently as 2018 Redhat 7 users were shipped GnuPG 2.0, which according to [0] defaulted to CAST5. And I've met some of those users that thought they were using AES off the back of "well I used GPG so I assumed...".

[0] https://security.stackexchange.com/questions/86305/what-is-t...


Specification of the format is at https://docs.google.com/document/d/11yHom20CrsuX8KQJXBBw04s8... and includes all the cryptographic primitives that it uses. TL;DR: Mostly the djb stack.


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.

https://github.com/vstakhov/hpenc


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.

[1] https://github.com/twpayne/chezmoi


>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.


> things like --passphrase-fd, which age doesn't seem to have

You can use /proc/self/fd/N if the program handles files correctly (particularly, no seek).


Bold move, grabbing a three-letter name with widespread pre-existing meaning, but it looks like you might just have gotten away with it! Congratulations!


Right?! I’m as surprised as you are.


I like age. I use it with wormhole for quick transfer jobs. Thanks for all the work on this!


Has anyone managed to replace file encryption using gpg or sops with age for anything other than simple use cases? I realize age doesn't cover the breadth of scope of the other two, but it doesn't seem like those gaps are being filled by anything else, either.


What is it that you’re missing?


Not OP, but I am missing:

* password store (Pass)

* hardware key support

* back up software using Age (as with duplicity)

* integration into operating systems (cross platform)

* integration into apps

* Mobile support

* command line for key management (maybe), to help me organize and rotate keys, and maybe share public keys

* An agent?

* robust positive review or audit (security people need to review the source code independently), or a long history of good security

Mount would be great too!



hardware key support exists: https://github.com/str4d/age-plugin-yubikey


Correct me if I am wrong, but that doesn't seem to do the decryption on the yubikey itself. So the Yubikey wouldn't act as a portable air gapped system as in the case of, say, GPG.


It decrypts the per-file symmetric key on the YubiKey, the same way the PGP applet works.


I think the important point when GPG uses something like a Yubikey as a portable air gapped system is that the private key is kept exclusively on the device and is normally not even available to the owner of that key. That implies that the asymmetric encryption is done on the device as well.


The same thing happens with age-plugin-yubikey. You can only generate identities on the device (so you never have access to the private key), and the YubiKey needs to be plugged in and available in order to decrypt, as the core asymmetric decryption operation occurs on the YubiKey. (You don't need the YubiKey to _encrypt_, just the recipient string).

The way this is set up in the plugin protocol, the age client provides the age header to the plugin, along with any identities the user has asked to decrypt with, and the plugin returns any file keys it is able to decrypt. The process of taking the per-recipient encrypted blobs in the header and extracting file keys, is entirely up to the plugin. This falls nicely out of the way the age format's "one joint" is designed: the file is encrypted with one symmetric key, and that key is encrypted separately to each recipient via whatever mechanism their type requires.

If you squint through the layers upon layers of packet complexity, this is the same way GPG encryption operates [0]: the data is symmetrically encrypted with a random session key, and that session key is asymmetrically encrypted to each recipient. Only the asymmetric decryption part happens on-device for GPG; the symmetric part happens on the computer.

[0] https://datatracker.ietf.org/doc/html/rfc4880#section-2.1


This is a great list. I also use sops because it has symmetric key support for a great many systems, allowing me to use gpg and aws kms for one set of secrets.


A tangential question: is there an age equivalent du jour for signatures? For release signing and verification.

I'm looking especially for end-user ease of installation. minisign seemed promising but `brew install minisign` has tons of dependencies and takes forever. I found a Go port with compiled binaries, but it's not a popular repository.

Is there anything cross-platform and widely trusted that's better than gpg?


Yes: it's minisign/signify (a deliberately simple ed25519 format based originally on OpenBSD's package signing system, which I think 'tedunangst designed.) Minisign is packaged by the libsodium team.


Gotcha, thanks! It seemed perfect until I tried the homebrew installation. I missed that they're releasing compiled binaries as well.


It's minisign.

I see a libsodium dependency in the sources, anything else?

Don't the binaries at https://github.com/jedisct1/minisign/releases work for you?


Ok thanks. Maybe the problem is that the homebrew formula is building from source instead of using the binaries? I'll have to look closer.


Something I've been wondering for awhile: while innovation for per-file-level encryption is useful, is there a clean way to run encrypted docker volumes for achieving containerized encryption-at-rest? In particularly, a portable docker-only way (ex: docker-compose containers / users on the network get access and that's it), with no host linux FS / k8s extensions?


I think this is dangerous to use for many of the use cases that it would appear useful for, especially for any form of backups. By design, it protects the confidentiality of encrypted files but not the integrity, and in almost every real threat model this is a serious flaw. The authors are not interested in changing this (they have closed various issues asking for it and clearly documented their position) so you should probably not use it unless you understand the issues well enough to be confident that is safe for you or can solve the problem using a second tool for signing (which will also cost you streaming).

I am not any kind of expert in this field, so you are not obligated to listen to me... but I am still right.


calling this format "secure" is not sufficient, here is a review that asked the right questions.

https://neilmadden.blog/2019/12/30/a-few-comments-on-age/ Unfortunately, the age spec doesn’t document its threat model or the security goals it is intended to achieve so I’m having to read between the lines to work out what was intended.


It's for the case where you are concerned about malicious modification and the user is expected to not deal with the ramifications of the error message. This case in particular:

    $ gpg2 -d backup.tgz.pgp | tar xz
So the modified backup file can do bad things before anyone can stop it because gpg will complete the operation before warning of the modification.

It doesn't have integrated signature support so that in most cases an attacker can use the public key to entirely replace the file and avoid the bother of some sort of known text attack in the first place. So I think that the use case is so narrow as to disappear.

The linked article covers this. That is really all there is.

It serves best as a demonstration of what an encryption utility would be like that blows up and errors out on a possible modification, as opposed to completing the operation and returning the error at the end.


More on this argument here:

https://news.ycombinator.com/item?id=27433186

This is, to put it charitably, an idiosyncratic argument about how data encryption is supposed to work.


Has someone created a Reed-Solomon encoder/decoder CLI program to use with age yet?

I think if we did that, the stupid discussion about GPG and framing its lack of IND-CCA2 as a "recoverability feature" would become moot.


The single bit error in the discussion was intended to be an extreme example. Typically media errors involve entire media blocks. Often the blocks are missing entirely.

I have looked into this a bit and there doesn't seem to be any reason age could not have a recovery utility. It would involve some minor brute forcing to find the next block and block number. It could even skip any 64k block that failed the integrity check so that only authenticated data was recovered. That would be consistent with the general principle about not releasing unauthenticated data that age was created to enbody.


The reason I suggested a separate utility, and a composition thereof, is that complexity is the enemy of security.

Error-correction and media block-based encryption is a separate utility than what age provides, and should therefore be a separate tool.

That separate tool can use age for the cryptography. That's fine.

But I will not advocate for more complexity to be shoved into age. It's fine as it is today.


That age does not yet have a data recovery utility is, I think, an interesting point, but it is a different point entirely.


If I've confused this for some other idiosyncratic argument about PGP's "authenticated" encryption, I apologize for scrambling the thread. Probably the top-line response re: PGP is that there might be no cryptographic tool in common use with a less coherently documented threat model than PGP.


Yeah it's definitely not cutting edge on all fronts. Another issue is that the length of the plaintext is being exposed. Simple padding would help mask that in most, if not all, instances.


Good job!

I much look forward to: hardware key support, password store, mount and integration with back up software (and perhaps rclone which is for data movement).

I am pretty happy with GPG, which supports most of these features and more (sometimes via third parties that use it), but also appreciate Age and may play with it, once it’s more useful.

I like that it’s, apparently, easier for applications to interface with it, particularly in the large and growing Go ecosystem.

One complaint: It’s hard to find Age by Google search!


If I google for “age encryption” (no quotes), it comes out on top for me.


Really well-written code! I am not a Go expert, but I find it readable and comprehensible.


Is there anything like this but to encrypt/decrypt strings via CLI? For example, if I want to read in an encrypted string from a config file and then decrypt it?


Fantastic news. Does anyone know if there is a UI for it? I know this was discussed in the Dec 2019 thread but that's a while ago now.


A few people have been experimenting with OS integration (e.g. Windows context menu), but IDK how far along those experiments are.

I've been experimenting with a web-based UI [0] for my Rust implementation of age, because I really wanted drag-and-drop but could not figure out how to persuade any Rust GUI libraries to do it ^_^;

[0] https://rage-encry.pt/


Sorry if this sounds hostile, but you are not the intended audience.


I would love to see a password manager based on age. Idealy using ssh-keys beeing combinable with git


There is passage that does this: https://sr.ht/~gpanders/passage/


gopass already has support for an age backend: https://github.com/gopasspw/gopass/blob/master/docs/backends...


There's quite a few floating around on github


I am not so clever and cryptography is hard. Why should I use age instead of regular gpg?


The DoD has a public encryption wizard (https://spi.dod.mil/ewizard_govt.htm). It requires Java and it just reached EOL though.


> requires Java

if the federal government builds a time machine one day I'm sure the firmware will consist of a little Java 5 servlet running on a Windows XP machine


Big words for someone who doesn't run on over 6 billion devices™!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: