Security Advisories (1)
CVE-2026-85783 (2026-10-20)

Crypt::Age versions before 0.004 for Perl allow CPU exhaustion via an unbounded number of recipient stanzas, each costing an X25519 scalar multiplication in unwrap_file_key. The header parser accepts any number of recipient stanzas, and unwrap_file_key tries every identity against every X25519 stanza. Each attempt runs a scalar multiplication, an HKDF and a ChaCha20-Poly1305 open. The header MAC cannot be checked first, because its key comes from the file key, which only a successful unwrap produces. Each stanza is 98 bytes of attacker-chosen header and buys one scalar multiplication per identity held, and the file is rejected only after every one of them has run. A caller reaches this through decrypt, decrypt_file or decrypt_filehandle whenever it decrypts a file it did not write.

NAME

Crypt::Age::Stanza - Base class for age recipient stanzas

VERSION

version 0.002

SYNOPSIS

use Crypt::Age::Stanza;

# Create a stanza
my $stanza = Crypt::Age::Stanza->new(
    type => 'X25519',
    args => ['base64-encoded-ephemeral-key'],
    body => $wrapped_file_key_bytes,
);

# Serialize to string
my $text = $stanza->to_string;
# -> X25519 base64-encoded-ephemeral-key
# base64-wrapped-file-key

DESCRIPTION

This is the base class for age recipient stanzas.

A stanza represents one way to unwrap the file key. Each recipient in an age file gets their own stanza. The stanza contains the information needed to unwrap the file key if you have the corresponding private identity.

Stanzas have three parts:

  • type - The recipient type (e.g., X25519, scrypt)

  • args - Type-specific arguments (e.g., ephemeral public key)

  • body - The wrapped file key (base64-encoded in the file)

The stanza format in an age file is:

-> type arg1 arg2 ...
base64-wrapped-key-line1
base64-wrapped-key-line2
...

Subclasses like Crypt::Age::Stanza::X25519 implement the actual wrapping and unwrapping logic for specific recipient types.

type

The stanza type (e.g., X25519, scrypt).

Required.

args

ArrayRef of type-specific arguments.

For X25519 stanzas, this is the base64-encoded ephemeral public key.

body

The wrapped file key as raw bytes.

This is base64-encoded when serialized to the age file format.

to_string

my $text = $stanza->to_string;

Serializes the stanza to age file format.

Returns a multi-line string with the stanza header (-> type args...) and base64-encoded body wrapped at 64 characters per line.

The last body line is always shorter than 64 characters, as the format requires. A body whose base64 encoding is an exact multiple of 64 characters is therefore followed by an empty final line, and the returned string ends in a newline.

FUNCTIONS

encode_base64_no_padding

my $encoded = Crypt::Age::Stanza::encode_base64_no_padding($bytes);

Encodes bytes to base64 without padding (no trailing = characters).

This is the encoding used for all base64 in the age format.

decode_base64_no_padding

my $bytes = Crypt::Age::Stanza::decode_base64_no_padding($encoded);

Decodes unpadded base64, strictly.

The age format specifies RFC 4648 section 4 base64 without padding, and requires that decoders reject anything else. This function therefore dies rather than repairing its input when:

  • the input contains = padding characters

  • the input contains a character outside the standard base64 alphabet

  • the input length is congruent to 1 modulo 4, which no encoding produces

  • the encoding is non-canonical, i.e. the unused trailing bits of the final group are not zero, so that some other encoding of the same bytes exists

The error messages name the reason and never the input, which may be key material.

SEE ALSO

SUPPORT

Issues

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

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.