Summary
Age.TestKit/CctvTestRunner fails 61 of 143 wire-format reference vectors on Windows. The same vectors pass cleanly on Linux (143/143). The failures are not crypto bugs — they're caused by Git's default core.autocrlf=true on Windows checkouts normalizing the LF-only testdata .txt files to CRLF, which corrupts byte-exact reference vectors.
Repro
On a fresh Windows checkout of pscheid92/AgeSharp@main (no patches):
git clone https://github.com/pscheid92/AgeSharp
cd AgeSharp
dotnet test Age.TestKit/Age.TestKit.csproj -c Release --nologo
Result:
Failed! - Failed: 61, Passed: 82, Skipped: 0, Total: 143
On a Linux runner (e.g. GitHub Actions ubuntu-latest):
Passed! - Failed: 0, Passed: 143, Skipped: 0, Total: 143
Root cause
testdata/*.txt files are byte-exact age v1 reference vectors. The CCTV runner reads them and compares against expected hashes / decryption outputs. On Windows, Git's default core.autocrlf=true converts LF to CRLF on checkout, breaking the byte-exactness:
$ file Age.TestKit/testdata/armor_empty.txt
Age.TestKit/testdata/armor_empty.txt: ASCII text, with CRLF line terminators
Hex dump confirms 0d 0a after every line — should be 0a only:
00000000: 6578 7065 6374 3a20 6865 6164 6572 2066 expect: header f
00000010: 6169 6c75 7265 0d0a 6669 6c65 206b 6579 ailure..file key
Failure distribution
The 61 failures span every test category — not just armored ones — because the metadata parser is also affected:
| Test prefix |
Failing |
| armor_* |
29 |
| stanza_* |
9 |
| x25519_* |
8 |
| stream_* |
7 |
| hybrid_* |
5 |
| scrypt_* |
1 |
| hmac_* |
1 |
| empty |
1 |
| Total |
61 |
Suggested fix
Add a .gitattributes file that marks the testdata as binary so Git leaves it alone on every platform:
Age.TestKit/testdata/** binary
Age.TestKit/testdata/**/*.txt binary
Or scope it tighter to just the .txt files in that directory.
After the .gitattributes lands, existing Windows clones need to re-fetch the files to get the LF version:
git rm --cached -r Age.TestKit/testdata
git checkout HEAD -- Age.TestKit/testdata
Environment
AgeSharp main (unpatched, commit at time of repro: latest)
- .NET SDK 10.0.203 on Windows 11
- Git
core.autocrlf=true (Windows default)
Why I noticed
This came up while validating an unrelated patch (#2 — WASM cipher swap). I initially attributed the 61 failures to pre-existing upstream bugs (because they showed up on both patched and unpatched checkouts on Windows). Re-running the same code on Linux CI revealed 143/143 pass — at which point the platform pattern was obvious.
Happy to send a PR with the .gitattributes fix if you'd like.
Summary
Age.TestKit/CctvTestRunnerfails 61 of 143 wire-format reference vectors on Windows. The same vectors pass cleanly on Linux (143/143). The failures are not crypto bugs — they're caused by Git's defaultcore.autocrlf=trueon Windows checkouts normalizing the LF-only testdata.txtfiles to CRLF, which corrupts byte-exact reference vectors.Repro
On a fresh Windows checkout of
pscheid92/AgeSharp@main(no patches):Result:
On a Linux runner (e.g. GitHub Actions
ubuntu-latest):Root cause
testdata/*.txtfiles are byte-exact age v1 reference vectors. The CCTV runner reads them and compares against expected hashes / decryption outputs. On Windows, Git's defaultcore.autocrlf=trueconverts LF to CRLF on checkout, breaking the byte-exactness:Hex dump confirms
0d 0aafter every line — should be0aonly:Failure distribution
The 61 failures span every test category — not just armored ones — because the metadata parser is also affected:
Suggested fix
Add a
.gitattributesfile that marks the testdata as binary so Git leaves it alone on every platform:Or scope it tighter to just the
.txtfiles in that directory.After the
.gitattributeslands, existing Windows clones need to re-fetch the files to get the LF version:Environment
AgeSharpmain(unpatched, commit at time of repro: latest)core.autocrlf=true(Windows default)Why I noticed
This came up while validating an unrelated patch (#2 — WASM cipher swap). I initially attributed the 61 failures to pre-existing upstream bugs (because they showed up on both patched and unpatched checkouts on Windows). Re-running the same code on Linux CI revealed 143/143 pass — at which point the platform pattern was obvious.
Happy to send a PR with the
.gitattributesfix if you'd like.