Skip to content

Decode has no bound on KDF iterations, so a 950-byte file dictates arbitrary CPU #80

Description

@cplieger

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions