Setup
go-pkcs12 v0.7.3 (c0472ed), Go 1.26.5, linux/amd64. I call DecodeChain on PKCS#12 files my service didn't necessarily write.
Problem
Decode and DecodeChain honor the KDF iteration counts stored in the file, and nothing bounds them. Both are plain int fields unmarshalled from the DER (pbeParams, PBKDF2 params), so the honored value runs to the largest an int holds.
Encoding with Modern2023.WithIterations(n) and timing DecodeChain:
| iterations |
file size |
DecodeChain |
| 2,048 (default) |
946 B |
1 ms |
| 100,000 |
949 B |
32 ms |
| 1,000,000 |
949 B |
324 ms |
| 10,000,000 |
952 B |
3.27 s |
| 50,000,000 |
952 B |
16.5 s |
Linear, roughly 330 ns per iteration. Six bytes separate the 1 ms file from the 16.5 s one, so size is no proxy for cost and a caller has nothing to gate on.
verifyMac runs before any decryption (#L573), so this needs the password. It doesn't need a stolen one: a certificate-import endpoint taking an uploaded .p12 plus its password is handed both halves.
The check isn't expressible outside the package. A caller can read the MAC's count and each encryptedData safe's outer algorithm from the plaintext, but not a pkcs8ShroudedKeyBag nested inside an encrypted safe. getSafeContents decrypts every encrypted safe (#L614) and flattens the bags together (#L624); DecodeChain then derives from the first shrouded bag it finds (#L487 into safebags.go#L34). pbDecrypt, getSafeContents and decodePkcs8ShroudedKeyBag are all unexported.
Feature request
A default ceiling on every count a decode honors, in the shape of c0472ed ("Reject files with invalid IV lengths or excessively-long key lengths") rather than a caller opt-in. Your own encode default is 2048, so a ceiling in the low millions sits orders of magnitude above any real file and still refuses the 16-second case.
An opt-in maximum mirroring Encoder.WithIterations would solve it for me too, if you'd rather not change a default. It protects fewer people, since it only reaches callers who know to set it.
Happy to send a PR for whichever shape you prefer.
Thanks for the library.
Setup
go-pkcs12 v0.7.3 (
c0472ed), Go 1.26.5, linux/amd64. I callDecodeChainon PKCS#12 files my service didn't necessarily write.Problem
DecodeandDecodeChainhonor the KDF iteration counts stored in the file, and nothing bounds them. Both are plainintfields unmarshalled from the DER (pbeParams, PBKDF2 params), so the honored value runs to the largest anintholds.Encoding with
Modern2023.WithIterations(n)and timingDecodeChain:DecodeChainLinear, roughly 330 ns per iteration. Six bytes separate the 1 ms file from the 16.5 s one, so size is no proxy for cost and a caller has nothing to gate on.
verifyMacruns before any decryption (#L573), so this needs the password. It doesn't need a stolen one: a certificate-import endpoint taking an uploaded.p12plus its password is handed both halves.The check isn't expressible outside the package. A caller can read the MAC's count and each
encryptedDatasafe's outer algorithm from the plaintext, but not apkcs8ShroudedKeyBagnested inside an encrypted safe.getSafeContentsdecrypts every encrypted safe (#L614) and flattens the bags together (#L624);DecodeChainthen derives from the first shrouded bag it finds (#L487 into safebags.go#L34).pbDecrypt,getSafeContentsanddecodePkcs8ShroudedKeyBagare all unexported.Feature request
A default ceiling on every count a decode honors, in the shape of
c0472ed("Reject files with invalid IV lengths or excessively-long key lengths") rather than a caller opt-in. Your own encode default is 2048, so a ceiling in the low millions sits orders of magnitude above any real file and still refuses the 16-second case.An opt-in maximum mirroring
Encoder.WithIterationswould solve it for me too, if you'd rather not change a default. It protects fewer people, since it only reaches callers who know to set it.Happy to send a PR for whichever shape you prefer.
Thanks for the library.