ec_privkey_import_der() in contrib/lax_der_privatekey_parsing.c rejects
malformed encodings through eight structural checks. None of them were
reachable from the test suite: every return 0 in the function was
uncovered, and only the successful round-trip path was exercised.
Measured with gcc --coverage + gcov -b on ./tests 2:
| lines | branches (taken at least once) | |
|---|---|---|
| master | 82.69% | 57.14% |
| this PR | 100% | 100% |
What the test does:
- Mutates every field of an encoding produced by
ec_privkey_export_der(), for both the compressed (1-byte sequence length) and the uncompressed (2-byte) layout, and asserts the layout it mutates before mutating it. - Adds malformed encodings that are not truncations of a valid key. A
truncated valid encoding is always caught by the sequence length check, so
hand-crafted short encodings are the only way to reach the length checks
behind it. This includes an indefinite-length sequence (
30 80 ...) that is otherwise well-formed, and a sequence length that exceeds the input. - Checks that
out32is cleared whenever no key is extracted. - Checks the lax behaviour the parser exists for: a key shorter than 32 bytes is accepted and left-padded with zeroes.
- Passes the input in a heap buffer of exactly the advertised length, so a read past the end is caught by ASan instead of silently succeeding on adjacent stack bytes. Four of the bounds checks are only detectable this way.
I sanity-checked that the test is load-bearing by removing each check in the
parser one at a time: 14 of 16 mutations are detected. The two that are not
are the empty-input check (ASan does not instrument a zero-size allocation)
and the if (privkey[1]) guard added in #879 (a zero-length memcpy from an
out-of-bounds pointer is UB but invisible to every sanitizer).
While writing this I also confirmed that the existing bounds checks form
pointers such as privkey + len outside the caller's object before comparing
them, which is UB in a strict reading of the standard and is not caught by
ASan/UBSan (including -fsanitize=pointer-overflow). That is a separate
concern and is addressed by @l0rinc; the {0x30, 0x82, 0xff, 0xff} vector
here is a regression test for it. This PR does not change the parser.
I am aware of #781. If the lax DER helpers are eventually removed these tests
go with them; until then contrib/ is inside the coverage target described in
CONTRIBUTING.md, and this file had the largest gap in it.