Skip to content

Add age hash format and age2john utility - #6014

Open
AlbertVeli wants to merge 1 commit into
openwall:bleeding-jumbofrom
AlbertVeli:age-format
Open

AlbertVeli wants to merge 1 commit into
openwall:bleeding-jumbofrom
AlbertVeli:age-format

Conversation

@AlbertVeli

Copy link
Copy Markdown
Contributor

Support cracking age (https://age-encryption.org/) passphrase-encrypted files (scrypt + ChaCha20-Poly1305) and extracting hashes with age2john.py

Skips non-scrypt recipients (X25519, SSH, plugins), age2john.py is compatible with python2.7 and python3.

@solardiz solardiz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. I made some comments, including changes that really need to be made and a few that are optional. When making changes, please amend the commit and force-push - don't add more commits. We'll also need a doc/NEWS entry for this, but we have those in other pending PRs, so maybe unwise to add it yet or we'd have merge conflicts.

Comment thread run/age2john.py Outdated
Comment thread src/age_fmt_plug.c
Comment thread src/age_fmt_plug.c Outdated
Comment thread src/age_fmt_plug.c
Comment thread src/age_fmt_plug.c Outdated
Comment thread src/age_fmt_plug.c Outdated
Comment thread src/age_fmt_plug.c Outdated
@AlbertVeli

Copy link
Copy Markdown
Contributor Author

Thanks for the review. I've amended the commit and force pushed. Now I'm a bit scared. I hope it's correct. I'll add samples to the john-samples repo if this pull request looks ok to the reviewer(s).

@AlbertVeli

Copy link
Copy Markdown
Contributor Author

In the latest force push I changed the password for the second fmt_test vector to password123 to match the samples in openwall/john-samples#48

Both passwords are in the default john wordlist password.lst.

@AlbertVeli

Copy link
Copy Markdown
Contributor Author

I changed the second test vector to be exactly the same as in the john-samples repository. All comments are now addressed as far as I can see.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Validation gaps can crash extraction and permit malformed hashes to trigger unsafe work-factor handling.

Review effort: Balanced
Findings: 1 High severity · 3 Medium severity · 1 Low severity

Open (5)
What changed in this PR

Adds support for extracting and cracking passphrase-encrypted age files.

Changes:

  • Implements the age scrypt/ChaCha20-Poly1305 format.
  • Adds a Python 2.7/3-compatible hash extractor.
  • Includes embedded cracking test vectors.
File Description
src/​age_fmt_plug.c Implements age hash validation and cracking.
run/​age2john.py Extracts hashes from binary or armored age files.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/age_fmt_plug.c
Comment thread run/age2john.py
Comment thread run/age2john.py Outdated
Comment thread src/age_fmt_plug.c Outdated
Comment thread src/age_fmt_plug.c Outdated
Add an age format using scrypt and ChaCha20-Poly1305, along with
age2john.py to extract hashes from binary and armored age files.

Validate work factors and hash fields, handle malformed recipient
stanzas without aborting subsequent files, and include test vectors
with different salts and work factors.
@teobas

teobas commented Sep 30, 2026

Copy link
Copy Markdown

I asked codex to fix the comments by copilot. AI vs AI. @Copilot can you review again?

@AlbertVeli

Copy link
Copy Markdown
Contributor Author

@kholia I don't have write access. Can you ask @Copilot to do a new review?

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread run/age2john.py
Comment on lines +63 to +67
if raw.startswith(b"-----BEGIN AGE ENCRYPTED FILE-----"):
try:
raw = base64.b64decode(
b"".join(l for l in raw.splitlines() if not l.startswith(b"-----"))
)
Comment thread run/age2john.py
Comment on lines +90 to +92
if parts[1] != b"scrypt":
_, i = read_body(lines, i)
continue
Comment thread src/age_fmt_plug.c
Comment on lines +300 to +306
if (yescrypt_kdf(NULL, &local[t], (const uint8_t *)saved_key[index], strlen(saved_key[index]),
full_salt, sizeof(full_salt), &params, wrapping_key, sizeof(wrapping_key))) {
failed = errno ? errno : EINVAL;
#ifndef _OPENMP
break;
#endif
}
@kholia

kholia commented Sep 30, 2026

Copy link
Copy Markdown
Member

@AlbertVeli Yep - I have triggered a new fresh code review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants