Revision history for Crypt-Age
0.002 2026-08-19 22:50:05Z
- The distribution now carries the upstream age test kit: 143 vectors from
C2SP/CCTV, run by t/07-testkit.t. 68 of them exercise this implementation
and pass; the remaining 75 are skipped with a stated reason, since they
test ASCII armor and the scrypt and post-quantum recipient types, none of
which are implemented. Unlike t/04-interop.t this needs no age binary on
PATH, so a machine without one is no longer a machine without a
compatibility proof.
- STREAM payload decryption no longer decides a chunk's finality from
end-of-file. A chunk is final because it authenticates under the
final-flag nonce, and any data after the final chunk now invalidates the
payload. Files that previously decrypted as if they had ended cleanly --
a spurious empty final chunk, trailing garbage, a second final chunk --
are rejected, and a full-length chunk written with the non-final nonce
releases its plaintext before the error instead of nothing at all.
- Age headers whose stanza arguments contain anything outside printable
ASCII (0x21-0x7e) are now rejected, as the format's ABNF requires. This
applies to every stanza line regardless of recipient type -- a stray byte
invalidates the whole header rather than merely making that one stanza
ignorable. The error no longer quotes the offending line back.
- cpanfile now pins CryptX to 0.067, the release that introduced
Crypt::PK::X25519. It previously required CryptX with no minimum at all,
so an older one satisfied the declared prerequisites and then failed at
runtime.
- A malformed X25519 stanza is now rejected while the header is parsed, as
the format requires: one that does not carry exactly one argument after the
type, whose argument is not the canonical unpadded base64 encoding of a
32-byte value, or whose body is not exactly 32 bytes. These are header
failures and die from Crypt::Age::Header->parse. Previously an extra
argument was silently ignored, and a wrong-length body came back as "No
matching identity found". Stanzas of other recipient types are unaffected
and are still ignored rather than rejected.
- Stanza serialization now emits the empty final line the format requires
when a stanza body's base64 encoding is an exact multiple of 64
characters. Files this distribution writes are byte-identical to before,
since an X25519 body never reaches that boundary.
- Unpadded base64 in headers is now decoded strictly: padding characters,
characters outside the base64 alphabet, impossible lengths and
non-canonical encodings are rejected rather than repaired, as the format
requires. Malformed age files that previously decoded now die.
- Crypt::Age::Header->verify_mac now compares the header MAC with
Crypt::Misc::slow_eq instead of a plain string eq, so a wrong MAC is no
longer rejected at the first differing byte. It returns 1 or 0 instead of
1 or the empty string; a MAC of the wrong length, or none at all, is still
false, no longer warns, and is never fatal.
- X25519 key exchange now aborts when the shared secret is all zero, as
c2sp.org/age requires. This is the low-order point check: it affects
decryption of a file carrying a low-order ephemeral share and encryption
to a low-order recipient key, both of which would otherwise proceed with a
wrapping key an attacker can predict.
- Header MAC is now verified against the literal header bytes read from the
file instead of a re-serialization of the parsed stanzas. Files whose
stanza formatting differs from ours but is valid per the spec now verify
correctly; output is unchanged, the write path still serializes.
- Crypt::Age::Header->parse no longer warns "Use of uninitialized value in
seek" when the offset scalar it is handed is undef; it is treated as 0
again, as before the filehandle rewrite.
- Removed the unfinished Crypt::Age::Stanza::Scrypt, which was never usable
(it failed to compile) and had no public API. scrypt/passphrase recipients
remain unimplemented.
- Fixed the method names in the encrypt_filehandle/decrypt_filehandle POD
synopses, which showed encrypt_file/decrypt_file.
0.001 2026-01-11 03:51:40Z
- Initial release
- X25519 recipient support
- Compatible with age/rage CLI tools
- Fixed payload key derivation to use nonce as HKDF salt per age spec
- Comprehensive POD documentation for all modules