Add explicit Authenticode workflows and correct CI helper contracts
This commit is contained in:
1 parent
1026c48324
commit
7aa23f50be
18 files changed
+1952
-185
No files matched your search
@@ -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.
|
||||
Reference in new issue
Block a user