Age Versions Save

A simple, modern and secure encryption tool (and Go library) with small explicit keys, no config options, and UNIX-style composability.

v1.1.1

1 year ago

age v1.1.1 is a patch release to fix go install filippo.io/age/...@latest.

See the release notes for v1.1.0 for changes since v1.0.0.

v1.1.0

1 year ago

The age logo, a wireframe of St. Peters dome in Rome, with the text: age, file encryption

age is a simple, modern and secure file encryption tool, format, and Go library. It features small explicit keys, no config options, and UNIX-style composability. Learn more by reading the README, the age(1) man page, the Go API reference, the format specification, or the full release changelog. Watch the repository or follow @[email protected] to be notified of new releases.

🛠️ FYI, age now has an extensive test suite which all age implementations are encouraged to adopt.

Plugin support

The age CLI now supports plugins, such as age-plugin-yubikey by @str4d. To try it on macOS with Homebrew:

$ brew upgrade age
$ brew install age-plugin-yubikey
$ age-plugin-yubikey # interactive setup
$ age -r age1yubikey1qwt50d05nh5vutpdzmlg5wn80xq5negm4uj9ghv0snvdd3yysf5yw3rhl3t
$ age -d -i age-yubikey-identity-388178f3.txt

Plugins must be loaded explicitly by using their respective recipient or identity, and are not tied to a specific header stanza type. This means plugins can be used not only to support new recipient types such as PIV tokens (i.e. YubiKeys) or cloud KMS solutions, but also to produce passphrase-encrypted files that can be decrypted without plugins, to store age native private keys on secure elements, or even for agent functionality or to proxy decryption operations to remote machines.

Plugins operate over a simple textual stdin/stdout protocol (https://github.com/C2SP/C2SP/pull/5). Developers are encouraged to reach out with plugin ideas and announcements. Read more in the relevant man page section.

Breaking changes

If -i is used, passphrase-encrypted files are now rejected. Previously, a passphrase-encrypted file was auto-detected and the identity file was ignored. This could lead to unexpected behavior, such as a script blocking for user interaction, based on potentially untrusted input files. Now, age -d must be invoked without -i arguments to decrypt passphrase-encrypted files. A helpful error is printed otherwise. This should not break any automated system as passphrase decryption was always interactive.

Empty final chunks are now rejected. If a payload was a multiple of 64KiB long, there were two valid encryptions for it: with a "full" last chunk encrypting 64KiB, or with an additional "empty" chunk encrypting 0 bytes. age, rage, and all other known implementations only ever produced the former. (Note that age will forever decrypt files it generated.) The latter is now rejected. The specification has been updated (https://github.com/C2SP/C2SP/pull/13) and test cases are included in the test suite.

Minor changes

PKCS#8-encoded Ed25519 private keys (such as 1Password exports) are now supported as SSH identities.

If an armored file is pasted into the terminal, age will now attempt to wait until the end of the file before prompting for a password.

Some invalid files are now correctly rejected, in particular encrypted files with trailing data. (Yay for the test suite!)

If /dev/tty is present but can't be opened, age will now fallback to trying to treat stdin as a terminal as if /dev/tty wasn't present. (Thanks @brandsimon!)

Input prompts now go to the terminal, even if standard error is redirected.

Values of the new armor.Error type are now returned wrapped in decryption errors when appropriate.

Windows binary releases are now signed. (Thanks @technion!)

Documentation and error messages were improved.

v1.1.0-rc.1

1 year ago

The age logo, an wireframe of St. Peters dome in Rome, with the text: age, file encryption

age is a simple, modern and secure file encryption tool, format, and Go library. It features small explicit keys, no config options, and UNIX-style composability. Learn more by reading the README, the age(1) man page, the Go API reference, the format specification, or the full release changelog. Watch the repository or follow @FiloSottile on Twitter to be notified of new releases.

v1.1.0-rc.1 is the first release candidate of v1.1.0. Users are encourage to test the new release and especially the new features listed below. Issue or UX reports in advance of the final release are greatly appreciated.

📃 In case you missed it: a new, more polished version of the age format specification has been published.

Plugin support

The age CLI now supports plugins, such as age-plugin-yubikey by @str4d. To test it on macOS with Homebrew:

$ brew install --HEAD age
$ brew install age-plugin-yubikey
$ age-plugin-yubikey # interactive setup
$ age -r age1yubikey1qwt50d05nh5vutpdzmlg5wn80xq5negm4uj9ghv0snvdd3yysf5yw3rhl3t
$ age -d -i age-yubikey-identity-388178f3.txt

Plugins must be loaded explicitly by using their respective recipient or identity, and are not tied to a specific header stanza type. This means plugins can be used not only to support new recipient types such as PIV tokens (i.e. YubiKeys) or cloud KMS solutions, but also to produce passphrase-encrypted files that can be decrypted without plugins, to store age native private keys on secure elements, or even for agent functionality or to proxy decryption operations to remote machines.

Plugins operate over a simple textual stdin/stdout protocol (https://github.com/C2SP/C2SP/pull/5). Developers are encouraged to reach out with plugin ideas and announcements. Read more in the relevant man page section.

CLI breaking changes

If -i is used, passphrase-encrypted files are now rejected. Previously, a passphrase-encrypted file was auto-detected and the identity file was ignored. This could lead to unexpected behavior, such as a script blocking for user interaction, based on potentially untrusted input files. Now, age -d must be invoked without -i arguments to decrypt passphrase-encrypted files. A helpful error is printed otherwise. This should not break any automated system as passphrase decryption was always interactive.

Empty final chunks are now rejected. If a payload was a multiple of 64KiB long, there were two valid encryptions for it: with a "full" last chunk encrypting 64KiB, or with an additional "empty" chunk encrypting 0 bytes. age, rage, and all other known implementations only ever produced the former. (Note that age will forever decrypt files it generated.) The latter is now rejected. The specification is being updated (https://github.com/C2SP/C2SP/pull/13) and test cases will be provided.

Minor changes

If /dev/tty is present but can't be opened, age will now fallback to trying to treat stdin as a terminal as if /dev/tty wasn't present.

Windows binary releases are now signed.

Documentation and error messages were improved.

v1.0.0

2 years ago

The age logo, an wireframe of St. Peters dome in Rome, with the text: age, file encryption

age—pronounced [aɡe̞], like the Italian “aghe”—is a simple, modern and secure file encryption tool, format, and Go library.

It features small explicit keys, no config options, and UNIX-style composability.

$ age-keygen -o key.txt
Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
$ tar cvz ~/data | age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p > data.tar.gz.age
$ age --decrypt -i key.txt data.tar.gz.age > data.tar.gz

v1.0.0 is the first stable release of the Go CLI and API, twenty months after the first beta.

Learn more by reading the README, the age(1) man page, the Go API reference, or the original design document.

Watch the repository or follow @FiloSottile on Twitter to be notified of new releases.

v1.0.0-rc.3

2 years ago

Maybe™️ actually™️ the last v1.0.0 release candidate!

Encrypted identity files are now supported. Regular passphrase-encrypted age files can be used with -i, the passphrase will be requested interactively, and the decrypted file will be read line-by-line as a standard identity file.

Passphrases can now be requested interactively from the terminal on Windows even if standard input is in use.

Errors are now tidier and all start with age: error: .

v1.0.0-rc.2

2 years ago

This is it! The v1.0.0 milestone is empty. Will let it simmer for a bit and then we'll have v1.0.0. Please test it!

Main changes

🤏 Reject RSA keys (for ssh-rsa) smaller than 2048 bits (#266)

🥞 Remove limit of 20 recipients per file (#139)

📜 Add age(1) and age-keygen(1) man pages (#131) — read them here!

⚔️ Fix armored encoding for files of certain lengths (#263)

Minor changes

  • Add freebsd/amd64 and darwin/arm64 builds
  • Use filippo.io/edwards25519 for Ed25519 to Curve25519 conversion (again)
  • Check Close() error on output files (#81)
  • Allow reading both passphrase and input from a terminal (#196)
  • Don't warn about world-readable output for public keys in age-keygen (#267)

v1.0.0-rc.1

3 years ago

This release includes the last minor changes to the CLI, improved error messages, and an expanded test suite.

  • The output file is now overwritten if it already exists, consistently with most UNIX tools.
  • There is now an optional -e/--encrypt flag to match -d/--decrypt. When it's explicitly specified, it's now possible to use -i/--identity to encrypt symmetrically to an identity file.
  • The new age-keygen -y mode converts an identity file into the corresponding recipients.

v1.0.0-beta7

3 years ago

The two interfaces Recipient and Identity went from

type Identity interface {
       Type() string
       Unwrap(block *Stanza) (fileKey []byte, err error)
}

type Recipient interface {
       Type() string
       Wrap(fileKey []byte) (*Stanza, error)
}

to

type Identity interface {
       Unwrap(stanzas []*Stanza) (fileKey []byte, err error)
}

type Recipient interface {
       Wrap(fileKey []byte) ([]*Stanza, error)
}

This is a better abstraction for interacting with complex implementations like plugins, as explained in the commit messages.

Most age applications (including all the public ones) use implementations provided by the age module itself, so they won't be affected by this breaking change. If third-party code implemented custom recipients or identities, they will need to update.

The IdentityMatcher interface was also removed, a positive side-effect of this change.

The header format was also changed to expect a short line at the end of every stanza, which allows the format to be used in a streaming protocol. No encrypted files produced by cmd/age are affected. A small number of encrypted files produced by rage are affected, and won't decrypt with newer versions of age. They still decrypt with any version of rage.

Finally, an NoIdentityMatchError type was added, to detect the specific Decrypt error condition.

This is hopefully the first and last breaking change before v1.0.0 is released.

v1.0.0-beta6

3 years ago

Recipient files. You can now use the -R flag to pass the path to a file which contains one or more recipients, one per line. Empty lines and lines starting with # are ignored. This is compatible with SSH .pub files and most authorized_keys files.

--armor performance. Armored encoding is now up to 20x faster.

Various goodies. You can use -R - and -i - to read those files from standard input. Both age and age-keygen support a -version flag. We avoid leaving behind an empty output file when an error occurs. Output is not buffered if the input is not a TTY. Error messages and docs were improved. Release binaries are again available.

v1.0.0-beta5

3 years ago

This release adds a ParseIdentities function to parse private keys files, and docs about key management in third-party applications.

There is also a security mitigations for applications that expose an online decryption oracle. See 2194f6962c8bb3bca8a55f313d5b9302596b593b for details.