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
143 lines
8.6 KiB
Markdown
143 lines
8.6 KiB
Markdown
# 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.
|