Files
Domito/docs/authenticode-digests.md
T
nefarius 3fbfe31d15 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
2026-10-27 00:17:25 +01:00

8.6 KiB

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 describes section ordering and exclusions, but unusual overlay behavior requires measurements against the relevant Windows implementation. The local tests use the actual 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.

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 and the flag probe to plan the VM run.