Skip to content

Releases: FiloSottile/age

age v1.2.1: security fix

18 Dec 15:26
Compare
Choose a tag to compare

This release fixes a security vulnerability that could allow an attacker to execute an arbitrary binary under certain conditions.

See GHSA-32gq-x56h-299c.

Plugin names may now only contain alphanumeric characters or the four special characters +-._.

Thanks to ⬡-49016 for reporting this issue.

age v1.2.0

16 Jun 14:11
Compare
Choose a tag to compare

A small release to build the release binaries with a more recent Go toolchain, and to fix a couple CLI edge cases (#491, #555).

The Go module now exposes a plugin package that provides an age plugin client. That is, Recipient and Identity implementations that invoke a plugin binary, allowing the use of age plugins in Go programs.

Finally, Recipients can now return a set of "labels" by implementing RecipientWithLabels. This allows replicating the special behavior of the scrypt Recipient in third-party Recipients, or applying policy useful for authenticated or post-quantum Recipients.

// RecipientWithLabels can be optionally implemented by a Recipient, in which
// case Encrypt will use WrapWithLabels instead of Wrap.
//
// Encrypt will succeed only if the labels returned by all the recipients
// (assuming the empty set for those that don't implement RecipientWithLabels)
// are the same.
//
// This can be used to ensure a recipient is only used with other recipients
// with equivalent properties (for example by setting a "postquantum" label) or
// to ensure a recipient is always used alone (by returning a random label, for
// example to preserve its authentication properties).
type RecipientWithLabels interface {
	WrapWithLabels(fileKey []byte) (s []*Stanza, labels []string, err error)
}

age v1.1.1

26 Dec 20:39
Compare
Choose a tag to compare

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.

age v1.1.0: plugin and YubiKeys support

26 Dec 14:57
Compare
Choose a tag to compare

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 (C2SP/C2SP#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 (C2SP/C2SP#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.

age v1.1.0-rc.1: plugin and YubiKeys support

11 Jun 16:00
Compare
Choose a tag to compare

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 (C2SP/C2SP#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 (C2SP/C2SP#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.

age v1.0.0 🏁

06 Sep 16:48
552aa0a
Compare
Choose a tag to compare

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.

Never two without three

15 Jun 12:19
Compare
Choose a tag to compare
Pre-release

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

The last (?) v1.0.0 release candidate!

26 May 11:49
Compare
Choose a tag to compare
Pre-release

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

10 Mar 12:19
Compare
Choose a tag to compare
Pre-release

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.

Breaking changes to the Identity and Recipient interfaces

08 Feb 19:09
Compare
Choose a tag to compare

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.