Add reusable certificate and file validation helpers

Fix process-name ownership and module lookup outputs, add explicit SPKI hashing and bounded chain access, and improve WDM integration. Deprecate superseded entry points with migration guidance and align branch code and tests with the library style.

Add CI validation helpers with explicit signature selection and owned results

Document runtime-confirmed CI signer selection and crypto reuse

Expand CI compatibility matrix to twenty verified samples

Extend CI export matrix with Windows 11 x64 and ARM64 samples

Document CI API availability and historical mincrypt layouts

Add explicit Authenticode workflows and correct CI helper contracts

Document CI validation flag 0x20 and add kernel probe

Harden CI digest parsing and failure cleanup
This commit is contained in:
nefarius committed 2026-10-27 00:17:25 +01:00
1 parent 7985b23be5
commit 3fbfe31d15
38 files changed
+9201 -751

No files matched your search

+142
View File
@@ -0,0 +1,142 @@
# Authenticode digests and legacy verification
Prefer `DomitoCalculatePortableExecutableDigestEx` when computing a PE digest.
It takes an explicit SHA-1/256/384/512 algorithm, validates its supported PE layout,
and returns a digest independently of whether the file has an embedded signature.
This allows SHA-256 catalog lookup for a file without an embedded certificate.
Hash calculation is separate from signature verification and signer acceptance.
The older `DomitoCalculatePortableExecutableDigest` and
`DomitoGetPortableExecutableDigestKind` retain their compatibility behavior:
linear byte hashing and heuristic pattern discovery with a SHA-1 fallback. They
remain bounded, but should not be used to discover a general signature policy.
The new API is additive so existing callers do not silently acquire different
selection or rejection rules.
## Supported PE layout
The explicit API validates DOS/NT/optional-header spans, PE32 and PE32+ magic,
declared data-directory count, the complete section table, `SizeOfHeaders`, every
nonempty section's raw interval, and the certificate table. It sorts section
descriptors by raw file offset before validating their sequence. At most 96
sections are accepted, keeping stack storage and sorting work bounded.
Nonempty sections must form a contiguous sequence starting at `SizeOfHeaders`.
Section-header order can differ from raw-file order. Zero-raw-size sections
contribute no bytes. An overlay between the raw sections and the certificate
table, or EOF for an unsigned file, is included. The checksum and physical
certificate-directory slot are excluded. A present certificate table must be
8-byte aligned and end at EOF; every entry must have a bounded length, version
`0x200`, type PKCS#7 SignedData, and complete 8-byte alignment storage.
For this subset, sorted section bytes coincide with linear file order, so the
implementation can stream validated ranges directly to BCrypt without copying
or allocating a whole-file scratch buffer. Digest allocation is the only pool
allocation in the hashing path and is released on every failure.
Gaps, overlapping raw sections, certificates in the middle of a file, and absent
certificate-directory slots are unsupported. Some malformed inputs return a
format error; ambiguous or excluded layouts return `STATUS_NOT_SUPPORTED`.
The missing-slot boundary is deliberate: Windows SIP produced a different hash
than the old linear helper on a fixture with zero declared directories. Do not
infer Windows parity from the older helper's successful return on that input.
This is a validated subset, not a claim that every PE accepted by every Windows
SIP is supported. Microsoft's [PE format documentation](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format)
describes section ordering and exclusions, but unusual overlay behavior requires
measurements against the relevant Windows implementation. The local tests use
the actual [CryptSIPCreateIndirectData](https://learn.microsoft.com/en-us/windows/win32/api/mssip/nf-mssip-cryptsipcreateindirectdata)
provider as a differential oracle, rather than treating a prose summary as the
complete algorithm. SIP hash agreement does not establish CI trust-policy parity.
## Discovering the first embedded content's digest algorithm
`DomitoGetPortableExecutableDigestKindEx` navigates a bounded DER subset:
PKCS#7 ContentInfo / SignedData / encapsulated `SpcIndirectDataContent` /
`DigestInfo.AlgorithmIdentifier`. It reads the algorithm associated with the
file digest, avoiding unrelated algorithm patterns in certificates, attributes,
timestamps, or signer information. SHA-1/256/384/512 are recognized. Algorithm
parameters may be absent or ASN.1 NULL; the digest OCTET STRING must have the
corresponding length. DER lengths must be bounded, definite, and minimally
encoded. Up to seven zero bytes of aligned padding after the outer DER are
accepted when included in `WIN_CERTIFICATE.dwLength`, as observed on Microsoft
system binaries. Other trailing bytes are rejected.
An absent certificate table returns `STATUS_NOT_FOUND`, an unsupported algorithm
or certificate type returns `STATUS_NOT_SUPPORTED`, and malformed supported
structure returns a format error. The output algorithm is zero on failure; no
default algorithm is silently manufactured.
The caller supplies an array containing the security-directory entry and a
stable input buffer. The helper inspects the first table entry. It does not
enumerate nested/multiple signatures, validate the entire ASN.1 document,
validate signerInfos, verify cryptographic signatures, or establish trust.
Indefinite-length BER is outside this supported DER subset. Choose explicit
algorithms when handling catalog-only files or application-defined lookup order.
## Explicit legacy catalog policy
`DomitoValidateFileLegacyModeEx` takes a caller-selected
`AcceptedCatalogPolicyBits` mask, a `RequireTrustedCatalog` marker gate, and a
`ValidationSource` output. Source is `DOMITO_CI_SOURCE_EMBEDDED` or
`DOMITO_CI_SOURCE_CATALOG` on success and zero on failure.
This helper accepts SHA-1 with exactly 20 bytes or SHA-256 with exactly 32 bytes.
Unsupported algorithms return `STATUS_NOT_SUPPORTED`; mismatched lengths return
`STATUS_INVALID_PARAMETER`. The underlying observed embedded API supports more
algorithms than this helper's catalog workflow, as documented in
[the mincrypt contracts](mincrypt-contracts.md).
Both policy objects must be zero-initialized before first use, or own valid CI
results. The helper frees previous contents on entry and before each retry and
clears signing time between attempts. The caller owns both final policy results,
including any returned on failure, and frees them with `CiFreePolicyInfo`.
Embedded-to-catalog fallback and catalog reload occur only for
`STATUS_INVALID_IMAGE_HASH`; resource, I/O, and other policy errors propagate.
Before certificate allocation, the helper references the file object and checks
the supplied table interval against the current file size. It then uses the
exact-read `DomitoReadFileAt`, which waits on a private completion event. An EOF
short read fails before CI sees any uninitialized bytes. The caller must keep
file identity/bytes stable between hashing and consumption; a size check cannot
prevent concurrent mutation. Handles must be valid in the current process
context and satisfy the documented kernel I/O/APC requirements.
The older `DomitoValidateFileLegacyMode` delegates with the existing
`DOMITO_CI_LEGACY_CATALOG_POLICY_BITS` (`0x2007F`) and a false marker gate. That
value is a compatibility default with broad categories, not an application trust
recommendation. The explicit mask uses **any-overlap** catalog filtering.
For a catalog-only file, choose an allowed algorithm sequence as application
policy (for example SHA-256 followed by SHA-1 when legacy acceptance is intended).
For each algorithm, call `DomitoCalculatePortableExecutableDigestEx`, then
`DomitoValidateFileLegacyModeEx` with a zero security directory. Free the digest
and both policy objects before retrying. Continue to another algorithm only on
the specifically intended lookup/signature-mismatch result, not on all failures.
Preserve the actual algorithm and validation source in the application's result.
This supports an explicit search strategy; it does not reproduce every native
CI scenario-selection rule or WinVerifyTrust policy.
## Validation performed
On the local Windows 26100 system, host tests compare all four digest algorithms
against Windows SIP for PE32/PE32+, unsigned/signed fixtures, overlays, reverse
section-header order, multiple certificate entries, and certificate alignment.
The installed `ci.dll`, `ntdll.dll`, `kernel32.dll`, and `notepad.exe` form a
real-file corpus. Embedded digest algorithms are independently extracted with
Crypt32, and computed file digests are compared with the signed
`SpcIndirectDataContent` values where an embedded signature exists. This reads
files and hashes bytes; it does not load or execute them as code.
Guard-page truncations, malformed offsets/lengths, unsupported layouts,
algorithm selection, injected CNG/allocation failures, retry ownership, exact
read failures, and explicit catalog controls are also covered. Actual CI calls
are mocked in those host tests. The kernel translation units and layout
assertions compile for x64/ARM64 with warnings treated as errors; the opt-in
26100 runtime-layout observations remain x64-specific.
Still pending are historical/ARM64 CI runtime compatibility, Driver Verifier,
real catalog-only acceptance across algorithms, nested/multiple-signature
selection, weak-crypto/revocation/timestamp policy, and extension of supported PE
layouts. Use the [remaining-work ledger](implementation-status.md) and the
[flag probe](ci-validate-file-object.md) to plan the VM run.