NAME

Crypt::Age - Perl implementation of age encryption (age-encryption.org)

VERSION

version 0.001

SYNOPSIS

use Crypt::Age;

# Generate keypair
my ($public, $secret) = Crypt::Age->generate_keypair();
# $public  = "age1ql3z7hjy..."
# $secret  = "AGE-SECRET-KEY-1..."

# Encrypt data
my $encrypted = Crypt::Age->encrypt(
    plaintext  => "Hello, World!",
    recipients => [$public],
);

# Decrypt data
my $decrypted = Crypt::Age->decrypt(
    ciphertext => $encrypted,
    identities => [$secret],
);

# Encrypt file
Crypt::Age->encrypt_file(
    input      => 'secret.txt',
    output     => 'secret.txt.age',
    recipients => [$public],
);

# Decrypt file
Crypt::Age->decrypt_file(
    input      => 'secret.txt.age',
    output     => 'secret.txt',
    identities => [$secret],
);

DESCRIPTION

Crypt::Age is a pure Perl implementation of the age encryption format, compatible with the reference Go implementation (https://github.com/FiloSottile/age) and the Rust implementation (https://github.com/str4d/rage).

age is a simple, modern and secure file encryption tool with small explicit keys, no config options, and UNIX-style composability. The format specification is available at https://github.com/C2SP/C2SP/blob/main/age.md.

This implementation uses X25519 for key exchange, ChaCha20-Poly1305 for authenticated encryption, and HKDF-SHA256 for key derivation. All cryptographic primitives are provided by CryptX.

Files encrypted with Crypt::Age can be decrypted with the age and rage command-line tools, and vice versa.

generate_keypair

my ($public_key, $secret_key) = Crypt::Age->generate_keypair();

Generates a new X25519 keypair for age encryption.

Returns a list of two elements:

  • $public_key - Bech32-encoded public key starting with age1

  • $secret_key - Bech32-encoded secret key starting with AGE-SECRET-KEY-1

The public key can be shared with others to encrypt files for you. The secret key must be kept private and is used to decrypt files encrypted to your public key.

encrypt

my $ciphertext = Crypt::Age->encrypt(
    plaintext  => $data,
    recipients => \@public_keys,
);

Encrypts plaintext data for one or more recipients.

Parameters:

  • plaintext - The data to encrypt (required)

  • recipients - ArrayRef of Bech32-encoded public keys (required)

Returns the encrypted data in age format, which includes a text header followed by the encrypted payload. The file key is wrapped separately for each recipient, allowing any of them to decrypt the data.

The returned data can be written to a file or transmitted directly.

decrypt

my $plaintext = Crypt::Age->decrypt(
    ciphertext => $encrypted,
    identities => \@secret_keys,
);

Decrypts age-encrypted data using one or more identities.

Parameters:

  • ciphertext - The age-encrypted data (required)

  • identities - ArrayRef of Bech32-encoded secret keys (required)

Returns the decrypted plaintext.

The method tries each identity against each recipient stanza in the header until one successfully unwraps the file key. Dies if no matching identity is found or if the MAC verification fails.

encrypt_file

Crypt::Age->encrypt_file(
    input      => 'plaintext.txt',
    output     => 'encrypted.age',
    recipients => \@public_keys,
);

Encrypts a file for one or more recipients.

Parameters:

  • input - Path to input file (required)

  • output - Path to output file (required)

  • recipients - ArrayRef of Bech32-encoded public keys (required)

The output file will be in age format and can be decrypted with the age or rage command-line tools.

Returns 1 on success. Dies on error (file not found, permission denied, etc).

decrypt_file

Crypt::Age->decrypt_file(
    input      => 'encrypted.age',
    output     => 'plaintext.txt',
    identities => \@secret_keys,
);

Decrypts an age-encrypted file using one or more identities.

Parameters:

  • input - Path to encrypted input file (required)

  • output - Path to decrypted output file (required)

  • identities - ArrayRef of Bech32-encoded secret keys (required)

Returns 1 on success. Dies if no matching identity is found, if the MAC verification fails, or on file I/O errors.

KEY FORMAT

Public Keys

Public keys are Bech32-encoded X25519 public keys with the human-readable part age:

age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

Secret Keys

Secret keys are uppercase Bech32-encoded X25519 secret keys with the human-readable part AGE-SECRET-KEY-:

AGE-SECRET-KEY-1QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ3290DG

INTEROPERABILITY

This module is designed to be compatible with:

Files encrypted with Crypt::Age can be decrypted with these tools and vice versa.

SECURITY

age uses modern cryptographic primitives:

  • X25519 for key agreement (Curve25519 Diffie-Hellman)

  • ChaCha20-Poly1305 for authenticated encryption

  • HKDF-SHA256 for key derivation

The file key is randomly generated for each encryption operation. The payload is encrypted in 64 KiB chunks with unique nonces derived from a counter and final-chunk flag.

SEE ALSO

SUPPORT

Issues

Please report bugs and feature requests on GitHub at https://github.com/Getty/p5-crypt-age/issues.

IRC

You can reach Getty on irc.perl.org for questions and support.

CONTRIBUTING

Contributions are welcome! Please fork the repository and submit a pull request.

AUTHOR

Torsten Raudssus <torsten@raudssus.de>

COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Torsten Raudssus.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.